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