add eeprom address for reference voltage on at-mio-64e-3
[comedilib.git] / comedi_calibrate / ni.c
1 /*
2    A little auto-calibration utility, for boards
3    that support it.
4
5    copyright (C) 1999,2000,2001,2002 by David Schleef
6    copyright (C) 2003 by Frank Mori Hess
7
8  */
9
10 /***************************************************************************
11  *                                                                         *
12  *   This program is free software; you can redistribute it and/or modify  *
13  *   it under the terms of the GNU Lesser General Public License as        *
14  *   published by                                                          *
15  *   the Free Software Foundation; either version 2.1 of the License, or   *
16  *   (at your option) any later version.                                   *
17  *                                                                         *
18  ***************************************************************************/
19
20 #define _GNU_SOURCE
21
22 #include <stdio.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <getopt.h>
27 #include <ctype.h>
28 #include <math.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <assert.h>
32
33 #include "calib.h"
34
35
36 char ni_id[] = "$Id$";
37
38 struct board_struct{
39         char *name;
40         int status;
41         int (*cal)( calibration_setup_t *setup);
42         void (*setup_observables)( calibration_setup_t *setup );
43         int ref_eeprom_lsb;
44         int ref_eeprom_msb;
45 };
46
47 static int ni_setup_board( calibration_setup_t *setup , const char *device_name );
48 static void ni_setup_observables( calibration_setup_t *setup );
49 static void ni_setup_observables_611x( calibration_setup_t *setup );
50 static void ni67xx_setup_observables( calibration_setup_t *setup );
51
52 static int cal_ni_at_mio_16e_2(calibration_setup_t *setup);
53 static int cal_ni_at_mio_64e_3(calibration_setup_t *setup);
54 static int cal_ni_daqcard_ai_16xe_50(calibration_setup_t *setup);
55 static int cal_ni_at_mio_16e_1(calibration_setup_t *setup);
56 static int cal_ni_pci_mio_16e_1(calibration_setup_t *setup);
57 static int cal_ni_pci_6014(calibration_setup_t *setup);
58 static int cal_ni_pci_6024e(calibration_setup_t *setup);
59 static int cal_ni_pci_6025e(calibration_setup_t *setup);
60 static int cal_ni_pci_6032e(calibration_setup_t *setup);
61 static int cal_ni_pci_6034e(calibration_setup_t *setup);
62 static int cal_ni_pci_6035e(calibration_setup_t *setup);
63 static int cal_ni_pci_6036e(calibration_setup_t *setup);
64 static int cal_ni_pci_6071e(calibration_setup_t *setup);
65 static int cal_ni_pxi_6071e(calibration_setup_t *setup);
66 static int cal_ni_at_mio_16e_10(calibration_setup_t *setup);
67 static int cal_ni_pci_mio_16xe_50(calibration_setup_t *setup);
68 static int cal_ni_pci_6023e(calibration_setup_t *setup);
69 static int cal_ni_at_mio_16xe_50(calibration_setup_t *setup);
70 static int cal_ni_pci_mio_16xe_10(calibration_setup_t *setup);
71 static int cal_ni_pci_6052e(calibration_setup_t *setup);
72 static int cal_ni_daqcard_ai_16e_4(calibration_setup_t *setup);
73 static int cal_ni_pci_611x(calibration_setup_t *setup);
74 static int cal_ni_pci_mio_16e_4(calibration_setup_t *setup);
75 static int cal_ni_daqcard_6062e(calibration_setup_t *setup);
76 static int cal_ni_daqcard_6024e(calibration_setup_t *setup);
77 static int cal_ni_daqcard_6036e(calibration_setup_t *setup);
78 static int cal_ni_pci_6711(calibration_setup_t *setup);
79
80 static double ni_get_reference( calibration_setup_t *setup, int lsb_loc,int msb_loc);
81
82 static struct board_struct boards[]={
83         { "at-ai-16xe-10", STATUS_UNKNOWN, NULL, ni_setup_observables, 0x1b7, 0x1b8 },
84         { "at-mio-16de-10", STATUS_UNKNOWN, NULL, ni_setup_observables, 0x1a7, 0x1a8 },
85         { "at-mio-16e-1", STATUS_DONE, cal_ni_at_mio_16e_1, ni_setup_observables, 0x1a9, 0x1aa },
86         { "at-mio-16e-2", STATUS_DONE, cal_ni_at_mio_16e_2, ni_setup_observables, 0x1a9, 0x1aa },
87         { "at-mio-16e-10", STATUS_DONE, cal_ni_at_mio_16e_10, ni_setup_observables, 0x1a7, 0x1a8 },
88         { "at-mio-16xe-10", STATUS_UNKNOWN, NULL, ni_setup_observables, 0x1b7, 0x1b8 },
89         { "at-mio-16xe-50", STATUS_SOME, cal_ni_at_mio_16xe_50, ni_setup_observables, 0x1b5, 0x1b6 },
90         { "at-mio-64e-3", STATUS_SOME, cal_ni_at_mio_64e_3, ni_setup_observables, 0x1a9, 0x1aa},
91         { "DAQCard-ai-16e-4", STATUS_DONE, cal_ni_daqcard_ai_16e_4, ni_setup_observables, 0x1b5, 0x1b6 },
92         { "DAQCard-ai-16xe-50", STATUS_DONE, cal_ni_daqcard_ai_16xe_50, ni_setup_observables, 0x1be, 0x1bf },
93         { "DAQCard-6024E", STATUS_SOME, cal_ni_daqcard_6024e, ni_setup_observables, -1, -1 },
94         { "DAQCard-6036E", STATUS_DONE, cal_ni_daqcard_6036e, ni_setup_observables, 0x1ab, 0x1ac },
95         { "DAQCard-6062E", STATUS_DONE, cal_ni_daqcard_6062e, ni_setup_observables, 0x1a9, 0x1aa },
96         { "pci-mio-16e-1", STATUS_DONE, cal_ni_pci_mio_16e_1, ni_setup_observables, 0x1a9, 0x1aa },
97         { "pci-mio-16e-4", STATUS_SOME, cal_ni_pci_mio_16e_4, ni_setup_observables, 0x1a9, 0x1aa },
98         { "pci-mio-16xe-10", STATUS_DONE,       cal_ni_pci_mio_16xe_10, ni_setup_observables, 0x1ae, 0x1af },
99         { "pci-mio-16xe-50", STATUS_SOME, cal_ni_pci_mio_16xe_50, ni_setup_observables, 0x1b5, 0x1b6 },
100         { "pci-6014", STATUS_DONE, cal_ni_pci_6014, ni_setup_observables, 0x1ab, 0x1ac },
101         { "pci-6023e", STATUS_DONE, cal_ni_pci_6023e, ni_setup_observables, 0x1bb, 0x1bc },
102         { "pci-6024e", STATUS_SOME, cal_ni_pci_6024e, ni_setup_observables, 0x1af, 0x1b0 },
103         { "pci-6025e", STATUS_SOME, cal_ni_pci_6025e, ni_setup_observables, 0x1af, 0x1b0 },
104         { "pci-6031e", STATUS_DONE, cal_ni_pci_mio_16xe_10, ni_setup_observables, 0x1ae, 0x1af },
105         { "pci-6032e", STATUS_DONE, cal_ni_pci_6032e, ni_setup_observables, 0x1ae, 0x1af },
106         { "pci-6033e", STATUS_DONE, cal_ni_pci_6032e, ni_setup_observables, 0x1b7, 0x1b8 },
107         { "pci-6034e", STATUS_DONE, cal_ni_pci_6034e, ni_setup_observables, 0x1bb, 0x1bc },
108         { "pci-6035e", STATUS_DONE, cal_ni_pci_6035e, ni_setup_observables, 0x1af, 0x1b0 },
109         { "pci-6036e", STATUS_DONE, cal_ni_pci_6036e, ni_setup_observables, 0x1ab, 0x1ac },
110         { "pci-6052e", STATUS_DONE, cal_ni_pci_6052e, ni_setup_observables, 0x19f, 0x1a0 },
111         { "pci-6071e", STATUS_DONE, cal_ni_pci_6071e, ni_setup_observables, 0x1a9, 0x1aa },
112         { "pci-6110", STATUS_DONE, cal_ni_pci_611x, ni_setup_observables_611x, 0x1d4, 0x1d5 },
113         { "pci-6111", STATUS_DONE, cal_ni_pci_611x, ni_setup_observables_611x, 0x1d4, 0x1d5 },
114         { "pxi-6025e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
115         { "pxi-6030e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
116         { "pxi-6031e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
117         { "pxi-6040e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
118         { "pxi-6052e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
119         { "pxi-6070e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
120         { "pxi-6071e", STATUS_GUESS, cal_ni_pxi_6071e, ni_setup_observables, -1, -1 },
121         { "pci-6711", STATUS_DONE, cal_ni_pci_6711, ni67xx_setup_observables, 0x1d4, 0x1d5},
122         { "pci-6713", STATUS_DONE, cal_ni_pci_6711, ni67xx_setup_observables, 0x1d4, 0x1d5},
123         { "pci-6731", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
124         { "pci-6733", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
125         { "pxi-6711", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
126         { "pxi-6713", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
127         { "pxi-6731", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
128         { "pxi-6733", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
129 #if 0
130         { "at-mio-64e-3",       cal_ni_16e_1 },
131 #endif
132 };
133 #define n_boards (sizeof(boards)/sizeof(boards[0]))
134
135 static const int ni_num_observables = 20;
136 enum observables{
137         ni_zero_offset_low = 0,
138         ni_zero_offset_high,
139         ni_reference_low,
140         ni_unip_zero_offset_low,
141         ni_unip_zero_offset_high,
142         ni_unip_reference_low,
143         ni_ao0_zero_offset,
144         ni_ao0_reference,
145         ni_ao0_linearity,
146         ni_ao1_zero_offset,
147         ni_ao1_reference,
148         ni_ao1_linearity,
149         ni_ao0_unip_zero_offset,
150         ni_ao0_unip_reference,
151         ni_ao0_unip_low_linearity,
152         ni_ao0_unip_mid_linearity,
153         ni_ao1_unip_zero_offset,
154         ni_ao1_unip_reference,
155         ni_ao1_unip_low_linearity,
156         ni_ao1_unip_mid_linearity,
157 };
158 static inline unsigned int ni_ao_zero_offset( unsigned int channel )
159 {
160         if( channel ) return ni_ao1_zero_offset;
161         else return ni_ao0_zero_offset;
162 }
163 static inline unsigned int ni_ao_reference( unsigned int channel )
164 {
165         if( channel ) return ni_ao1_reference;
166         else return ni_ao0_reference;
167 }
168 static inline unsigned int ni_ao_mid_linearity( unsigned int channel )
169 {
170         if( channel ) return ni_ao1_linearity;
171         else return ni_ao0_linearity;
172 }
173 static inline unsigned int ni_ao_unip_zero_offset( unsigned int channel )
174 {
175         if( channel ) return ni_ao1_unip_zero_offset;
176         else return ni_ao0_unip_zero_offset;
177 }
178 static inline unsigned int ni_ao_unip_reference( unsigned int channel )
179 {
180         if( channel ) return ni_ao1_unip_reference;
181         else return ni_ao0_unip_reference;
182 }
183 static inline unsigned int ni_ao_unip_low_linearity( unsigned int channel )
184 {
185         if( channel ) return ni_ao1_unip_low_linearity;
186         else return ni_ao0_unip_low_linearity;
187 }
188 static inline unsigned int ni_ao_unip_mid_linearity( unsigned int channel )
189 {
190         if( channel ) return ni_ao1_unip_mid_linearity;
191         else return ni_ao0_unip_mid_linearity;
192 }
193
194 static const int num_ao_observables_611x = 4;
195 static int ni_ao_zero_offset_611x( const calibration_setup_t *setup,
196         unsigned int channel, unsigned int range ) {
197         assert( range == 0 );
198         return 2 * channel;
199 };
200 static int ni_ao_reference_611x( const calibration_setup_t *setup,
201         unsigned int channel, unsigned int range ) {
202         assert( range == 0 );
203         return 2 * channel + 1;
204 };
205 static int ni_zero_offset_611x( const calibration_setup_t *setup,
206         unsigned int channel, unsigned int range ) {
207         return num_ao_observables_611x + 8 * range + 2 * channel;
208 };
209 static int ni_reference_611x( const calibration_setup_t *setup,
210         unsigned int channel, unsigned int range ) {
211         return num_ao_observables_611x + 8 * range + 2 * channel + 1;
212 };
213
214 enum reference_sources {
215         REF_GND_GND = 0,
216         REF_AOGND_AIGND = 1,
217         REF_DAC0_GND = 2,
218         REF_DAC1_GND = 3,
219         REF_CALSRC_CALSRC = 4,
220         REF_CALSRC_GND = 5,
221         REF_DAC0_CALSRC = 6,
222         REF_DAC1_CALSRC = 7,
223 };
224 static inline unsigned int REF_DAC_GND( unsigned int channel )
225 {
226         if( channel ) return REF_DAC1_GND;
227         else return REF_DAC0_GND;
228 }
229 static inline unsigned int REF_DAC_CALSRC( unsigned int channel )
230 {
231         if( channel ) return REF_DAC1_CALSRC;
232         else return REF_DAC0_CALSRC;
233 }
234
235 static struct board_struct* ni_board( calibration_setup_t *setup )
236 {
237         return setup->private_data;
238 }
239
240 typedef struct
241 {
242         int adc_pregain_offset;
243         int adc_postgain_offset;
244         int adc_gain;
245         int adc_pregain_offset_fine;
246         int adc_postgain_offset_fine;
247         int adc_gain_fine;
248         int adc_unip_offset;
249         int adc_unip_offset_fine;
250         int dac_offset[ 2 ];
251         int dac_offset_fine[ 2 ];
252         int dac_gain[ 2 ];
253         int dac_gain_fine[ 2 ];
254         int dac_linearity[ 2 ];
255 } ni_caldac_layout_t;
256
257 static int cal_ni_generic( calibration_setup_t *setup,
258         const ni_caldac_layout_t *layout );
259
260 static inline void init_ni_caldac_layout( ni_caldac_layout_t *layout )
261 {
262         int i;
263
264         layout->adc_pregain_offset = -1;
265         layout->adc_postgain_offset = -1;
266         layout->adc_gain = -1;
267         layout->adc_unip_offset = -1;
268         layout->adc_unip_offset_fine = -1;
269         layout->adc_pregain_offset_fine = -1;
270         layout->adc_postgain_offset_fine = -1;
271         layout->adc_gain_fine = -1;
272         for( i = 0; i < 2; i++ )
273         {
274                 layout->dac_offset[ i ] = -1;
275                 layout->dac_offset_fine[ i ] = -1;
276                 layout->dac_gain[ i ] = -1;
277                 layout->dac_gain_fine[ i ] = -1;
278                 layout->dac_linearity[ i ] = -1;
279         }
280 }
281
282 int ni_setup( calibration_setup_t *setup , const char *device_name )
283 {
284         int retval;
285
286         retval = ni_setup_board( setup, device_name );
287         if( retval < 0 ) return retval;
288         setup_caldacs( setup, setup->caldac_subdev );
289
290         return 0;
291 }
292
293 static int ni_setup_board( calibration_setup_t *setup, const char *device_name )
294 {
295         int i;
296
297         for(i = 0; i < n_boards; i++ ){
298                 if(!strcmp( device_name, boards[i].name )){
299                         setup->status = boards[i].status;
300                         setup->do_cal = boards[i].cal;
301                         setup->private_data = &boards[ i ];
302                         boards[i].setup_observables( setup );
303                         break;
304                 }
305         }
306         if( i == n_boards ) return -1;
307         return 0;
308 }
309
310 static void ni_setup_ao_observables( calibration_setup_t *setup )
311 {
312         observable *o;
313         comedi_insn tmpl, po_tmpl;
314         unsigned int channel;
315         int ai_bipolar_lowgain;
316         int ao_bipolar_lowgain;
317         int ao_unipolar_lowgain;
318
319         ai_bipolar_lowgain = get_bipolar_lowgain( setup->dev, setup->ad_subdev);
320         assert(ai_bipolar_lowgain >= 0);
321         ao_bipolar_lowgain = get_bipolar_lowgain( setup->dev, setup->da_subdev);
322         assert(ao_bipolar_lowgain >= 0);
323         ao_unipolar_lowgain = get_unipolar_lowgain( setup->dev, setup->da_subdev);
324
325         memset(&tmpl,0,sizeof(tmpl));
326         tmpl.insn = INSN_READ;
327         tmpl.n = 1;
328         tmpl.subdev = setup->ad_subdev;
329
330         memset(&po_tmpl, 0, sizeof(po_tmpl));
331         po_tmpl.insn = INSN_WRITE;
332         po_tmpl.n = 1;
333         po_tmpl.subdev = setup->da_subdev;
334
335         for( channel = 0; channel < 2; channel++ )
336         {
337                 /* ao zero offset */
338                 o = setup->observables + ni_ao_zero_offset( channel );
339                 assert( o->name == NULL );
340                 asprintf( &o->name, "ao %i, zero offset, low gain", channel );
341                 o->preobserve_insn = po_tmpl;
342                 o->preobserve_insn.chanspec = CR_PACK(channel,ao_bipolar_lowgain,0);
343                 o->preobserve_insn.data = o->preobserve_data;
344                 o->observe_insn = tmpl;
345                 o->observe_insn.chanspec =
346                         CR_PACK(REF_DAC_GND( channel ),ai_bipolar_lowgain,AREF_OTHER)
347                         | CR_ALT_SOURCE | CR_ALT_FILTER;
348                 o->reference_source = REF_DAC_GND( channel );
349                 set_target( setup, ni_ao_zero_offset( channel ),0.0);
350
351                 /* ao gain */
352                 o = setup->observables + ni_ao_reference( channel );
353                 assert( o->name == NULL );
354                 asprintf( &o->name, "ao %i, reference voltage, low gain", channel );
355                 o->preobserve_insn = po_tmpl;
356                 o->preobserve_insn.chanspec = CR_PACK(channel,ao_bipolar_lowgain,0);
357                 o->preobserve_insn.data = o->preobserve_data;
358                 o->observe_insn = tmpl;
359                 o->observe_insn.chanspec =
360                         CR_PACK(REF_DAC_GND( channel ),ai_bipolar_lowgain,AREF_OTHER)
361                         | CR_ALT_SOURCE | CR_ALT_FILTER;
362                 o->reference_source = REF_DAC_GND( channel );
363                 set_target( setup, ni_ao_reference( channel ),8.0);
364
365                 /* ao linearity, mid */
366                 o = setup->observables + ni_ao_mid_linearity( channel );
367                 assert( o->name == NULL );
368                 asprintf( &o->name, "ao %i, linearity (mid), low gain", channel );
369                 o->preobserve_insn = po_tmpl;
370                 o->preobserve_insn.chanspec = CR_PACK(channel,ao_bipolar_lowgain,0);
371                 o->preobserve_insn.data = o->preobserve_data;
372                 o->observe_insn = tmpl;
373                 o->observe_insn.chanspec =
374                         CR_PACK(REF_DAC_GND( channel ),ai_bipolar_lowgain,AREF_OTHER)
375                         | CR_ALT_SOURCE | CR_ALT_FILTER;
376                 o->reference_source = REF_DAC_GND( channel );
377                 set_target( setup, ni_ao_mid_linearity( channel ),4.0);
378
379                 if( ao_unipolar_lowgain >= 0 )
380                 {
381                         /* ao unipolar zero offset */
382                         o = setup->observables + ni_ao_unip_zero_offset( channel );
383                         assert( o->name == NULL );
384                         asprintf( &o->name, "ao %i, unipolar zero offset, low gain", channel );
385                         o->preobserve_insn = po_tmpl;
386                         o->preobserve_insn.chanspec = CR_PACK(channel,ao_unipolar_lowgain,0);
387                         o->preobserve_insn.data = o->preobserve_data;
388                         o->observe_insn = tmpl;
389                         o->observe_insn.chanspec =
390                                 CR_PACK(REF_DAC_GND( channel ),ai_bipolar_lowgain,AREF_OTHER)
391                                 | CR_ALT_SOURCE | CR_ALT_FILTER;
392                         o->reference_source = REF_DAC_GND( channel );
393                         set_target( setup, ni_ao_unip_zero_offset( channel ),0.0);
394
395                         /* ao unipolar gain */
396                         o = setup->observables + ni_ao_unip_reference( channel );
397                         assert( o->name == NULL );
398                         asprintf( &o->name, "ao %i, unipolar high, low gain", channel );
399                         o->preobserve_insn = po_tmpl;
400                         o->preobserve_insn.chanspec = CR_PACK(channel,ao_unipolar_lowgain,0);
401                         o->preobserve_insn.data = o->preobserve_data;
402                         o->observe_insn = tmpl;
403                         o->observe_insn.chanspec =
404                                 CR_PACK(REF_DAC_GND( channel ),ai_bipolar_lowgain,AREF_OTHER)
405                                 | CR_ALT_SOURCE | CR_ALT_FILTER;
406                         o->reference_source = REF_DAC_GND( channel );
407                         set_target( setup, ni_ao_unip_reference( channel ), 9.0);
408
409                         /* ao unipolar linearity, mid */
410                         o = setup->observables + ni_ao_unip_mid_linearity( channel );
411                         assert( o->name == NULL );
412                         asprintf( &o->name, "ao %i, unipolar linearity (mid), low gain", channel );
413                         o->preobserve_insn = po_tmpl;
414                         o->preobserve_insn.chanspec = CR_PACK(channel,ao_unipolar_lowgain,0);
415                         o->preobserve_insn.data = o->preobserve_data;
416                         o->observe_insn = tmpl;
417                         o->observe_insn.chanspec =
418                                 CR_PACK(REF_DAC_GND( channel ),ai_bipolar_lowgain,AREF_OTHER)
419                                 | CR_ALT_SOURCE | CR_ALT_FILTER;
420                         o->reference_source = REF_DAC_GND( channel );
421                         set_target( setup, ni_ao_unip_mid_linearity( channel ), 5.0);
422
423                         /* ao unipolar linearity, low */
424                         o = setup->observables + ni_ao_unip_low_linearity( channel );
425                         assert( o->name == NULL );
426                         asprintf( &o->name, "ao %i, unipolar linearity (low), low gain", channel );
427                         o->preobserve_insn = po_tmpl;
428                         o->preobserve_insn.chanspec = CR_PACK(channel,ao_unipolar_lowgain,0);
429                         o->preobserve_insn.data = o->preobserve_data;
430                         o->observe_insn = tmpl;
431                         o->observe_insn.chanspec =
432                                 CR_PACK(REF_DAC_GND( channel ),ai_bipolar_lowgain,AREF_OTHER)
433                                 | CR_ALT_SOURCE | CR_ALT_FILTER;
434                         o->reference_source = REF_DAC_GND( channel );
435                         set_target( setup, ni_ao_unip_low_linearity( channel ), 1.0);
436                 }
437         }
438 }
439
440 static void ni_setup_observables( calibration_setup_t *setup )
441 {
442         comedi_insn tmpl;
443         int bipolar_lowgain;
444         int bipolar_highgain;
445         int unipolar_lowgain;
446         int unipolar_highgain;
447         double voltage_reference;
448         observable *o;
449
450         bipolar_lowgain = get_bipolar_lowgain( setup->dev, setup->ad_subdev);
451         bipolar_highgain = get_bipolar_highgain( setup->dev, setup->ad_subdev);
452         unipolar_lowgain = get_unipolar_lowgain( setup->dev, setup->ad_subdev);
453         unipolar_highgain = get_unipolar_highgain( setup->dev, setup->ad_subdev);
454
455         if( ni_board( setup )->ref_eeprom_lsb >= 0 &&
456                 ni_board( setup )->ref_eeprom_msb >= 0 )
457         {
458                 voltage_reference = ni_get_reference( setup,
459                         ni_board( setup )->ref_eeprom_lsb, ni_board( setup )->ref_eeprom_msb );
460         }else
461         {
462                 DPRINT( 0, "WARNING: unknown eeprom address for reference voltage\n"
463                         "correction.  This might be fixable if you send us an eeprom dump\n"
464                         "(see the demo/eeprom_dump program).\n");
465                 voltage_reference = 5.0;
466         }
467
468         memset(&tmpl,0,sizeof(tmpl));
469         tmpl.insn = INSN_READ;
470         tmpl.n = 1;
471         tmpl.subdev = setup->ad_subdev;
472
473         setup->n_observables = ni_num_observables;
474
475         /* 0 offset, low gain */
476         o = setup->observables + ni_zero_offset_low;
477         o->name = "ai, bipolar zero offset, low gain";
478         o->observe_insn = tmpl;
479         o->observe_insn.chanspec = CR_PACK(REF_GND_GND,bipolar_lowgain,AREF_OTHER)
480                 | CR_ALT_SOURCE | CR_ALT_FILTER;
481         o->reference_source = REF_GND_GND;
482         o->target = 0;
483
484         /* 0 offset, high gain */
485         o = setup->observables + ni_zero_offset_high;
486         o->name = "ai, bipolar zero offset, high gain";
487         o->observe_insn = tmpl;
488         o->observe_insn.chanspec = CR_PACK(REF_GND_GND,bipolar_highgain,AREF_OTHER)
489                 | CR_ALT_SOURCE | CR_ALT_FILTER;
490         o->reference_source = REF_GND_GND;
491         o->target = 0;
492
493         /* voltage reference */
494         o = setup->observables + ni_reference_low;
495         o->name = "ai, bipolar voltage reference, low gain";
496         o->observe_insn = tmpl;
497         o->observe_insn.chanspec = CR_PACK(REF_CALSRC_GND,bipolar_lowgain,AREF_OTHER)
498                 | CR_ALT_SOURCE | CR_ALT_FILTER;
499         o->reference_source = REF_CALSRC_GND;
500         o->target = voltage_reference;
501
502         if(unipolar_lowgain>=0){
503                 o = setup->observables + ni_unip_zero_offset_low;
504                 o->name = "ai, unipolar zero offset, low gain";
505                 o->observe_insn = tmpl;
506                 o->observe_insn.chanspec =
507                         CR_PACK(REF_GND_GND,unipolar_lowgain,AREF_OTHER)
508                         | CR_ALT_SOURCE | CR_ALT_FILTER;
509                 o->reference_source = REF_GND_GND;
510                 o->target = very_low_target( setup->dev, setup->ad_subdev, 0, unipolar_lowgain );
511
512                 o = setup->observables + ni_unip_reference_low;
513                 o->name = "ai, unipolar voltage reference, low gain";
514                 o->observe_insn = tmpl;
515                 o->observe_insn.chanspec =
516                         CR_PACK(REF_CALSRC_GND,unipolar_lowgain,AREF_OTHER)
517                         | CR_ALT_SOURCE | CR_ALT_FILTER;
518                 o->reference_source = REF_CALSRC_GND;
519                 o->target = voltage_reference;
520         }
521
522         if(unipolar_highgain >= 0)
523         {
524                 o = setup->observables + ni_unip_zero_offset_high;
525                 o->name = "ai, unipolar zero offset, high gain";
526                 o->observe_insn = tmpl;
527                 o->observe_insn.chanspec =
528                         CR_PACK(REF_GND_GND,unipolar_highgain,AREF_OTHER)
529                         | CR_ALT_SOURCE | CR_ALT_FILTER;
530                 o->reference_source = REF_GND_GND;
531                 o->target = very_low_target( setup->dev, setup->ad_subdev, 0, unipolar_highgain );
532         }
533
534         if(setup->da_subdev >= 0)
535                 ni_setup_ao_observables( setup );
536 }
537
538 /* for +-50V and +-20V ranges, the reference source goes 0V
539  * to 50V instead of 0V to 5V */
540 static unsigned int cal_gain_register_bits_611x( double reference, double *voltage )
541 {
542         int bits;
543
544         bits = 200.0 * ( *voltage / reference );
545         if( bits > 200 ) bits = 200;
546         if( bits < 0 ) bits = 0;
547
548         *voltage = reference * ( bits / 200.0 );
549         return bits;
550 }
551
552 static unsigned int ref_source_611x( unsigned int ref_source, unsigned int cal_gain_bits )
553 {
554         return ( ref_source & 0xf ) | ( ( cal_gain_bits << 4 ) & 0xff0 );
555 }
556
557 static void reference_target_611x( calibration_setup_t *setup,
558         observable *o, double master_reference, unsigned int range )
559 {
560         int cal_gain_reg_bits;
561         double reference;
562         double target;
563         comedi_range *range_ptr;
564
565         range_ptr = comedi_get_range( setup->dev, setup->ad_subdev, 0, range );
566         assert( range_ptr != NULL );
567         if( range_ptr->max > 19.0 ) reference = 10 * master_reference;
568         else reference = master_reference;
569         target = range_ptr->max * 0.8;
570
571         cal_gain_reg_bits = cal_gain_register_bits_611x( reference, &target );
572
573         o->reference_source = ref_source_611x( REF_CALSRC_GND, cal_gain_reg_bits );
574         o->target = target;
575 }
576
577 static void ni_setup_observables_611x( calibration_setup_t *setup )
578 {
579         comedi_insn tmpl;
580         comedi_insn po_tmpl;
581         int range, channel;
582         double master_reference;
583         observable *o;
584         int num_ai_channels, num_ai_ranges;
585         static const int num_ao_channels = 2;
586
587         setup->sv_settling_time_ns = 10000000;
588         setup->sv_order = 14;
589
590         master_reference = ni_get_reference( setup,
591                 ni_board( setup )->ref_eeprom_lsb, ni_board( setup )->ref_eeprom_msb );
592
593         memset(&tmpl,0,sizeof(tmpl));
594         tmpl.insn = INSN_READ;
595         tmpl.n = 1;
596         tmpl.subdev = setup->ad_subdev;
597
598         num_ai_channels = comedi_get_n_channels( setup->dev, setup->ad_subdev );
599         assert( num_ai_channels >= 0 );
600         num_ai_ranges = comedi_get_n_ranges( setup->dev, setup->ad_subdev, 0 );
601         assert( num_ai_ranges >= 0 );
602
603         for( channel = 0; channel < num_ai_channels; channel++ )
604         {
605                 for( range = 0; range < num_ai_ranges; range++ )
606                 {
607                         /* 0 offset */
608                         o = setup->observables + ni_zero_offset_611x( setup, channel, range );
609                         assert( o->name == NULL );
610                         asprintf( &o->name, "ai, ch %i, range %i, zero offset",
611                                 channel, range );
612                         o->observe_insn = tmpl;
613                         o->observe_insn.chanspec = CR_PACK( channel, range, AREF_DIFF )
614                                 | CR_ALT_SOURCE | CR_ALT_FILTER;
615                         o->reference_source = REF_GND_GND;
616                         o->target = 0.0;
617
618                         /* voltage reference */
619                         o = setup->observables + ni_reference_611x( setup, channel, range );
620                         assert( o->name == NULL );
621                         asprintf( &o->name, "ai, ch %i, range %i, voltage reference",
622                                 channel, range );
623                         o->observe_insn = tmpl;
624                         o->observe_insn.chanspec = CR_PACK( channel, range, AREF_DIFF )
625                                 | CR_ALT_SOURCE | CR_ALT_FILTER;
626                         reference_target_611x( setup, o, master_reference, range );
627                 }
628         }
629
630         memset(&po_tmpl,0,sizeof(po_tmpl));
631         po_tmpl.insn = INSN_WRITE;
632         po_tmpl.n = 1;
633         po_tmpl.subdev = setup->da_subdev;
634
635         for( channel = 0; channel < num_ao_channels; channel ++ )
636         {
637                 static const int ai_range_for_ao = 2;
638
639                 /* ao zero offset */
640                 o = setup->observables + ni_ao_zero_offset_611x( setup, channel, 0 );
641                 assert( o->name == NULL );
642                 asprintf( &o->name, "ao ch %i, zero offset", channel );
643                 o->preobserve_insn = po_tmpl;
644                 o->preobserve_insn.chanspec = CR_PACK( channel, 0, AREF_GROUND );
645                 o->preobserve_insn.data = o->preobserve_data;
646                 o->observe_insn = tmpl;
647                 o->observe_insn.chanspec = CR_PACK( 0, ai_range_for_ao, AREF_DIFF )
648                         | CR_ALT_SOURCE | CR_ALT_FILTER;
649                 o->reference_source = REF_DAC_GND( channel );
650                 set_target( setup, ni_ao_zero_offset_611x( setup, channel, 0 ), 0.0 );
651
652                 /* ao gain */
653                 o = setup->observables + ni_ao_reference_611x( setup, channel, 0 );
654                 assert( o->name == NULL );
655                 asprintf( &o->name, "ao ch %i, reference voltage", channel );
656                 o->preobserve_insn = po_tmpl;
657                 o->preobserve_insn.chanspec = CR_PACK( channel, 0, AREF_GROUND );
658                 o->preobserve_insn.data = o->preobserve_data;
659                 o->observe_insn = tmpl;
660                 o->observe_insn.chanspec = CR_PACK( 0, ai_range_for_ao, AREF_DIFF )
661                         | CR_ALT_SOURCE | CR_ALT_FILTER;
662                 o->reference_source = REF_DAC_GND( channel );
663                 set_target( setup, ni_ao_reference_611x( setup, channel, 0 ), 5.0 );
664         }
665
666         setup->n_observables = num_ao_observables_611x + 2 * num_ai_ranges * num_ai_channels;
667 }
668
669 static int cal_ni_daqcard_ai_16xe_50(calibration_setup_t *setup)
670 {
671         ni_caldac_layout_t layout;
672
673         init_ni_caldac_layout( &layout );
674         layout.adc_pregain_offset = 8;
675         layout.adc_postgain_offset = 2;
676         layout.adc_gain = 0;
677         layout.adc_gain_fine = 1;
678
679         return cal_ni_generic( setup, &layout );
680 }
681
682 static int cal_ni_at_mio_16xe_50(calibration_setup_t *setup)
683 {
684         ni_caldac_layout_t layout;
685
686         init_ni_caldac_layout( &layout );
687         layout.adc_pregain_offset = 8;
688         layout.adc_postgain_offset = 2;
689         layout.adc_gain = 0;
690         layout.adc_gain_fine = 1;
691         layout.dac_offset[ 0 ] = 6;
692         layout.dac_gain[ 0 ] = 4;
693         layout.dac_offset[ 1 ] = 7;
694         layout.dac_gain[ 1 ] = 5;
695
696         return cal_ni_generic( setup, &layout );
697 }
698
699 static int cal_ni_pci_mio_16xe_10(calibration_setup_t *setup)
700 {
701         ni_caldac_layout_t layout;
702
703         init_ni_caldac_layout( &layout );
704         layout.adc_pregain_offset = 8;
705         layout.adc_postgain_offset = 2;
706         layout.adc_postgain_offset_fine = 3;
707         layout.adc_gain = 0;
708         layout.adc_gain_fine = 1;
709         layout.dac_offset[ 0 ] = 6;
710         layout.dac_gain[ 0 ] = 4;
711         layout.dac_offset[ 1 ] = 7;
712         layout.dac_gain[ 1 ] = 5;
713
714         return cal_ni_generic( setup, &layout );
715 }
716
717 static int cal_ni_at_mio_16e_1(calibration_setup_t *setup)
718 {
719         ni_caldac_layout_t layout;
720
721         init_ni_caldac_layout( &layout );
722         layout.adc_pregain_offset = 0;
723         layout.adc_postgain_offset = 1;
724         layout.adc_gain = 3;
725         layout.adc_unip_offset = 2;
726         layout.dac_offset[0] = 5;
727         layout.dac_gain[0] = 6;
728         layout.dac_linearity[0] = 4;
729         layout.dac_offset[1] = 8;
730         layout.dac_gain[1] = 9;
731         layout.dac_linearity[1] = 7;
732
733         return cal_ni_generic( setup, &layout );
734 }
735
736 static int cal_ni_at_mio_16e_2(calibration_setup_t *setup)
737 {
738         return cal_ni_at_mio_16e_1(setup);
739 }
740
741 static int cal_ni_pci_mio_16e_1(calibration_setup_t *setup)
742 {
743         ni_caldac_layout_t layout;
744
745         init_ni_caldac_layout( &layout );
746         layout.adc_pregain_offset = 0;
747         layout.adc_postgain_offset = 1;
748         layout.adc_unip_offset = 2;
749         layout.adc_gain = 3;
750         layout.dac_offset[ 0 ] = 5;
751         layout.dac_gain[ 0 ] = 6;
752         layout.dac_linearity[ 0 ] = 4;
753         layout.dac_offset[ 1 ] = 8;
754         layout.dac_gain[ 1 ] = 9;
755         layout.dac_linearity[ 1 ] = 7;
756
757         return cal_ni_generic( setup, &layout );
758 }
759
760 static int cal_ni_pci_6014(calibration_setup_t *setup)
761 {
762         ni_caldac_layout_t layout;
763
764         init_ni_caldac_layout( &layout );
765         layout.adc_pregain_offset = 0;
766         layout.adc_postgain_offset = 4;
767         layout.adc_pregain_offset_fine = 8;
768         layout.adc_gain = 2;
769         layout.dac_offset[0] = 6;
770         layout.dac_offset_fine[0] = 10;
771         layout.dac_gain[0] = 7;
772         layout.dac_gain_fine[0] = 11;
773         layout.dac_offset[1] = 9;
774         layout.dac_offset_fine[1] = 1;
775         layout.dac_gain[1] = 3;
776         layout.dac_gain_fine[1] = 5;
777         return cal_ni_generic( setup, &layout );
778 }
779
780 static int cal_ni_pci_6032e(calibration_setup_t *setup)
781 {
782         ni_caldac_layout_t layout;
783
784         init_ni_caldac_layout( &layout );
785         layout.adc_pregain_offset = 8;
786         layout.adc_postgain_offset = 2;
787         layout.adc_postgain_offset_fine = 3;
788         layout.adc_gain = 0;
789         layout.adc_gain_fine = 1;
790
791         return cal_ni_generic( setup, &layout );
792 }
793
794 static int cal_ni_pci_6034e(calibration_setup_t *setup)
795 {
796         ni_caldac_layout_t layout;
797
798         init_ni_caldac_layout( &layout );
799         layout.adc_pregain_offset = 0;
800         layout.adc_pregain_offset_fine = 8;
801         layout.adc_postgain_offset = 4;
802         layout.adc_gain = 2;
803
804         return cal_ni_generic( setup, &layout );
805 }
806
807 static int cal_ni_pci_6035e(calibration_setup_t *setup)
808 {
809         /* this is for the ad8804_debug caldac */
810         ni_caldac_layout_t layout;
811
812         init_ni_caldac_layout( &layout );
813         layout.adc_pregain_offset = 0;
814         layout.adc_pregain_offset_fine = 8;
815         layout.adc_postgain_offset = 4;
816         layout.adc_gain = 2;
817         layout.dac_offset[ 0 ] = 6;
818         layout.dac_gain[ 0 ] = 11;
819         layout.dac_linearity[ 0 ] = 10;
820         layout.dac_offset[ 1 ] = 9;
821         layout.dac_gain[ 1 ] = 5;
822         layout.dac_linearity[ 1 ] = 1;
823
824         return cal_ni_generic( setup, &layout );
825 }
826
827 static int cal_ni_pci_6036e(calibration_setup_t *setup)
828 {
829         ni_caldac_layout_t layout;
830
831         if( comedi_get_version_code( setup->dev ) <= COMEDI_VERSION_CODE( 0, 7, 66 ) )
832         {
833                 DPRINT(0, "WARNING: you need comedi driver version 0.7.67 or later\n"
834                  "for this calibration to work properly\n" );
835         }
836
837         /* this is for the ad8804_debug caldac */
838         init_ni_caldac_layout( &layout );
839         layout.adc_pregain_offset = 0;
840         layout.adc_postgain_offset = 4;
841         layout.adc_pregain_offset_fine = 8;
842         layout.adc_gain = 2;
843         layout.dac_offset[ 0 ] = 6;
844         layout.dac_gain[ 0 ] = 7;
845         layout.dac_gain_fine[ 0 ] = 11;
846         layout.dac_linearity[ 0 ] = 10;
847         layout.dac_offset[ 1 ] = 9;
848         layout.dac_gain[ 1 ] = 3;
849         layout.dac_gain_fine[ 1 ] = 5;
850         layout.dac_linearity[ 1 ] = 1;
851
852         return cal_ni_generic( setup, &layout );
853 }
854
855 static int cal_ni_pci_6071e(calibration_setup_t *setup)
856 {
857         ni_caldac_layout_t layout;
858
859         if( comedi_get_version_code( setup->dev ) <= COMEDI_VERSION_CODE( 0, 7, 66 ) )
860         {
861                 DPRINT(0, "WARNING: you need comedi driver version 0.7.67 or later\n"
862                  "for this calibration to work properly\n" );
863         }
864
865         init_ni_caldac_layout( &layout );
866         layout.adc_pregain_offset = 8;
867         layout.adc_postgain_offset = 4;
868         layout.adc_unip_offset = 7;
869         layout.adc_gain = 2;
870         layout.dac_offset[ 0 ] = 6;
871         layout.dac_gain[ 0 ] = 11;
872         layout.dac_linearity[ 0 ] = 10;
873         layout.dac_offset[ 1 ] = 9;
874         layout.dac_gain[ 1 ] = 5;
875         layout.dac_linearity[ 1 ] = 1;
876         return cal_ni_generic( setup, &layout );
877 }
878
879 static int cal_ni_pxi_6071e(calibration_setup_t *setup)
880 {
881         ni_caldac_layout_t layout;
882
883         if( comedi_get_version_code( setup->dev ) <= COMEDI_VERSION_CODE( 0, 7, 66 ) )
884         {
885                 DPRINT(0, "WARNING: you need comedi driver version 0.7.67 or later\n"
886                  "for this calibration to work properly\n" );
887         }
888
889         init_ni_caldac_layout( &layout );
890         layout.adc_pregain_offset = 0;
891         layout.adc_pregain_offset_fine = 8;
892         layout.adc_postgain_offset = 4;
893         layout.adc_gain = 2;
894         layout.dac_offset[ 0 ] = 6;
895         layout.dac_gain[ 0 ] = 11;
896         layout.dac_linearity[ 0 ] = 10;
897         layout.dac_offset[ 1 ] = 9;
898         layout.dac_gain[ 1 ] = 5;
899         layout.dac_linearity[ 1 ] = 1;
900         return cal_ni_generic( setup, &layout );
901 }
902
903 static int cal_ni_at_mio_16e_10(calibration_setup_t *setup)
904 {
905         ni_caldac_layout_t layout;
906
907         if(comedi_get_version_code(setup->dev) <= COMEDI_VERSION_CODE(0, 7, 68))
908         {
909                 DPRINT(0, "WARNING: you need comedi driver version 0.7.69 or later\n"
910                  "for this calibration to work properly\n" );
911         }
912         init_ni_caldac_layout( &layout );
913         layout.adc_pregain_offset = 0;
914         layout.adc_pregain_offset_fine = 8;
915         layout.adc_postgain_offset = 4;
916         layout.adc_gain = 2;
917         layout.adc_unip_offset = 7;
918         layout.dac_offset[ 0 ] = 6;
919         layout.dac_gain[ 0 ] = 11; 
920         layout.dac_linearity[ 0 ] = 10;
921         layout.dac_offset[ 1 ] = 9;
922         layout.dac_gain[ 1 ] = 5;
923         layout.dac_linearity[1] = 1;
924         return cal_ni_generic( setup, &layout );
925 }
926
927 static int cal_ni_pci_mio_16xe_50(calibration_setup_t *setup)
928 {
929         ni_caldac_layout_t layout;
930
931         init_ni_caldac_layout( &layout );
932         layout.adc_pregain_offset = 8;
933         layout.adc_postgain_offset = 2;
934         layout.adc_gain = 0;
935         layout.adc_gain_fine = 1;
936         layout.adc_unip_offset = 7;
937         layout.dac_offset[ 0 ] = 6;
938         layout.dac_gain[ 0 ] = 4;
939         layout.dac_offset[ 1 ] = 7;
940         layout.dac_gain[ 1 ] = 5;
941
942         return cal_ni_generic( setup, &layout );
943 }
944
945 static int cal_ni_pci_6023e(calibration_setup_t *setup)
946 {
947         /* for comedi-0.7.65 */
948         ni_caldac_layout_t layout;
949
950         init_ni_caldac_layout( &layout );
951         layout.adc_pregain_offset = 8; /* possibly wrong */
952         layout.adc_pregain_offset_fine = 0;
953         layout.adc_postgain_offset = 4;
954         layout.adc_gain = 2;
955
956         return cal_ni_generic( setup, &layout );
957 }
958
959 static int cal_ni_pci_6024e(calibration_setup_t *setup)
960 {
961         ni_caldac_layout_t layout;
962
963         init_ni_caldac_layout( &layout );
964         layout.adc_pregain_offset = 0;
965         layout.adc_postgain_offset = 4;
966         layout.adc_pregain_offset_fine = 8;
967         layout.adc_gain = 2;
968         layout.dac_offset[ 0 ] = 6;
969         layout.dac_gain[ 0 ] = 11;
970         layout.dac_linearity[ 0 ] = 10;
971         layout.dac_offset[ 1 ] = 9;
972         layout.dac_gain[ 1 ] = 5;
973         layout.dac_linearity[ 1 ] = 1;
974
975         return cal_ni_generic( setup, &layout );
976 }
977
978 static int cal_ni_pci_6025e(calibration_setup_t *setup)
979 {
980         ni_caldac_layout_t layout;
981
982         init_ni_caldac_layout( &layout );
983         layout.adc_pregain_offset = 0;
984         layout.adc_postgain_offset = 4;
985         layout.adc_pregain_offset_fine = 8;
986         layout.adc_gain = 2;
987         layout.dac_offset[ 0 ] = 6;
988         layout.dac_gain[ 0 ] = 11;
989         layout.dac_linearity[ 0 ] = 10;
990         layout.dac_offset[ 1 ] = 9;
991         layout.dac_gain[ 1 ] = 5;
992         layout.dac_linearity[ 1 ] = 1;
993
994         return cal_ni_generic( setup, &layout );
995 }
996
997 static int cal_ni_pci_6052e(calibration_setup_t *setup)
998 {
999         /*
1000          * This board has noisy caldacs
1001          *
1002          * The NI documentation says (true mb88341 addressing):
1003          *   0, 8   AI pregain  (coarse, fine)
1004          *   4, 12  AI postgain
1005          *   2, 10  AI reference
1006          *   14, 7  AI unipolar offset
1007          *
1008          *   0      AO0 linearity
1009          *   8, 4   AO0 reference
1010          *   12     AO0 offset
1011          *   2      AO1 linearity
1012          *   10, 6  AO1 reference
1013          *   14     AO1 offset
1014          *
1015          *  For us, these map to (ad8804 channels)
1016          *
1017          *   0, 1   AI pregain  (coarse, fine)
1018          *   2, 3  AI postgain
1019          *   4, 5  AI reference
1020          *   7  AI unipolar offset
1021          *
1022          *   0      AO0 linearity
1023          *   1, 2   AO0 reference
1024          *   3      AO0 offset
1025          *   4      AO1 linearity
1026          *   5, 6   AO1 reference
1027          *   7      AO1 offset
1028          *
1029          *  or, with mb88341 channels
1030          *
1031          *   xxx    AO0 linearity
1032          *   7, 3   AO0 reference
1033          *   11     AO0 offset
1034          *   1      AO1 linearity
1035          *   9, 5   AO1 reference
1036          *   xxx    AO1 offset
1037          *
1038          */
1039         ni_caldac_layout_t layout;
1040
1041         init_ni_caldac_layout( &layout );
1042         layout.adc_pregain_offset = 0;
1043         layout.adc_postgain_offset = 2;
1044         layout.adc_gain = 4;
1045         layout.adc_unip_offset = 6;
1046         layout.adc_unip_offset_fine = 7;
1047         layout.adc_pregain_offset_fine = 1;
1048         layout.adc_postgain_offset_fine = 3;
1049         layout.adc_gain_fine = 5;
1050
1051         DPRINT(0, "WARNING: you need comedi driver version 0.7.67 or later\n"
1052          "for this calibration to work properly\n" );
1053 /* this works when the first two caldacs are ad8804_debug */
1054         layout.dac_offset[ 0 ] = 16 + 3;
1055         layout.dac_gain[ 0 ] = 16 + 1;
1056         layout.dac_gain_fine[ 0 ] = 16 + 2;
1057         layout.dac_linearity[ 0 ] = 16 + 0;
1058         layout.dac_offset[ 1 ] = 16 + 7;
1059         layout.dac_gain[ 1 ] = 16 + 5;
1060         layout.dac_gain_fine[ 1 ] = 16 + 6;
1061         layout.dac_linearity[ 1 ] = 16 + 4;
1062
1063         return cal_ni_generic( setup, &layout );
1064 }
1065
1066 static int cal_ni_daqcard_ai_16e_4(calibration_setup_t *setup)
1067 {
1068         ni_caldac_layout_t layout;
1069
1070         init_ni_caldac_layout( &layout );
1071         layout.adc_pregain_offset = 0;
1072         layout.adc_postgain_offset = 1;
1073         layout.adc_gain = 3;
1074         layout.adc_unip_offset = 2;
1075
1076         return cal_ni_generic( setup, &layout );
1077 }
1078
1079 static int adc_offset_611x( unsigned int channel )
1080 {
1081         return 2 * channel + 2;
1082 }
1083 static int adc_gain_611x( unsigned int channel )
1084 {
1085         return 2 * channel + 1;
1086 }
1087 static int dac_offset_611x( unsigned int channel )
1088 {
1089         return 12 + 2 + 2 * channel;
1090 }
1091 static int dac_gain_611x( unsigned int channel )
1092 {
1093         return 12 + 1 + 2 * channel;
1094 }
1095 static int cal_ni_pci_611x( calibration_setup_t *setup )
1096 {
1097         generic_layout_t layout;
1098
1099         init_generic_layout( &layout );
1100         layout.adc_offset = adc_offset_611x;
1101         layout.adc_gain = adc_gain_611x;
1102         layout.dac_offset = dac_offset_611x;
1103         layout.dac_gain = dac_gain_611x;
1104         layout.adc_high_observable = ni_reference_611x;
1105         layout.adc_ground_observable = ni_zero_offset_611x;
1106         layout.dac_high_observable = ni_ao_reference_611x;
1107         layout.dac_ground_observable = ni_ao_zero_offset_611x;
1108
1109         return generic_cal_by_channel_and_range( setup, &layout );
1110 }
1111
1112 static int cal_ni_pci_mio_16e_4( calibration_setup_t *setup )
1113 {
1114         ni_caldac_layout_t layout;
1115
1116         init_ni_caldac_layout( &layout );
1117         layout.adc_pregain_offset = 8;
1118         layout.adc_postgain_offset = 4;
1119         layout.adc_gain = 2;
1120         layout.adc_unip_offset = 7;
1121         layout.dac_offset[ 0 ] = 6;
1122         layout.dac_gain[ 0 ] = 11;
1123         layout.dac_linearity[ 0 ] = 10;
1124         layout.dac_offset[ 1 ] = 9;
1125         layout.dac_gain[ 1 ] = 5;
1126         layout.dac_linearity[ 1 ] = 1;
1127
1128         return cal_ni_generic( setup, &layout );
1129 }
1130
1131 static int cal_ni_daqcard_6062e( calibration_setup_t *setup )
1132 {
1133         ni_caldac_layout_t layout;
1134
1135         if( comedi_get_version_code( setup->dev ) <= COMEDI_VERSION_CODE( 0, 7, 66 ) )
1136         {
1137                 DPRINT(0, "WARNING: you need comedi driver version 0.7.67 or later\n"
1138                  "for this calibration to work properly\n" );
1139         }
1140         init_ni_caldac_layout( &layout );
1141         layout.adc_pregain_offset = 8;
1142         layout.adc_postgain_offset = 4;
1143         layout.adc_gain = 2;
1144         layout.adc_unip_offset = 7;
1145         layout.dac_offset[ 0 ] = 6;
1146         layout.dac_gain[ 0 ] = 11;
1147         layout.dac_linearity[ 0 ] = 10;
1148         layout.dac_offset[ 1 ] = 9;
1149         layout.dac_gain[ 1 ] = 5;
1150         layout.dac_linearity[ 1 ] = 1;
1151
1152         return cal_ni_generic( setup, &layout );
1153 }
1154
1155 static int cal_ni_daqcard_6024e( calibration_setup_t *setup )
1156 {
1157         ni_caldac_layout_t layout;
1158
1159         init_ni_caldac_layout( &layout );
1160
1161         layout.adc_pregain_offset = 0;
1162         layout.adc_postgain_offset = 4;
1163         layout.adc_gain = 2;
1164         //layout.adc_unip_offset = 7;
1165         layout.dac_offset[ 0 ] = 6;
1166         layout.dac_gain[ 0 ] = 3;
1167         //layout.dac_linearity[ 0 ] = 10;
1168         layout.dac_offset[ 1 ] = 1;
1169         layout.dac_gain[ 1 ] = 5;
1170         //layout.dac_linearity[ 1 ] = 1;
1171
1172         return cal_ni_generic( setup, &layout );
1173 }
1174
1175 static int cal_ni_daqcard_6036e( calibration_setup_t *setup )
1176 {
1177         ni_caldac_layout_t layout;
1178
1179         if( comedi_get_version_code( setup->dev ) <= COMEDI_VERSION_CODE( 0, 7, 68 ) )
1180         {
1181                 DPRINT(0, "WARNING: you need comedi driver version 0.7.69 or later\n"
1182                  "for this calibration to work properly\n" );
1183         }
1184
1185         init_ni_caldac_layout( &layout );
1186
1187         layout.adc_pregain_offset = 0;
1188         layout.adc_pregain_offset_fine = 8;
1189         layout.adc_postgain_offset = 4;
1190         layout.adc_gain = 2;
1191
1192         layout.dac_offset[0] = 6;
1193         layout.dac_gain[0] = 7;
1194         layout.dac_gain_fine[ 0 ] = 11;
1195         layout.dac_linearity[0] = 10;
1196
1197         layout.dac_offset[ 1 ] = 9;
1198         layout.dac_gain[ 1 ] = 3;
1199         layout.dac_gain_fine[ 1 ] = 5;
1200         layout.dac_linearity[1] = 1;
1201
1202         return cal_ni_generic( setup, &layout );
1203 }
1204
1205 static int cal_ni_at_mio_64e_3( calibration_setup_t *setup )
1206 {
1207         ni_caldac_layout_t layout;
1208
1209         if( comedi_get_version_code( setup->dev ) <= COMEDI_VERSION_CODE( 0, 7, 68 ) )
1210         {
1211                 DPRINT(0, "WARNING: you need comedi driver version 0.7.69 or later\n"
1212                  "for this calibration to work properly\n" );
1213         }
1214
1215         init_ni_caldac_layout( &layout );
1216         
1217         layout.adc_pregain_offset = 8;
1218         layout.adc_postgain_offset = 4;
1219         layout.adc_gain = 2;
1220
1221         layout.dac_offset[0] = 6;
1222         layout.dac_gain[0] = 14;
1223         layout.dac_linearity[0] = 10;
1224
1225         layout.dac_offset[ 1 ] = 9;
1226         layout.dac_gain[ 1 ] = 5;
1227         layout.dac_linearity[1] = 1;
1228
1229         return cal_ni_generic( setup, &layout );
1230 }
1231
1232 static void prep_adc_caldacs_generic( calibration_setup_t *setup,
1233         const ni_caldac_layout_t *layout, unsigned int range )
1234 {
1235         int retval;
1236
1237         if( setup->old_calibration == NULL )
1238         {
1239                 reset_caldac( setup, layout->adc_pregain_offset );
1240                 reset_caldac( setup, layout->adc_postgain_offset );
1241                 reset_caldac( setup, layout->adc_gain );
1242                 reset_caldac( setup, layout->adc_pregain_offset_fine );
1243                 reset_caldac( setup, layout->adc_postgain_offset_fine );
1244                 reset_caldac( setup, layout->adc_gain_fine );
1245                 reset_caldac( setup, layout->adc_unip_offset );
1246                 reset_caldac( setup, layout->adc_unip_offset_fine );
1247         }else
1248         {
1249                 retval = comedi_apply_parsed_calibration( setup->dev, setup->ad_subdev,
1250                         0, range, AREF_GROUND, setup->old_calibration );
1251                 if( retval < 0 )
1252                 {
1253                         DPRINT( 0, "Failed to apply existing calibration, reseting adc caldacs.\n" );
1254                         reset_caldac( setup, layout->adc_pregain_offset );
1255                         reset_caldac( setup, layout->adc_postgain_offset );
1256                         reset_caldac( setup, layout->adc_gain );
1257                         reset_caldac( setup, layout->adc_pregain_offset_fine );
1258                         reset_caldac( setup, layout->adc_postgain_offset_fine );
1259                         reset_caldac( setup, layout->adc_gain_fine );
1260                         reset_caldac( setup, layout->adc_unip_offset );
1261                         reset_caldac( setup, layout->adc_unip_offset_fine );
1262                 }
1263         }
1264 }
1265
1266 static void prep_dac_caldacs_generic( calibration_setup_t *setup,
1267         const ni_caldac_layout_t *layout, unsigned int channel, unsigned int range )
1268 {
1269         int retval;
1270
1271         if( setup->da_subdev < 0 ) return;
1272
1273         if( setup->old_calibration == NULL )
1274         {
1275                 reset_caldac( setup, layout->dac_offset[ channel ] );
1276                 reset_caldac( setup, layout->dac_offset_fine[ channel ] );
1277                 reset_caldac( setup, layout->dac_gain[ channel ] );
1278                 reset_caldac( setup, layout->dac_gain_fine[ channel ] );
1279                 reset_caldac( setup, layout->dac_linearity[ channel ] );
1280         }else
1281         {
1282                 retval = comedi_apply_parsed_calibration( setup->dev, setup->da_subdev,
1283                         channel, range, AREF_GROUND, setup->old_calibration );
1284                 if( retval < 0 )
1285                 {
1286                         DPRINT( 0, "Failed to apply existing calibration, reseting dac caldacs.\n" );
1287                         reset_caldac( setup, layout->dac_offset[ channel ] );
1288                         reset_caldac( setup, layout->dac_offset_fine[ channel ] );
1289                         reset_caldac( setup, layout->dac_gain[ channel ] );
1290                         reset_caldac( setup, layout->dac_gain_fine[ channel ] );
1291                         reset_caldac( setup, layout->dac_linearity[ channel ] );
1292                 }
1293         }
1294 }
1295
1296 static void prep_adc_for_dac( calibration_setup_t *setup, int observable )
1297 {
1298         unsigned int adc_range;
1299         int chanspec;
1300
1301         if( observable < 0 ) return;
1302
1303         chanspec = setup->observables[ observable ].observe_insn.chanspec;
1304         adc_range = CR_RANGE( chanspec );
1305
1306         comedi_apply_parsed_calibration( setup->dev, setup->ad_subdev,
1307                 0, adc_range, 0, setup->new_calibration );
1308 }
1309
1310 static int cal_ni_generic( calibration_setup_t *setup, const ni_caldac_layout_t *layout )
1311 {
1312         comedi_calibration_setting_t *current_cal;
1313         int retval;
1314         int num_ai_ranges;
1315         int range;
1316         int ai_unipolar_lowgain, ai_bipolar_lowgain;
1317
1318         num_ai_ranges = comedi_get_n_ranges( setup->dev, setup->ad_subdev, 0 );
1319         assert( num_ai_ranges > 0 );
1320
1321         ai_bipolar_lowgain = get_bipolar_lowgain( setup->dev, setup->ad_subdev );
1322         ai_unipolar_lowgain = get_unipolar_lowgain( setup->dev, setup->ad_subdev );
1323
1324         prep_adc_caldacs_generic( setup, layout, ai_bipolar_lowgain );
1325
1326         current_cal = sc_alloc_calibration_setting( setup );
1327         current_cal->subdevice = setup->ad_subdev;
1328         reset_caldac( setup, layout->adc_gain_fine );
1329         generic_do_relative( setup, current_cal, ni_zero_offset_low,
1330                 ni_reference_low, layout->adc_gain );
1331         reset_caldac( setup, layout->adc_postgain_offset_fine );
1332         generic_do_relative( setup, current_cal, ni_zero_offset_low,
1333                 ni_zero_offset_high, layout->adc_postgain_offset );
1334         generic_do_relative( setup, current_cal, ni_zero_offset_low,
1335                 ni_zero_offset_high, layout->adc_postgain_offset_fine );
1336         reset_caldac( setup, layout->adc_pregain_offset_fine );
1337         generic_do_cal( setup, current_cal, ni_zero_offset_high, layout->adc_pregain_offset );
1338         generic_do_relative( setup, current_cal, ni_zero_offset_low,
1339                 ni_reference_low, layout->adc_gain_fine );
1340         generic_do_cal( setup, current_cal, ni_zero_offset_high,
1341                 layout->adc_pregain_offset_fine );
1342         sc_push_channel( current_cal, SC_ALL_CHANNELS );
1343         sc_push_aref( current_cal, SC_ALL_AREFS );
1344         if( layout->adc_unip_offset >= 0 )
1345         {
1346                 sc_push_range( current_cal, SC_ALL_RANGES );
1347         }else
1348         {
1349                 for( range = 0; range < num_ai_ranges; range++ )
1350                 {
1351                         if( is_bipolar( setup->dev, setup->ad_subdev, 0, range ) )
1352                                 sc_push_range( current_cal, range );
1353                 }
1354         }
1355
1356         /* do seperate unipolar calibration if appropriate */
1357         if( ai_unipolar_lowgain >= 0 )
1358         {
1359                 current_cal = sc_alloc_calibration_setting( setup );
1360                 current_cal->subdevice = setup->ad_subdev;
1361                 if( layout->adc_unip_offset >= 0 )
1362                 {
1363                         reset_caldac( setup, layout->adc_unip_offset_fine );
1364                         generic_do_cal( setup, current_cal, ni_unip_zero_offset_high,
1365                                 layout->adc_unip_offset );
1366                         generic_do_cal( setup, current_cal, ni_unip_zero_offset_high,
1367                                 layout->adc_unip_offset_fine );
1368                 /* if we don't have a unipolar offset caldac, do a fully
1369                  * independent calibration for unipolar ranges */
1370                 }else
1371                 {
1372                         prep_adc_caldacs_generic( setup, layout, ai_unipolar_lowgain );
1373                         generic_peg( setup, ni_unip_zero_offset_low,
1374                                 layout->adc_pregain_offset, 1 );
1375                         generic_peg( setup, ni_unip_zero_offset_low,
1376                                 layout->adc_postgain_offset, 1 );
1377                         generic_do_relative( setup, current_cal, ni_unip_zero_offset_low,
1378                                 ni_unip_reference_low, layout->adc_gain );
1379                         generic_do_relative( setup, current_cal, ni_unip_zero_offset_low,
1380                                 ni_unip_zero_offset_high, layout->adc_postgain_offset );
1381                         generic_do_relative( setup, current_cal, ni_unip_zero_offset_low,
1382                                 ni_unip_zero_offset_high, layout->adc_postgain_offset_fine );
1383                         generic_do_cal( setup, current_cal, ni_unip_zero_offset_high,
1384                                 layout->adc_pregain_offset );
1385                         generic_do_relative( setup, current_cal, ni_unip_zero_offset_low,
1386                                 ni_unip_reference_low, layout->adc_gain_fine );
1387                         generic_do_cal( setup, current_cal, ni_unip_zero_offset_high,
1388                                 layout->adc_pregain_offset_fine );
1389                 }
1390                 for( range = 0; range < num_ai_ranges; range++ )
1391                 {
1392                         if( is_unipolar( setup->dev, setup->ad_subdev, 0, range ) )
1393                                 sc_push_range( current_cal, range );
1394                 }
1395                 sc_push_channel( current_cal, SC_ALL_CHANNELS );
1396                 sc_push_aref( current_cal, SC_ALL_AREFS );
1397         }
1398         if( setup->da_subdev >= 0 && setup->do_output )
1399         {
1400                 unsigned int channel, range;
1401                 int ao_unipolar_lowgain = get_unipolar_lowgain( setup->dev, setup->da_subdev );
1402                 int ao_bipolar_lowgain = get_bipolar_lowgain( setup->dev, setup->da_subdev );
1403                 int num_ao_ranges;
1404
1405                 for( channel = 0; channel < 2; channel++ )
1406                 {
1407                         num_ao_ranges = comedi_get_n_ranges( setup->dev, setup->da_subdev, channel );
1408                         prep_dac_caldacs_generic( setup, layout, channel, ao_bipolar_lowgain );
1409                         prep_adc_for_dac( setup, ni_ao_reference( channel ) );
1410
1411                         current_cal = sc_alloc_calibration_setting( setup );
1412                         current_cal->subdevice = setup->da_subdev;
1413                         generic_do_linearity( setup, current_cal, ni_ao_zero_offset( channel ),
1414                                 ni_ao_mid_linearity( channel ), ni_ao_reference( channel ),
1415                                 layout->dac_linearity[ channel ] );
1416                         reset_caldac(setup, layout->dac_offset_fine[channel]);
1417                         generic_do_cal( setup, current_cal, ni_ao_zero_offset( channel ),
1418                                 layout->dac_offset[ channel ] );
1419                         generic_do_cal( setup, current_cal, ni_ao_zero_offset( channel ),
1420                                 layout->dac_offset_fine[ channel ] );
1421                         reset_caldac( setup, layout->dac_gain_fine[ channel ] );
1422                         generic_do_cal( setup, current_cal, ni_ao_reference( channel ),
1423                                 layout->dac_gain[ channel ] );
1424                         generic_do_cal( setup, current_cal, ni_ao_reference( channel ),
1425                                 layout->dac_gain_fine[ channel ] );
1426                         sc_push_channel( current_cal, channel );
1427                         for( range = 0; range < num_ao_ranges; range++ )
1428                         {
1429                                 if( is_bipolar( setup->dev, setup->da_subdev, channel, range ) )
1430                                         sc_push_range( current_cal, range );
1431                         }
1432                         sc_push_aref( current_cal, SC_ALL_AREFS );
1433
1434                         if( ao_unipolar_lowgain >= 0 )
1435                         {
1436                                 prep_dac_caldacs_generic( setup, layout, channel, ao_unipolar_lowgain );
1437
1438                                 current_cal = sc_alloc_calibration_setting( setup );
1439                                 current_cal->subdevice = setup->da_subdev;
1440                                 generic_do_linearity( setup, current_cal, ni_ao_unip_low_linearity( channel ),
1441                                         ni_ao_unip_mid_linearity( channel ), ni_ao_unip_reference( channel ),
1442                                         layout->dac_linearity[ channel ] );
1443                                 reset_caldac( setup, layout->dac_offset_fine[ channel ] );
1444                                 generic_do_cal( setup, current_cal, ni_ao_unip_zero_offset( channel),
1445                                         layout->dac_offset[ channel ] );
1446                                 generic_do_cal( setup, current_cal, ni_ao_unip_zero_offset( channel),
1447                                         layout->dac_offset_fine[ channel ] );
1448                                 reset_caldac( setup, layout->dac_gain_fine[ channel ] );
1449                                 generic_do_cal( setup, current_cal, ni_ao_unip_reference( channel ),
1450                                         layout->dac_gain[ channel ] );
1451                                 generic_do_cal( setup, current_cal, ni_ao_unip_reference( channel ),
1452                                         layout->dac_gain_fine[ channel ] );
1453                                 sc_push_channel( current_cal, channel );
1454                                 for( range = 0; range < num_ao_ranges; range++ )
1455                                 {
1456                                         if( is_unipolar( setup->dev, setup->da_subdev, channel, range ) )
1457                                                 sc_push_range( current_cal, range );
1458                                 }
1459                                 sc_push_aref( current_cal, SC_ALL_AREFS );
1460                         }
1461                 }
1462         }
1463
1464         retval = write_calibration_file( setup );
1465
1466         return retval;
1467 }
1468
1469 static double ni_get_reference( calibration_setup_t *setup, int lsb_loc,int msb_loc)
1470 {
1471         int lsb,msb;
1472         int16_t uv;
1473         double ref;
1474
1475         lsb=read_eeprom( setup, lsb_loc );
1476         msb=read_eeprom( setup, msb_loc );
1477         assert( lsb >=0 && msb >= 0 );
1478         DPRINT(0,"eeprom reference lsb=%d msb=%d\n", lsb, msb);
1479
1480         uv = ( lsb & 0xff ) | ( ( msb << 8 ) & 0xff00 );
1481         ref=5.000+1.0e-6*uv;
1482         DPRINT(0, "resulting reference voltage: %g\n", ref );
1483         if( fabs( ref - 5.0 ) > 0.005 )
1484                 DPRINT( 0, "WARNING: eeprom indicates reference is more than 5mV away\n"
1485                         "from 5V.  Possible bad eeprom address?\n" );
1486
1487         return ref;
1488 }
1489
1490 /****************
1491  NI 671x and 673x support
1492  **************/
1493
1494 static const int channels_per_ad8804 = 16;
1495
1496 static inline int ni67xx_ao_gain_caldac(unsigned int ao_channel)
1497 {
1498         int ad8804_gain_channels[4] = {8, 2, 11, 5};
1499         int caldac_channel = ad8804_gain_channels[ao_channel % 4];
1500         int caldac_index = ao_channel / 4;
1501         /* just guessing that second ad8804 is works for ao channels 4-7
1502          * the same as the first ad8804 works for ao channels 0-3 */
1503         return caldac_index * channels_per_ad8804 + caldac_channel;
1504 }
1505 static inline int ni67xx_ao_linearity_caldac(unsigned int ao_channel)
1506 {
1507         int ad8804_linearity_channels[4] = {4, 10, 1, 0};
1508         int caldac_channel = ad8804_linearity_channels[ao_channel % 4];
1509         int caldac_index = ao_channel / 4;
1510
1511         return caldac_index * channels_per_ad8804 + caldac_channel;
1512 }
1513 static inline int ni67xx_ao_offset_caldac(unsigned int ao_channel)
1514 {
1515         int ad8804_offset_channels[4] = {7, 6, 9, 3};
1516         int caldac_channel = ad8804_offset_channels[ao_channel % 4];
1517         int caldac_index = ao_channel / 4;
1518
1519         return caldac_index * channels_per_ad8804 + caldac_channel;
1520 }
1521
1522 static int ni67xx_ao_ground_observable_index( const calibration_setup_t *setup,
1523         unsigned int channel, unsigned int ao_range )
1524 {
1525         return 3 * channel + 0;
1526 }
1527
1528 static int ni67xx_ao_mid_observable_index( const calibration_setup_t *setup,
1529         unsigned int channel, unsigned int ao_range )
1530 {
1531         return 3 * channel + 1;
1532 }
1533
1534 static int ni67xx_ao_high_observable_index( const calibration_setup_t *setup,
1535         unsigned int channel, unsigned int ao_range )
1536 {
1537         return 3 * channel + 2;
1538 }
1539
1540 static const double ni67xx_unitless_adc_offset = 0.5;
1541
1542 /* determine conversion factor between actual voltage and
1543  * interval [0,1) returned by reads from the calibration adc
1544  * subdevice.
1545  */
1546 static double ni67xx_unitless_adc_slope(calibration_setup_t *setup)
1547 {
1548         double reference_in_volts;
1549         double reference_unitless;
1550         double slope;
1551         comedi_insn insn;
1552         lsampl_t data;
1553         comedi_range *range;
1554         static const int maxdata = 0x10000;
1555         int retval;
1556
1557         if(ni_board(setup)->ref_eeprom_lsb >= 0 &&
1558                 ni_board(setup)->ref_eeprom_msb >= 0)
1559         {
1560                 reference_in_volts = ni_get_reference(setup,
1561                         ni_board(setup)->ref_eeprom_lsb, ni_board(setup)->ref_eeprom_msb );
1562         }else
1563         {
1564                 DPRINT( 0, "WARNING: unknown eeprom address for reference voltage\n"
1565                         "correction.  This might be fixable if you send us an eeprom dump\n"
1566                         "(see the demo/eeprom_dump program).\n");
1567                 reference_in_volts = 5.0;
1568         }
1569
1570         memset(&insn, 0, sizeof(insn));
1571         insn.insn = INSN_READ;
1572         insn.n = 1;
1573         insn.subdev = setup->ad_subdev;
1574         insn.data = &data;
1575         insn.chanspec = CR_PACK(0, 0, AREF_GROUND) | CR_ALT_SOURCE;
1576         retval = comedi_do_insn(setup->dev, &insn);
1577         assert(retval >= 0);
1578
1579         range = comedi_get_range(setup->dev, setup->ad_subdev, 0, 0);
1580         assert( range );
1581         reference_unitless = comedi_to_phys(data, range, maxdata);
1582
1583         slope = (reference_unitless - ni67xx_unitless_adc_offset) / reference_in_volts;
1584
1585         return slope;
1586 }
1587
1588 /* calibration adc uses RANGE_UNKNOWN, so it will return a value from
1589    0.0 to 1.0 instead of a voltage, so we need to renormalize. */
1590 static void ni67xx_set_target( calibration_setup_t *setup, int obs, double target, double slope)
1591 {
1592         set_target(setup, obs, target);
1593         /* convert target from volts to interval [0,1) which calibration
1594          * adc returns */
1595         setup->observables[obs].target *= slope;
1596         setup->observables[obs].target += ni67xx_unitless_adc_offset;
1597 }
1598
1599 static void ni67xx_setup_observables( calibration_setup_t *setup )
1600 {
1601         comedi_insn tmpl, po_tmpl;
1602         observable *o;
1603         int num_ao_channels;
1604         int i;
1605         double slope;
1606
1607         slope = ni67xx_unitless_adc_slope(setup);
1608
1609         /* calibration adc is very slow (15HZ) but accurate, so only sample a few times */
1610         setup->sv_order = 1;
1611
1612         num_ao_channels = comedi_get_n_channels(setup->dev, setup->da_subdev);
1613         assert(num_ao_channels >= 0);
1614
1615         memset( &tmpl, 0, sizeof(tmpl) );
1616         tmpl.insn = INSN_READ;
1617         tmpl.n = 1;
1618         tmpl.subdev = setup->ad_subdev;
1619
1620         memset( &po_tmpl, 0, sizeof(po_tmpl) );
1621         po_tmpl.insn = INSN_WRITE;
1622         po_tmpl.n = 1;
1623         po_tmpl.subdev = setup->da_subdev;
1624
1625         setup->n_observables = 0;
1626
1627         for(i = 0; i < num_ao_channels; i++)
1628         {
1629                 o = setup->observables + ni67xx_ao_ground_observable_index( setup,
1630                         i, 0);
1631                 o->reference_source = -1;
1632                 assert( o->name == NULL );
1633                 asprintf(&o->name, "dac%i ground, ground referenced", i);
1634                 o->preobserve_insn = po_tmpl;
1635                 o->preobserve_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
1636                 o->preobserve_insn.data = o->preobserve_data;
1637                 o->observe_insn = tmpl;
1638                 o->observe_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
1639                 ni67xx_set_target(setup, ni67xx_ao_ground_observable_index(setup, i, 0), 0.0, slope);
1640                 setup->n_observables++;
1641
1642                 o = setup->observables + ni67xx_ao_mid_observable_index( setup,
1643                         i, 0);
1644                 o->reference_source = -1;
1645                 assert( o->name == NULL );
1646                 asprintf(&o->name, "dac%i mid, ground referenced", i);
1647                 o->preobserve_insn = po_tmpl;
1648                 o->preobserve_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
1649                 o->preobserve_insn.data = o->preobserve_data;
1650                 o->observe_insn = tmpl;
1651                 o->observe_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
1652                 ni67xx_set_target(setup, ni67xx_ao_mid_observable_index(setup, i, 0), 4.0, slope);
1653                 setup->n_observables++;
1654
1655                 o = setup->observables + ni67xx_ao_high_observable_index( setup, i, 0);
1656                 o->reference_source = -1;
1657                 assert( o->name == NULL );
1658                 asprintf(&o->name, "dac%i high, ground referenced", i);
1659                 o->preobserve_insn = po_tmpl;
1660                 o->preobserve_insn.chanspec = CR_PACK( i, 0, AREF_GROUND );
1661                 o->preobserve_insn.data = o->preobserve_data;
1662                 o->observe_insn = tmpl;
1663                 o->observe_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
1664                 ni67xx_set_target(setup, ni67xx_ao_high_observable_index(setup, i, 0), 8.0, slope);
1665                 setup->n_observables++;
1666         }
1667
1668         return;
1669 }
1670
1671 static int cal_ni_pci_6711(calibration_setup_t *setup)
1672 {
1673         generic_layout_t layout;
1674         
1675         if( comedi_get_version_code( setup->dev ) <= COMEDI_VERSION_CODE(0, 7, 69))
1676         {
1677                 DPRINT(0, "WARNING: you need comedi driver version 0.7.69 or later\n"
1678                  "for this calibration to work properly\n" );
1679         }
1680         init_generic_layout( &layout );
1681         layout.dac_gain = ni67xx_ao_gain_caldac;
1682         layout.dac_linearity = ni67xx_ao_linearity_caldac;
1683         layout.dac_offset = ni67xx_ao_offset_caldac;
1684         layout.dac_high_observable = ni67xx_ao_high_observable_index;
1685         layout.dac_mid_observable = ni67xx_ao_mid_observable_index;
1686         layout.dac_ground_observable = ni67xx_ao_ground_observable_index;
1687         layout.dac_fractional_tolerance = get_tolerance( setup, setup->da_subdev, 1.0 );
1688         return generic_cal_ao(setup, &layout);
1689 }
1690