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