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