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