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