f5ae51bca83b4e1574dcf29c5a780f178d15e1c1
[comedilib.git] / comedi_calibrate / ni.c
1 /*
2    A little auto-calibration utility, for boards
3    that support it.
4
5    Right now, it only supports NI E series boards,
6    but it should be easily portable.
7
8    A few things need improvement here:
9     - current system gets "close", but doesn't
10       do any fine-tuning
11     - no pre/post gain discrimination for the
12       A/D zero offset.
13     - should read (and use) the actual reference
14       voltage value from eeprom
15     - statistics would be nice, to show how good
16       the calibration is.
17     - doesn't check unipolar ranges
18     - "alternate calibrations" would be cool--to
19       accurately measure 0 in a unipolar range
20     - more portable
21  */
22
23 #define _GNU_SOURCE
24
25 #include <stdio.h>
26 #include <comedilib.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <getopt.h>
31 #include <ctype.h>
32 #include <math.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include "calib.h"
37
38
39 struct board_struct{
40         char *name;
41         int status;
42         void (*cal)(void);
43 };
44
45 void ni_setup_board(void);
46 void ni_setup_observables(void);
47
48 void cal_ni_16e_1(void);
49 void cal_ni_daqcard_ai_16xe_50(void);
50 void cal_ni_6035e(void);
51 void cal_ni_6071e(void);
52 void cal_ni_16e_10(void);
53 void cal_ni_16xe_50(void);
54
55 struct board_struct boards[]={
56         { "at-mio-16e-2",       STATUS_DONE,    cal_ni_16e_1 },
57         { "DAQCard-ai-16xe-50", STATUS_DONE,    cal_ni_daqcard_ai_16xe_50 },
58         { "at-mio-16e-1",       STATUS_SOME,    cal_ni_16e_1 },
59         { "pci-mio-16e-1",      STATUS_SOME,    cal_ni_16e_1 },
60         { "pci-6035e",          STATUS_GUESS,   cal_ni_6035e },
61         { "pci-6071e",          STATUS_GUESS,   cal_ni_6071e },
62         { "pxi-6071e",          STATUS_GUESS,   cal_ni_6071e },
63         { "at-mio-16e-10",      STATUS_GUESS,   cal_ni_16e_10 },
64         { "pci-mio-16xe-50",    STATUS_GUESS,   cal_ni_16xe_50 },
65         { "pci-6023e",          STATUS_GUESS,   cal_ni_6035e },
66 #if 0
67 //      { "at-mio-16de-10",     cal_ni_unknown },
68         { "at-mio-64e-3",       cal_ni_16e_1 },
69 //      { "at-mio-16xe-50",     cal_ni_unknown },
70 //      { "at-mio-16xe-10",     cal_ni_unknown },
71 //      { "at-ai-16xe-10",      cal_ni_unknown },
72         { "pci-mio-16xe-10",    cal_ni_16xe_10 },
73 //      { "pxi-6030e",          cal_ni_unknown },
74 //      { "pci-mio-16e-4",      cal_ni_unknown },
75 //      { "pxi-6040e",          cal_ni_unknown },
76 //      { "pci-6031e",          cal_ni_unknown },
77 //      { "pci-6032e",          cal_ni_unknown },
78 //      { "pci-6033e",          cal_ni_unknown },
79 //      { "pci-6071e",          cal_ni_unknown },
80         { "pci-6024e",          cal_ni_6023e }, // guess
81         { "pci-6025e",          cal_ni_6023e }, // guess
82         { "pxi-6025e",          cal_ni_6023e }, // guess
83         { "pci-6034e",          cal_ni_6023e }, // guess
84         { "pci-6035e",          cal_ni_6023e },
85 //      { "pci-6052e",          cal_ni_unknown },
86 //      { "pci-6110e",          cal_ni_unknown },
87 //      { "pci-6111e",          cal_ni_unknown },
88 //      { "pci-6711",           cal_ni_unknown },
89 //      { "pci-6713",           cal_ni_unknown },
90 //      { "pxi-6070e",          cal_ni_unknown },
91 //      { "pxi-6052e",          cal_ni_unknown },
92 //      { "DAQCard-ai-16e-4",   cal_ni_unknown },
93 //      { "DAQCard-6062e",      cal_ni_unknown },
94 //      { "DAQCard-6024e",      cal_ni_unknown },
95 #endif
96 };
97 #define n_boards (sizeof(boards)/sizeof(boards[0]))
98
99 enum {
100         ni_zero_offset_low = 0,
101         ni_zero_offset_high,
102         ni_reference_low,
103         ni_unip_offset_low,
104         ni_ao0_zero_offset,
105         ni_ao0_reference,
106         ni_ao1_zero_offset,
107         ni_ao1_reference,
108 };
109
110 void ni_setup(void)
111 {
112         ni_setup_board();
113         ni_setup_observables();
114         setup_caldacs();
115 }
116
117 void ni_setup_board(void)
118 {
119         int i;
120
121         for(i=0;i<n_boards;i++){
122                 if(!strcmp(devicename,boards[i].name)){
123                         device_status = boards[i].status;
124                         do_cal = boards[i].cal;
125                         return;
126                 }
127         }
128
129 }
130
131 void ni_setup_observables(void)
132 {
133         comedi_insn tmpl;
134         int bipolar_lowgain;
135         int bipolar_highgain;
136         int unipolar_lowgain;
137         double voltage_reference;
138         observable *o;
139
140         bipolar_lowgain = get_bipolar_lowgain(dev,ad_subdev);
141         bipolar_highgain = get_bipolar_highgain(dev,ad_subdev);
142         unipolar_lowgain = get_unipolar_lowgain(dev,ad_subdev);
143
144         voltage_reference = 5.000;
145
146         memset(&tmpl,0,sizeof(tmpl));
147         tmpl.insn = INSN_READ;
148         tmpl.n = 1;
149         tmpl.subdev = ad_subdev;
150
151         /* 0 offset, low gain */
152         o = observables + ni_zero_offset_low;
153         o->name = "ai, bipolar zero offset, low gain";
154         o->observe_insn = tmpl;
155         o->observe_insn.chanspec = CR_PACK(0,bipolar_lowgain,AREF_OTHER);
156         o->target = 0;
157
158         /* 0 offset, high gain */
159         o = observables + ni_zero_offset_high;
160         o->name = "ai, bipolar zero offset, high gain";
161         o->observe_insn = tmpl;
162         o->observe_insn.chanspec = CR_PACK(0,bipolar_highgain,AREF_OTHER);
163         o->target = 0;
164
165         /* voltage reference */
166         o = observables + ni_reference_low;
167         o->name = "ai, bipolar voltage reference, low gain";
168         o->observe_insn = tmpl;
169         o->observe_insn.chanspec = CR_PACK(5,bipolar_lowgain,AREF_OTHER);
170         o->target = voltage_reference;
171
172         if(unipolar_lowgain>=0){
173                 /* unip/bip offset */
174                 o = observables + ni_unip_offset_low;
175                 o->name = "ai, unipolar zero offset, low gain";
176                 o->observe_insn = tmpl;
177                 o->observe_insn.chanspec =
178                         CR_PACK(0,unipolar_lowgain,AREF_OTHER);
179                 o->target = 0;
180
181 #if 0
182                 /* unip gain */
183                 o = observables + ni_unip_reference_low;
184                 o->name = "ai, unipolar voltage reference, low gain";
185                 o->observe_insn = tmpl;
186                 o->observe_insn.chanspec =
187                         CR_PACK(5,unipolar_lowgain,AREF_OTHER);
188                 o->target = voltage_reference;
189                 i++;
190 #endif
191         }
192
193         if(da_subdev>=0){
194                 comedi_insn po_tmpl;
195
196                 memset(&po_tmpl,0,sizeof(po_tmpl));
197                 po_tmpl.insn = INSN_WRITE;
198                 po_tmpl.n = 1;
199                 po_tmpl.subdev = da_subdev;
200
201                 /* ao 0, zero offset */
202                 o = observables + ni_ao0_zero_offset;
203                 o->name = "ao 0, zero offset, low gain";
204                 o->preobserve_insn = po_tmpl;
205                 o->preobserve_insn.chanspec = CR_PACK(0,0,0);
206                 o->preobserve_insn.data = &o->preobserve_data;
207                 o->observe_insn = tmpl;
208                 o->observe_insn.chanspec =
209                         CR_PACK(2,bipolar_lowgain,AREF_OTHER);
210                 set_target(ni_ao0_zero_offset,0.0);
211
212                 /* ao 0, gain */
213                 o = observables + ni_ao0_reference;
214                 o->name = "ao 0, reference voltage, low gain";
215                 o->preobserve_insn = po_tmpl;
216                 o->preobserve_insn.chanspec = CR_PACK(0,0,0);
217                 o->preobserve_insn.data = &o->preobserve_data;
218                 o->observe_insn = tmpl;
219                 o->observe_insn.chanspec =
220                         CR_PACK(6,bipolar_lowgain,AREF_OTHER);
221                 set_target(ni_ao0_reference,5.0);
222                 o->target -= voltage_reference;
223
224                 /* ao 1, zero offset */
225                 o = observables + ni_ao1_zero_offset;
226                 o->name = "ao 1, zero offset, low gain";
227                 o->preobserve_insn = po_tmpl;
228                 o->preobserve_insn.chanspec = CR_PACK(1,0,0);
229                 o->preobserve_insn.data = &o->preobserve_data;
230                 o->observe_insn = tmpl;
231                 o->observe_insn.chanspec =
232                         CR_PACK(3,bipolar_lowgain,AREF_OTHER);
233                 set_target(ni_ao1_zero_offset,0.0);
234
235                 /* ao 1, gain */
236                 o = observables + ni_ao1_reference;
237                 o->name = "ao 1, reference voltage, low gain";
238                 o->preobserve_insn = po_tmpl;
239                 o->preobserve_insn.chanspec = CR_PACK(1,0,0);
240                 o->preobserve_insn.data = &o->preobserve_data;
241                 o->observe_insn = tmpl;
242                 o->observe_insn.chanspec =
243                         CR_PACK(7,bipolar_lowgain,AREF_OTHER);
244                 set_target(ni_ao1_reference,5.0);
245                 o->target -= voltage_reference;
246
247         }
248         n_observables = ni_ao1_reference + 1;
249 }
250
251 void cal_ni_daqcard_ai_16xe_50(void)
252 {
253         // daqcard
254         postgain_cal(ni_zero_offset_low,ni_zero_offset_high,2);
255         cal1(ni_zero_offset_high,8);
256         cal1(ni_reference_low,0);
257 }
258
259 void cal_ni_16e_1(void)
260 {
261         // 16e-2
262         postgain_cal(ni_zero_offset_low,ni_zero_offset_high,1);
263         cal1(ni_zero_offset_high,0);
264         cal1(ni_reference_low,3);
265         cal1(ni_unip_offset_low,2);
266         if(do_output){
267                 cal1(ni_ao0_zero_offset,5);
268                 cal1(ni_ao0_reference,6);
269                 cal1(ni_ao1_zero_offset,8);
270                 cal1(ni_ao1_reference,9);
271         }
272 }
273
274 void cal_ni_16e_10(void)
275 {
276         // 16e-10 (old)
277         postgain_cal(ni_zero_offset_low,ni_zero_offset_high,1);
278         cal1(ni_zero_offset_high,10);
279         cal1(ni_zero_offset_high,0);
280         cal1(ni_reference_low,3);
281         cal1(ni_unip_offset_low,2);
282         if(do_output){
283                 cal1(ni_ao0_zero_offset,5); // guess
284                 cal1(ni_ao0_reference,6); // guess
285                 cal1(ni_ao0_zero_offset,8); // guess
286                 cal1(ni_ao0_reference,9); // guess
287         }
288 }
289
290 void cal_ni_16xe_50(void)
291 {
292         // 16xe-50 (old) (same as daqcard?)
293         postgain_cal(ni_zero_offset_low,ni_zero_offset_high,2);
294         cal1(ni_zero_offset_high,8);
295         cal1(ni_reference_low,0);
296         if(do_output){
297                 // unknown
298         }
299 }
300
301 void cal_ni_6035e(void)
302 {
303         // 6035e (old)
304         postgain_cal(ni_zero_offset_low,ni_zero_offset_high,1);
305         cal1(ni_zero_offset_high,0);
306         cal1(ni_reference_low,3);
307         if(do_output){
308                 // unknown
309         }
310 }
311
312 void cal_ni_6071e(void)
313 {
314         // 6071e (old)
315         postgain_cal(ni_zero_offset_low,ni_zero_offset_high,1);
316         cal1(ni_zero_offset_high,0);
317         cal1(ni_reference_low,3);
318         if(do_output){
319                 // unknown
320         }
321 }
322
323
324 double ni_get_reference(int lsb_loc,int msb_loc)
325 {
326         int lsb,msb;
327         int uv;
328         double ref;
329
330         lsb=read_eeprom(lsb_loc);
331         msb=read_eeprom(msb_loc);
332         printf("lsb=%d msb=%d\n",read_eeprom(425),read_eeprom(426));
333
334         uv=lsb | (msb<<8);
335         if(uv>=0x8000)uv-=0x10000;
336         ref=5.000+1.0e-6*uv;
337         printf("ref=%g\n",ref);
338
339         return ref;
340 }
341
342 void cal_ni_unknown(void)
343 {
344         comedi_range *range;
345         int bipolar_lowgain;
346         int bipolar_highgain;
347         int unipolar_lowgain;
348         int have_ao = 1;
349
350         reset_caldacs();
351         printf("Warning: device not calibrated due to insufficient information\n");
352         printf("Please send this output to <ds@schleef.org>\n");
353         printf("$Id$\n");
354         printf("Device name: %s\n",comedi_get_board_name(dev));
355         printf("Comedi version: %d.%d.%d\n",
356                 (comedi_get_version_code(dev)>>16)&0xff,
357                 (comedi_get_version_code(dev)>>8)&0xff,
358                 (comedi_get_version_code(dev))&0xff);
359
360         bipolar_lowgain = get_bipolar_lowgain(dev,ad_subdev);
361         bipolar_highgain = get_bipolar_highgain(dev,ad_subdev);
362         unipolar_lowgain = get_unipolar_lowgain(dev,ad_subdev);
363
364         /* 0 offset, low gain */
365         range = comedi_get_range(dev,ad_subdev,0,bipolar_lowgain);
366         DPRINT(0,"bipolar zero offset, low gain [%g,%g]\n",
367                 range->min,range->max);
368         channel_dependence(0,bipolar_lowgain);
369
370         /* 0 offset, high gain */
371         range = comedi_get_range(dev,ad_subdev,0,bipolar_highgain);
372         DPRINT(0,"bipolar zero offset, high gain [%g,%g]\n",
373                 range->min,range->max);
374         channel_dependence(0,bipolar_highgain);
375
376         /* unip/bip offset */
377         range = comedi_get_range(dev,ad_subdev,0,unipolar_lowgain);
378         DPRINT(0,"unipolar zero offset, low gain [%g,%g]\n",
379                 range->min,range->max);
380         channel_dependence(0,unipolar_lowgain);
381
382         /* voltage reference */
383         range = comedi_get_range(dev,ad_subdev,0,bipolar_lowgain);
384         DPRINT(0,"bipolar voltage reference, low gain [%g,%g]\n",
385                 range->min,range->max);
386         channel_dependence(5,bipolar_lowgain);
387
388         have_ao = (comedi_get_subdevice_type(dev,da_subdev)==COMEDI_SUBD_AO);
389         if(have_ao){
390                 int ao_chan;
391
392                 /* ao 0, zero offset */
393                 ao_chan = 0;
394                 set_ao(dev,da_subdev,ao_chan,0,0.0);
395                 range = comedi_get_range(dev,ad_subdev,0,bipolar_lowgain);
396                 DPRINT(0,"ao 0, zero offset, low gain [%g,%g]\n",
397                         range->min,range->max);
398                 channel_dependence(2,bipolar_lowgain);
399
400                 /* ao 0, gain */
401                 ao_chan = 0;
402                 set_ao(dev,da_subdev,ao_chan,0,5.0);
403                 range = comedi_get_range(dev,ad_subdev,0,bipolar_lowgain);
404                 DPRINT(0,"ao 0, gain, low gain [%g,%g]\n",
405                         range->min,range->max);
406                 channel_dependence(6,bipolar_lowgain);
407
408                 /* ao 1, zero offset */
409                 ao_chan = 1;
410                 set_ao(dev,da_subdev,ao_chan,0,0.0);
411                 range = comedi_get_range(dev,ad_subdev,0,bipolar_lowgain);
412                 DPRINT(0,"ao 1, zero offset, low gain [%g,%g]\n",
413                         range->min,range->max);
414                 channel_dependence(3,bipolar_lowgain);
415
416                 /* ao 1, gain */
417                 ao_chan = 1;
418                 set_ao(dev,da_subdev,ao_chan,0,5.0);
419                 range = comedi_get_range(dev,ad_subdev,0,bipolar_lowgain);
420                 DPRINT(0,"ao 1, gain, low gain [%g,%g]\n",
421                         range->min,range->max);
422                 channel_dependence(7,bipolar_lowgain);
423         }
424
425         cal_ni_results();
426 }
427
428 void cal_ni_results(void)
429 {
430         comedi_range *range;
431         int bipolar_lowgain;
432         int bipolar_highgain;
433         int unipolar_lowgain;
434         //int have_ao;
435         char s[32];
436
437         bipolar_lowgain = get_bipolar_lowgain(dev,ad_subdev);
438         bipolar_highgain = get_bipolar_highgain(dev,ad_subdev);
439         unipolar_lowgain = get_unipolar_lowgain(dev,ad_subdev);
440
441         /* 0 offset, low gain */
442         range = comedi_get_range(dev,ad_subdev,0,bipolar_lowgain);
443         read_chan2(s,0,bipolar_lowgain);
444         DPRINT(0,"bipolar zero offset, low gain [%g,%g]: %s\n",
445                 range->min,range->max,s);
446
447         /* 0 offset, high gain */
448         range = comedi_get_range(dev,ad_subdev,0,bipolar_highgain);
449         read_chan2(s,0,bipolar_highgain);
450         DPRINT(0,"bipolar zero offset, high gain [%g,%g]: %s\n",
451                 range->min,range->max,s);
452
453         /* unip/bip offset */
454         range = comedi_get_range(dev,ad_subdev,0,unipolar_lowgain);
455         read_chan2(s,0,unipolar_lowgain);
456         DPRINT(0,"unipolar zero offset, low gain [%g,%g]: %s\n",
457                 range->min,range->max,s);
458
459 }
460
461 #if 0
462 void ni_mio_ai_postgain_cal(void)
463 {
464         linear_fit_t l;
465         double offset_r0;
466         double offset_r7;
467         double gain;
468         double a;
469
470         check_gain_chan_x(&l,CR_PACK(0,0,AREF_OTHER),1);
471         offset_r0=linear_fit_func_y(&l,caldacs[1].current);
472         printf("offset r0 %g\n",offset_r0);
473
474         check_gain_chan_x(&l,CR_PACK(0,7,AREF_OTHER),1);
475         offset_r7=linear_fit_func_y(&l,caldacs[1].current);
476         printf("offset r7 %g\n",offset_r7);
477
478         gain=l.slope;
479         
480         a=(offset_r0-offset_r7)/(200.0-1.0);
481         a=caldacs[1].current-a/gain;
482
483         printf("%g\n",a);
484
485         caldacs[1].current=rint(a);
486         update_caldac(1);
487 }
488
489 void ni_mio_ai_postgain_cal_2(int chan,int dac,int range_lo,int range_hi,double gain)
490 {
491         double offset_lo,offset_hi;
492         linear_fit_t l;
493         double slope;
494         double a;
495
496         check_gain_chan_x(&l,CR_PACK(chan,range_lo,AREF_OTHER),dac);
497         offset_lo=linear_fit_func_y(&l,caldacs[dac].current);
498         printf("offset lo %g\n",offset_lo);
499
500         check_gain_chan_x(&l,CR_PACK(chan,range_hi,AREF_OTHER),dac);
501         offset_hi=linear_fit_func_y(&l,caldacs[dac].current);
502         printf("offset hi %g\n",offset_hi);
503
504         slope=l.slope;
505         
506         a=(offset_lo-offset_hi)/(gain-1.0);
507         a=caldacs[dac].current-a/slope;
508
509         printf("%g\n",a);
510
511         caldacs[dac].current=rint(a);
512         update_caldac(dac);
513 }
514 #endif
515