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