filling in missing caldacs for at-mio-16e-10
[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_SOME, 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         ni_caldac_layout_t layout;
866
867         if(comedi_get_version_code(setup->dev) <= COMEDI_VERSION_CODE(0, 7, 68))
868         {
869                 DPRINT(0, "WARNING: you need comedi driver version 0.7.69 or later\n"
870                  "for this calibration to work properly\n" );
871         }
872         init_ni_caldac_layout( &layout );
873         layout.adc_pregain_offset = 0;
874         layout.adc_pregain_offset_fine = 8;
875         layout.adc_postgain_offset = 4;
876         layout.adc_gain = 2;
877         layout.adc_unip_offset = 7;
878         layout.dac_offset[ 0 ] = 6;
879         layout.dac_gain[ 0 ] = 11; 
880         layout.dac_linearity[ 0 ] = 10;
881         layout.dac_offset[ 1 ] = 9;
882         layout.dac_gain[ 1 ] = 5;
883         layout.dac_linearity[1] = 1;
884         return cal_ni_generic( setup, &layout );
885 }
886
887 static int cal_ni_pci_mio_16xe_50(calibration_setup_t *setup)
888 {
889         ni_caldac_layout_t layout;
890
891         init_ni_caldac_layout( &layout );
892         layout.adc_pregain_offset = 8;
893         layout.adc_postgain_offset = 2;
894         layout.adc_gain = 0;
895         layout.adc_gain_fine = 1;
896         layout.adc_unip_offset = 7;
897         layout.dac_offset[ 0 ] = 6;
898         layout.dac_gain[ 0 ] = 4;
899         layout.dac_offset[ 1 ] = 7;
900         layout.dac_gain[ 1 ] = 5;
901
902         return cal_ni_generic( setup, &layout );
903 }
904
905 static int cal_ni_pci_6023e(calibration_setup_t *setup)
906 {
907         /* for comedi-0.7.65 */
908         ni_caldac_layout_t layout;
909
910         init_ni_caldac_layout( &layout );
911         layout.adc_pregain_offset = 8; /* possibly wrong */
912         layout.adc_pregain_offset_fine = 0;
913         layout.adc_postgain_offset = 4;
914         layout.adc_gain = 2;
915
916         return cal_ni_generic( setup, &layout );
917 }
918
919 static int cal_ni_pci_6024e(calibration_setup_t *setup)
920 {
921         ni_caldac_layout_t layout;
922
923         init_ni_caldac_layout( &layout );
924         layout.adc_pregain_offset = 0;
925         layout.adc_postgain_offset = 4;
926         layout.adc_pregain_offset_fine = 8;
927         layout.adc_gain = 2;
928         layout.dac_offset[ 0 ] = 6;
929         layout.dac_gain[ 0 ] = 11;
930         layout.dac_linearity[ 0 ] = 10;
931         layout.dac_offset[ 1 ] = 9;
932         layout.dac_gain[ 1 ] = 5;
933         layout.dac_linearity[ 1 ] = 1;
934
935         return cal_ni_generic( setup, &layout );
936 }
937
938 static int cal_ni_pci_6025e(calibration_setup_t *setup)
939 {
940         ni_caldac_layout_t layout;
941
942         init_ni_caldac_layout( &layout );
943         layout.adc_pregain_offset = 0;
944         layout.adc_postgain_offset = 4;
945         layout.adc_pregain_offset_fine = 8;
946         layout.adc_gain = 2;
947         layout.dac_offset[ 0 ] = 6;
948         layout.dac_gain[ 0 ] = 11;
949         layout.dac_linearity[ 0 ] = 10;
950         layout.dac_offset[ 1 ] = 9;
951         layout.dac_gain[ 1 ] = 5;
952         layout.dac_linearity[ 1 ] = 1;
953
954         return cal_ni_generic( setup, &layout );
955 }
956
957 static int cal_ni_pci_6052e(calibration_setup_t *setup)
958 {
959         /*
960          * This board has noisy caldacs
961          *
962          * The NI documentation says (true mb88341 addressing):
963          *   0, 8   AI pregain  (coarse, fine)
964          *   4, 12  AI postgain
965          *   2, 10  AI reference
966          *   14, 7  AI unipolar offset
967          *
968          *   0      AO0 linearity
969          *   8, 4   AO0 reference
970          *   12     AO0 offset
971          *   2      AO1 linearity
972          *   10, 6  AO1 reference
973          *   14     AO1 offset
974          *
975          *  For us, these map to (ad8804 channels)
976          *
977          *   0, 1   AI pregain  (coarse, fine)
978          *   2, 3  AI postgain
979          *   4, 5  AI reference
980          *   7  AI unipolar offset
981          *
982          *   0      AO0 linearity
983          *   1, 2   AO0 reference
984          *   3      AO0 offset
985          *   4      AO1 linearity
986          *   5, 6   AO1 reference
987          *   7      AO1 offset
988          *
989          *  or, with mb88341 channels
990          *
991          *   xxx    AO0 linearity
992          *   7, 3   AO0 reference
993          *   11     AO0 offset
994          *   1      AO1 linearity
995          *   9, 5   AO1 reference
996          *   xxx    AO1 offset
997          *
998          */
999         ni_caldac_layout_t layout;
1000
1001         init_ni_caldac_layout( &layout );
1002         layout.adc_pregain_offset = 0;
1003         layout.adc_postgain_offset = 2;
1004         layout.adc_gain = 4;
1005         layout.adc_unip_offset = 6;
1006         layout.adc_unip_offset_fine = 7;
1007         layout.adc_pregain_offset_fine = 1;
1008         layout.adc_postgain_offset_fine = 3;
1009         layout.adc_gain_fine = 5;
1010
1011         DPRINT(0, "WARNING: you need comedi driver version 0.7.67 or later\n"
1012          "for this calibration to work properly\n" );
1013 /* this works when the first two caldacs are ad8804_debug */
1014         layout.dac_offset[ 0 ] = 16 + 3;
1015         layout.dac_gain[ 0 ] = 16 + 1;
1016         layout.dac_gain_fine[ 0 ] = 16 + 2;
1017         layout.dac_linearity[ 0 ] = 16 + 0;
1018         layout.dac_offset[ 1 ] = 16 + 7;
1019         layout.dac_gain[ 1 ] = 16 + 5;
1020         layout.dac_gain_fine[ 1 ] = 16 + 6;
1021         layout.dac_linearity[ 1 ] = 16 + 4;
1022
1023         return cal_ni_generic( setup, &layout );
1024 }
1025
1026 static int cal_ni_daqcard_ai_16e_4(calibration_setup_t *setup)
1027 {
1028         ni_caldac_layout_t layout;
1029
1030         init_ni_caldac_layout( &layout );
1031         layout.adc_pregain_offset = 0;
1032         layout.adc_postgain_offset = 1;
1033         layout.adc_gain = 3;
1034         layout.adc_unip_offset = 2;
1035
1036         return cal_ni_generic( setup, &layout );
1037 }
1038
1039 static int adc_offset_611x( unsigned int channel )
1040 {
1041         return 2 * channel + 2;
1042 }
1043 static int adc_gain_611x( unsigned int channel )
1044 {
1045         return 2 * channel + 1;
1046 }
1047 static int dac_offset_611x( unsigned int channel )
1048 {
1049         return 12 + 2 + 2 * channel;
1050 }
1051 static int dac_gain_611x( unsigned int channel )
1052 {
1053         return 12 + 1 + 2 * channel;
1054 }
1055 static int cal_ni_pci_611x( calibration_setup_t *setup )
1056 {
1057         generic_layout_t layout;
1058
1059         init_generic_layout( &layout );
1060         layout.adc_offset = adc_offset_611x;
1061         layout.adc_gain = adc_gain_611x;
1062         layout.dac_offset = dac_offset_611x;
1063         layout.dac_gain = dac_gain_611x;
1064         layout.adc_high_observable = ni_reference_611x;
1065         layout.adc_ground_observable = ni_zero_offset_611x;
1066         layout.dac_high_observable = ni_ao_reference_611x;
1067         layout.dac_ground_observable = ni_ao_zero_offset_611x;
1068
1069         return generic_cal_by_channel_and_range( setup, &layout );
1070 }
1071
1072 static int cal_ni_pci_mio_16e_4( calibration_setup_t *setup )
1073 {
1074         ni_caldac_layout_t layout;
1075
1076         init_ni_caldac_layout( &layout );
1077         layout.adc_pregain_offset = 8;
1078         layout.adc_postgain_offset = 4;
1079         layout.adc_gain = 2;
1080         layout.adc_unip_offset = 7;
1081         layout.dac_offset[ 0 ] = 6;
1082         layout.dac_gain[ 0 ] = 11;
1083         layout.dac_linearity[ 0 ] = 10;
1084         layout.dac_offset[ 1 ] = 9;
1085         layout.dac_gain[ 1 ] = 5;
1086         layout.dac_linearity[ 1 ] = 1;
1087
1088         return cal_ni_generic( setup, &layout );
1089 }
1090
1091 static int cal_ni_daqcard_6062e( calibration_setup_t *setup )
1092 {
1093         ni_caldac_layout_t layout;
1094
1095         if( comedi_get_version_code( setup->dev ) <= COMEDI_VERSION_CODE( 0, 7, 66 ) )
1096         {
1097                 DPRINT(0, "WARNING: you need comedi driver version 0.7.67 or later\n"
1098                  "for this calibration to work properly\n" );
1099         }
1100         init_ni_caldac_layout( &layout );
1101         layout.adc_pregain_offset = 8;
1102         layout.adc_postgain_offset = 4;
1103         layout.adc_gain = 2;
1104         layout.adc_unip_offset = 7;
1105         layout.dac_offset[ 0 ] = 6;
1106         layout.dac_gain[ 0 ] = 11;
1107         layout.dac_linearity[ 0 ] = 10;
1108         layout.dac_offset[ 1 ] = 9;
1109         layout.dac_gain[ 1 ] = 5;
1110         layout.dac_linearity[ 1 ] = 1;
1111
1112         return cal_ni_generic( setup, &layout );
1113 }
1114
1115 static int cal_ni_daqcard_6024e( calibration_setup_t *setup )
1116 {
1117         ni_caldac_layout_t layout;
1118
1119         init_ni_caldac_layout( &layout );
1120
1121         layout.adc_pregain_offset = 0;
1122         layout.adc_postgain_offset = 4;
1123         layout.adc_gain = 2;
1124         //layout.adc_unip_offset = 7;
1125         layout.dac_offset[ 0 ] = 6;
1126         layout.dac_gain[ 0 ] = 3;
1127         //layout.dac_linearity[ 0 ] = 10;
1128         layout.dac_offset[ 1 ] = 1;
1129         layout.dac_gain[ 1 ] = 5;
1130         //layout.dac_linearity[ 1 ] = 1;
1131
1132         return cal_ni_generic( setup, &layout );
1133 }
1134
1135 static int cal_ni_daqcard_6036e( calibration_setup_t *setup )
1136 {
1137         ni_caldac_layout_t layout;
1138
1139         if( comedi_get_version_code( setup->dev ) <= COMEDI_VERSION_CODE( 0, 7, 68 ) )
1140         {
1141                 DPRINT(0, "WARNING: you need comedi driver version 0.7.69 or later\n"
1142                  "for this calibration to work properly\n" );
1143         }
1144
1145         init_ni_caldac_layout( &layout );
1146
1147         layout.adc_pregain_offset = 0;
1148         layout.adc_postgain_offset = 4;
1149         layout.adc_gain = 2;
1150
1151         layout.dac_offset[ 0 ] = 6;
1152         layout.dac_gain[ 0 ] = 7;
1153 //      layout.dac_gain_fine[ 0 ] = XXX;
1154
1155         layout.dac_offset[ 1 ] = 1;
1156         layout.dac_gain[ 1 ] = 3;
1157         layout.dac_gain_fine[ 1 ] = 5;
1158
1159         return cal_ni_generic( setup, &layout );
1160 }
1161
1162 static void prep_adc_caldacs_generic( calibration_setup_t *setup,
1163         const ni_caldac_layout_t *layout, unsigned int range )
1164 {
1165         int retval;
1166
1167         if( setup->old_calibration == NULL )
1168         {
1169                 reset_caldac( setup, layout->adc_pregain_offset );
1170                 reset_caldac( setup, layout->adc_postgain_offset );
1171                 reset_caldac( setup, layout->adc_gain );
1172                 reset_caldac( setup, layout->adc_pregain_offset_fine );
1173                 reset_caldac( setup, layout->adc_postgain_offset_fine );
1174                 reset_caldac( setup, layout->adc_gain_fine );
1175                 reset_caldac( setup, layout->adc_unip_offset );
1176                 reset_caldac( setup, layout->adc_unip_offset_fine );
1177         }else
1178         {
1179                 retval = comedi_apply_parsed_calibration( setup->dev, setup->ad_subdev,
1180                         0, range, AREF_GROUND, setup->old_calibration );
1181                 if( retval < 0 )
1182                 {
1183                         DPRINT( 0, "Failed to apply existing calibration, reseting adc caldacs.\n" );
1184                         reset_caldac( setup, layout->adc_pregain_offset );
1185                         reset_caldac( setup, layout->adc_postgain_offset );
1186                         reset_caldac( setup, layout->adc_gain );
1187                         reset_caldac( setup, layout->adc_pregain_offset_fine );
1188                         reset_caldac( setup, layout->adc_postgain_offset_fine );
1189                         reset_caldac( setup, layout->adc_gain_fine );
1190                         reset_caldac( setup, layout->adc_unip_offset );
1191                         reset_caldac( setup, layout->adc_unip_offset_fine );
1192                 }
1193         }
1194 }
1195
1196 static void prep_dac_caldacs_generic( calibration_setup_t *setup,
1197         const ni_caldac_layout_t *layout, unsigned int channel, unsigned int range )
1198 {
1199         int retval;
1200
1201         if( setup->da_subdev < 0 ) return;
1202
1203         if( setup->old_calibration == NULL )
1204         {
1205                 reset_caldac( setup, layout->dac_offset[ channel ] );
1206                 reset_caldac( setup, layout->dac_gain[ channel ] );
1207                 reset_caldac( setup, layout->dac_gain_fine[ channel ] );
1208                 reset_caldac( setup, layout->dac_linearity[ channel ] );
1209         }else
1210         {
1211                 retval = comedi_apply_parsed_calibration( setup->dev, setup->da_subdev,
1212                         channel, range, AREF_GROUND, setup->old_calibration );
1213                 if( retval < 0 )
1214                 {
1215                         DPRINT( 0, "Failed to apply existing calibration, reseting dac caldacs.\n" );
1216                         reset_caldac( setup, layout->dac_offset[ channel ] );
1217                         reset_caldac( setup, layout->dac_gain[ channel ] );
1218                         reset_caldac( setup, layout->dac_gain_fine[ channel ] );
1219                         reset_caldac( setup, layout->dac_linearity[ channel ] );
1220                 }
1221         }
1222 }
1223
1224 static void prep_adc_for_dac( calibration_setup_t *setup, int observable )
1225 {
1226         unsigned int adc_range;
1227         int chanspec;
1228
1229         if( observable < 0 ) return;
1230
1231         chanspec = setup->observables[ observable ].observe_insn.chanspec;
1232         adc_range = CR_RANGE( chanspec );
1233
1234         comedi_apply_parsed_calibration( setup->dev, setup->ad_subdev,
1235                 0, adc_range, 0, setup->new_calibration );
1236 }
1237
1238 static int cal_ni_generic( calibration_setup_t *setup, const ni_caldac_layout_t *layout )
1239 {
1240         comedi_calibration_setting_t *current_cal;
1241         int retval;
1242         int num_ai_ranges;
1243         int range;
1244         int ai_unipolar_lowgain, ai_bipolar_lowgain;
1245
1246         num_ai_ranges = comedi_get_n_ranges( setup->dev, setup->ad_subdev, 0 );
1247         assert( num_ai_ranges > 0 );
1248
1249         ai_bipolar_lowgain = get_bipolar_lowgain( setup->dev, setup->ad_subdev );
1250         ai_unipolar_lowgain = get_unipolar_lowgain( setup->dev, setup->ad_subdev );
1251
1252         prep_adc_caldacs_generic( setup, layout, ai_bipolar_lowgain );
1253
1254         current_cal = sc_alloc_calibration_setting( setup );
1255         current_cal->subdevice = setup->ad_subdev;
1256         reset_caldac( setup, layout->adc_gain_fine );
1257         generic_do_relative( setup, current_cal, ni_zero_offset_low,
1258                 ni_reference_low, layout->adc_gain );
1259         reset_caldac( setup, layout->adc_postgain_offset_fine );
1260         generic_do_relative( setup, current_cal, ni_zero_offset_low,
1261                 ni_zero_offset_high, layout->adc_postgain_offset );
1262         generic_do_relative( setup, current_cal, ni_zero_offset_low,
1263                 ni_zero_offset_high, layout->adc_postgain_offset_fine );
1264         reset_caldac( setup, layout->adc_pregain_offset_fine );
1265         generic_do_cal( setup, current_cal, ni_zero_offset_high, layout->adc_pregain_offset );
1266         generic_do_relative( setup, current_cal, ni_zero_offset_low,
1267                 ni_reference_low, layout->adc_gain_fine );
1268         generic_do_cal( setup, current_cal, ni_zero_offset_high,
1269                 layout->adc_pregain_offset_fine );
1270         sc_push_channel( current_cal, SC_ALL_CHANNELS );
1271         sc_push_aref( current_cal, SC_ALL_AREFS );
1272         if( layout->adc_unip_offset >= 0 )
1273         {
1274                 sc_push_range( current_cal, SC_ALL_RANGES );
1275         }else
1276         {
1277                 for( range = 0; range < num_ai_ranges; range++ )
1278                 {
1279                         if( is_bipolar( setup->dev, setup->ad_subdev, 0, range ) )
1280                                 sc_push_range( current_cal, range );
1281                 }
1282         }
1283
1284         /* do seperate unipolar calibration if appropriate */
1285         if( ai_unipolar_lowgain >= 0 )
1286         {
1287                 current_cal = sc_alloc_calibration_setting( setup );
1288                 current_cal->subdevice = setup->ad_subdev;
1289                 if( layout->adc_unip_offset >= 0 )
1290                 {
1291                         reset_caldac( setup, layout->adc_unip_offset_fine );
1292                         generic_do_cal( setup, current_cal, ni_unip_zero_offset_high,
1293                                 layout->adc_unip_offset );
1294                         generic_do_cal( setup, current_cal, ni_unip_zero_offset_high,
1295                                 layout->adc_unip_offset_fine );
1296                 /* if we don't have a unipolar offset caldac, do a fully
1297                  * independent calibration for unipolar ranges */
1298                 }else
1299                 {
1300                         prep_adc_caldacs_generic( setup, layout, ai_unipolar_lowgain );
1301                         generic_peg( setup, ni_unip_zero_offset_low,
1302                                 layout->adc_pregain_offset, 1 );
1303                         generic_peg( setup, ni_unip_zero_offset_low,
1304                                 layout->adc_postgain_offset, 1 );
1305                         generic_do_relative( setup, current_cal, ni_unip_zero_offset_low,
1306                                 ni_unip_reference_low, layout->adc_gain );
1307                         generic_do_relative( setup, current_cal, ni_unip_zero_offset_low,
1308                                 ni_unip_zero_offset_high, layout->adc_postgain_offset );
1309                         generic_do_relative( setup, current_cal, ni_unip_zero_offset_low,
1310                                 ni_unip_zero_offset_high, layout->adc_postgain_offset_fine );
1311                         generic_do_cal( setup, current_cal, ni_unip_zero_offset_high,
1312                                 layout->adc_pregain_offset );
1313                         generic_do_relative( setup, current_cal, ni_unip_zero_offset_low,
1314                                 ni_unip_reference_low, layout->adc_gain_fine );
1315                         generic_do_cal( setup, current_cal, ni_unip_zero_offset_high,
1316                                 layout->adc_pregain_offset_fine );
1317                 }
1318                 for( range = 0; range < num_ai_ranges; range++ )
1319                 {
1320                         if( is_unipolar( setup->dev, setup->ad_subdev, 0, range ) )
1321                                 sc_push_range( current_cal, range );
1322                 }
1323                 sc_push_channel( current_cal, SC_ALL_CHANNELS );
1324                 sc_push_aref( current_cal, SC_ALL_AREFS );
1325         }
1326         if( setup->da_subdev >= 0 && setup->do_output )
1327         {
1328                 unsigned int channel, range;
1329                 int ao_unipolar_lowgain = get_unipolar_lowgain( setup->dev, setup->da_subdev );
1330                 int ao_bipolar_lowgain = get_bipolar_lowgain( setup->dev, setup->da_subdev );
1331                 int num_ao_ranges;
1332
1333                 for( channel = 0; channel < 2; channel++ )
1334                 {
1335                         num_ao_ranges = comedi_get_n_ranges( setup->dev, setup->da_subdev, channel );
1336                         prep_dac_caldacs_generic( setup, layout, channel, ao_bipolar_lowgain );
1337                         prep_adc_for_dac( setup, ni_ao_reference( channel ) );
1338
1339                         current_cal = sc_alloc_calibration_setting( setup );
1340                         current_cal->subdevice = setup->da_subdev;
1341                         generic_do_linearity( setup, current_cal, ni_ao_zero_offset( channel ),
1342                                 ni_ao_mid_linearity( channel ), ni_ao_reference( channel ),
1343                                 layout->dac_linearity[ channel ] );
1344                         generic_do_cal( setup, current_cal, ni_ao_zero_offset( channel ),
1345                                 layout->dac_offset[ channel ] );
1346                         reset_caldac( setup, layout->dac_gain_fine[ channel ] );
1347                         generic_do_cal( setup, current_cal, ni_ao_reference( channel ),
1348                                 layout->dac_gain[ channel ] );
1349                         generic_do_cal( setup, current_cal, ni_ao_reference( channel ),
1350                                 layout->dac_gain_fine[ channel ] );
1351                         sc_push_channel( current_cal, channel );
1352                         for( range = 0; range < num_ao_ranges; range++ )
1353                         {
1354                                 if( is_bipolar( setup->dev, setup->da_subdev, channel, range ) )
1355                                         sc_push_range( current_cal, range );
1356                         }
1357                         sc_push_aref( current_cal, SC_ALL_AREFS );
1358
1359                         if( ao_unipolar_lowgain >= 0 )
1360                         {
1361                                 prep_dac_caldacs_generic( setup, layout, channel, ao_unipolar_lowgain );
1362
1363                                 current_cal = sc_alloc_calibration_setting( setup );
1364                                 current_cal->subdevice = setup->da_subdev;
1365                                 generic_do_linearity( setup, current_cal, ni_ao_unip_low_linearity( channel ),
1366                                         ni_ao_unip_mid_linearity( channel ), ni_ao_unip_reference( channel ),
1367                                         layout->dac_linearity[ channel ] );
1368                                 generic_do_cal( setup, current_cal, ni_ao_unip_zero_offset( channel),
1369                                         layout->dac_offset[ channel ] );
1370                                 generic_do_cal( setup, current_cal, ni_ao_unip_reference( channel ),
1371                                         layout->dac_gain[ channel ] );
1372                                 generic_do_cal( setup, current_cal, ni_ao_unip_reference( channel ),
1373                                         layout->dac_gain_fine[ channel ] );
1374                                 sc_push_channel( current_cal, channel );
1375                                 for( range = 0; range < num_ao_ranges; range++ )
1376                                 {
1377                                         if( is_unipolar( setup->dev, setup->da_subdev, channel, range ) )
1378                                                 sc_push_range( current_cal, range );
1379                                 }
1380                                 sc_push_aref( current_cal, SC_ALL_AREFS );
1381                         }
1382                 }
1383         }
1384
1385         retval = write_calibration_file( setup );
1386
1387         return retval;
1388 }
1389
1390 static double ni_get_reference( calibration_setup_t *setup, int lsb_loc,int msb_loc)
1391 {
1392         int lsb,msb;
1393         int16_t uv;
1394         double ref;
1395
1396         lsb=read_eeprom( setup, lsb_loc );
1397         msb=read_eeprom( setup, msb_loc );
1398         assert( lsb >=0 && msb >= 0 );
1399         DPRINT(0,"eeprom reference lsb=%d msb=%d\n", lsb, msb);
1400
1401         uv = ( lsb & 0xff ) | ( ( msb << 8 ) & 0xff00 );
1402         ref=5.000+1.0e-6*uv;
1403         DPRINT(0, "resulting reference voltage: %g\n", ref );
1404         if( fabs( ref - 5.0 ) > 0.005 )
1405                 DPRINT( 0, "WARNING: eeprom indicates reference is more than 5mV away\n"
1406                         "from 5V.  Possible bad eeprom address?\n" );
1407
1408         return ref;
1409 }
1410
1411 /****************
1412  NI 671x and 673x support
1413  **************/
1414
1415 static const int channels_per_ad8804 = 16;
1416
1417 static inline int ni67xx_ao_gain_caldac(unsigned int ao_channel)
1418 {
1419         int ad8804_gain_channels[4] = {8, 2, 11, 5};
1420         int caldac_channel = ad8804_gain_channels[ao_channel % 4];
1421         int caldac_index = ao_channel / 4;
1422         /* just guessing that second ad8804 is works for ao channels 4-7
1423          * the same as the first ad8804 works for ao channels 0-3 */
1424         return caldac_index * channels_per_ad8804 + caldac_channel;
1425 }
1426 static inline int ni67xx_ao_linearity_caldac(unsigned int ao_channel)
1427 {
1428         int ad8804_linearity_channels[4] = {4, 10, 1, 0};
1429         int caldac_channel = ad8804_linearity_channels[ao_channel % 4];
1430         int caldac_index = ao_channel / 4;
1431
1432         return caldac_index * channels_per_ad8804 + caldac_channel;
1433 }
1434 static inline int ni67xx_ao_offset_caldac(unsigned int ao_channel)
1435 {
1436         int ad8804_offset_channels[4] = {7, 6, 9, 3};
1437         int caldac_channel = ad8804_offset_channels[ao_channel % 4];
1438         int caldac_index = ao_channel / 4;
1439
1440         return caldac_index * channels_per_ad8804 + caldac_channel;
1441 }
1442
1443 static int ni67xx_ao_ground_observable_index( const calibration_setup_t *setup,
1444         unsigned int channel, unsigned int ao_range )
1445 {
1446         return 3 * channel + 0;
1447 }
1448
1449 static int ni67xx_ao_mid_observable_index( const calibration_setup_t *setup,
1450         unsigned int channel, unsigned int ao_range )
1451 {
1452         return 3 * channel + 1;
1453 }
1454
1455 static int ni67xx_ao_high_observable_index( const calibration_setup_t *setup,
1456         unsigned int channel, unsigned int ao_range )
1457 {
1458         return 3 * channel + 2;
1459 }
1460
1461 static const double ni67xx_unitless_adc_offset = 0.5;
1462
1463 /* determine conversion factor between actual voltage and
1464  * interval [0,1) returned by reads from the calibration adc
1465  * subdevice.
1466  */
1467 static double ni67xx_unitless_adc_slope(calibration_setup_t *setup)
1468 {
1469         double reference_in_volts;
1470         double reference_unitless;
1471         double slope;
1472         comedi_insn insn;
1473         lsampl_t data;
1474         comedi_range *range;
1475         static const int maxdata = 0x10000;
1476         int retval;
1477
1478         if(ni_board(setup)->ref_eeprom_lsb >= 0 &&
1479                 ni_board(setup)->ref_eeprom_msb >= 0)
1480         {
1481                 reference_in_volts = ni_get_reference(setup,
1482                         ni_board(setup)->ref_eeprom_lsb, ni_board(setup)->ref_eeprom_msb );
1483         }else
1484         {
1485                 DPRINT( 0, "WARNING: unknown eeprom address for reference voltage\n"
1486                         "correction.  This might be fixable if you send us an eeprom dump\n"
1487                         "(see the demo/eeprom_dump program).\n");
1488                 reference_in_volts = 5.0;
1489         }
1490
1491         memset(&insn, 0, sizeof(insn));
1492         insn.insn = INSN_READ;
1493         insn.n = 1;
1494         insn.subdev = setup->ad_subdev;
1495         insn.data = &data;
1496         insn.chanspec = CR_PACK(0, 0, AREF_GROUND) | CR_ALT_SOURCE;
1497         retval = comedi_do_insn(setup->dev, &insn);
1498         assert(retval >= 0);
1499
1500         range = comedi_get_range(setup->dev, setup->ad_subdev, 0, 0);
1501         assert( range );
1502         reference_unitless = comedi_to_phys(data, range, maxdata);
1503
1504         slope = (reference_unitless - ni67xx_unitless_adc_offset) / reference_in_volts;
1505
1506         return slope;
1507 }
1508
1509 /* calibration adc uses RANGE_UNKNOWN, so it will return a value from
1510    0.0 to 1.0 instead of a voltage, so we need to renormalize. */
1511 static void ni67xx_set_target( calibration_setup_t *setup, int obs, double target, double slope)
1512 {
1513         set_target(setup, obs, target);
1514         /* convert target from volts to interval [0,1) which calibration
1515          * adc returns */
1516         setup->observables[obs].target *= slope;
1517         setup->observables[obs].target += ni67xx_unitless_adc_offset;
1518 }
1519
1520 static void ni67xx_setup_observables( calibration_setup_t *setup )
1521 {
1522         comedi_insn tmpl, po_tmpl;
1523         observable *o;
1524         int num_ao_channels;
1525         int i;
1526         double slope;
1527
1528         slope = ni67xx_unitless_adc_slope(setup);
1529
1530         /* calibration adc is very slow (15HZ) but accurate, so only sample a few times */
1531         setup->sv_order = 1;
1532
1533         num_ao_channels = comedi_get_n_channels(setup->dev, setup->da_subdev);
1534         assert(num_ao_channels >= 0);
1535
1536         memset( &tmpl, 0, sizeof(tmpl) );
1537         tmpl.insn = INSN_READ;
1538         tmpl.n = 1;
1539         tmpl.subdev = setup->ad_subdev;
1540
1541         memset( &po_tmpl, 0, sizeof(po_tmpl) );
1542         po_tmpl.insn = INSN_WRITE;
1543         po_tmpl.n = 1;
1544         po_tmpl.subdev = setup->da_subdev;
1545
1546         setup->n_observables = 0;
1547
1548         for(i = 0; i < num_ao_channels; i++)
1549         {
1550                 o = setup->observables + ni67xx_ao_ground_observable_index( setup,
1551                         i, 0);
1552                 o->reference_source = -1;
1553                 assert( o->name == NULL );
1554                 asprintf(&o->name, "dac%i ground, ground referenced", i);
1555                 o->preobserve_insn = po_tmpl;
1556                 o->preobserve_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
1557                 o->preobserve_insn.data = o->preobserve_data;
1558                 o->observe_insn = tmpl;
1559                 o->observe_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
1560                 ni67xx_set_target(setup, ni67xx_ao_ground_observable_index(setup, i, 0), 0.0, slope);
1561                 setup->n_observables++;
1562
1563                 o = setup->observables + ni67xx_ao_mid_observable_index( setup,
1564                         i, 0);
1565                 o->reference_source = -1;
1566                 assert( o->name == NULL );
1567                 asprintf(&o->name, "dac%i mid, ground referenced", i);
1568                 o->preobserve_insn = po_tmpl;
1569                 o->preobserve_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
1570                 o->preobserve_insn.data = o->preobserve_data;
1571                 o->observe_insn = tmpl;
1572                 o->observe_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
1573                 ni67xx_set_target(setup, ni67xx_ao_mid_observable_index(setup, i, 0), 4.0, slope);
1574                 setup->n_observables++;
1575
1576                 o = setup->observables + ni67xx_ao_high_observable_index( setup, i, 0);
1577                 o->reference_source = -1;
1578                 assert( o->name == NULL );
1579                 asprintf(&o->name, "dac%i high, ground referenced", i);
1580                 o->preobserve_insn = po_tmpl;
1581                 o->preobserve_insn.chanspec = CR_PACK( i, 0, AREF_GROUND );
1582                 o->preobserve_insn.data = o->preobserve_data;
1583                 o->observe_insn = tmpl;
1584                 o->observe_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
1585                 ni67xx_set_target(setup, ni67xx_ao_high_observable_index(setup, i, 0), 8.0, slope);
1586                 setup->n_observables++;
1587         }
1588
1589         return;
1590 }
1591
1592 static int cal_ni_pci_6711(calibration_setup_t *setup)
1593 {
1594         generic_layout_t layout;
1595
1596         init_generic_layout( &layout );
1597         layout.dac_gain = ni67xx_ao_gain_caldac;
1598         layout.dac_linearity = ni67xx_ao_linearity_caldac;
1599         layout.dac_offset = ni67xx_ao_offset_caldac;
1600         layout.dac_high_observable = ni67xx_ao_high_observable_index;
1601         layout.dac_mid_observable = ni67xx_ao_mid_observable_index;
1602         layout.dac_ground_observable = ni67xx_ao_ground_observable_index;
1603         layout.dac_fractional_tolerance = get_tolerance( setup, setup->da_subdev, 1.0 );
1604         return generic_cal_ao(setup, &layout);
1605 }
1606