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