d73f94ca9eecd39f767541b418bad585edad25fa
[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_at_mio_16e_2(void);
49 void cal_ni_daqcard_ai_16xe_50(void);
50 void cal_ni_at_mio_16e_1(void);
51 void cal_ni_pci_mio_16e_1(void);
52 void cal_ni_pci_6035e(void);
53 void cal_ni_pci_6071e(void);
54 void cal_ni_pxi_6071e(void);
55 void cal_ni_at_mio_16e_10(void);
56 void cal_ni_pci_mio_16xe_50(void);
57 void cal_ni_pci_6023e(void);
58
59 struct board_struct boards[]={
60         { "at-mio-16e-2",       STATUS_DONE,    cal_ni_at_mio_16e_2 },
61         { "DAQCard-ai-16xe-50", STATUS_DONE,    cal_ni_daqcard_ai_16xe_50 },
62         { "at-mio-16e-1",       STATUS_SOME,    cal_ni_at_mio_16e_1 },
63         { "pci-mio-16e-1",      STATUS_SOME,    cal_ni_pci_mio_16e_1 },
64         { "pci-6035e",          STATUS_GUESS,   cal_ni_pci_6035e },
65         { "pci-6071e",          STATUS_GUESS,   cal_ni_pci_6071e },
66         { "pxi-6071e",          STATUS_GUESS,   cal_ni_pxi_6071e },
67         { "at-mio-16e-10",      STATUS_GUESS,   cal_ni_at_mio_16e_10 },
68         { "pci-mio-16xe-50",    STATUS_GUESS,   cal_ni_pci_mio_16xe_50 },
69         { "pci-6023e",          STATUS_GUESS,   cal_ni_pci_6023e },
70 #if 0
71 //      { "at-mio-16de-10",     cal_ni_unknown },
72         { "at-mio-64e-3",       cal_ni_16e_1 },
73 //      { "at-mio-16xe-50",     cal_ni_unknown },
74 //      { "at-mio-16xe-10",     cal_ni_unknown },
75 //      { "at-ai-16xe-10",      cal_ni_unknown },
76         { "pci-mio-16xe-10",    cal_ni_16xe_10 },
77 //      { "pxi-6030e",          cal_ni_unknown },
78 //      { "pci-mio-16e-4",      cal_ni_unknown },
79 //      { "pxi-6040e",          cal_ni_unknown },
80 //      { "pci-6031e",          cal_ni_unknown },
81 //      { "pci-6032e",          cal_ni_unknown },
82 //      { "pci-6033e",          cal_ni_unknown },
83 //      { "pci-6071e",          cal_ni_unknown },
84         { "pci-6024e",          cal_ni_6023e }, // guess
85         { "pci-6025e",          cal_ni_6023e }, // guess
86         { "pxi-6025e",          cal_ni_6023e }, // guess
87         { "pci-6034e",          cal_ni_6023e }, // guess
88         { "pci-6035e",          cal_ni_6023e },
89 //      { "pci-6052e",          cal_ni_unknown },
90 //      { "pci-6110e",          cal_ni_unknown },
91 //      { "pci-6111e",          cal_ni_unknown },
92 //      { "pci-6711",           cal_ni_unknown },
93 //      { "pci-6713",           cal_ni_unknown },
94 //      { "pxi-6070e",          cal_ni_unknown },
95 //      { "pxi-6052e",          cal_ni_unknown },
96 //      { "DAQCard-ai-16e-4",   cal_ni_unknown },
97 //      { "DAQCard-6062e",      cal_ni_unknown },
98 //      { "DAQCard-6024e",      cal_ni_unknown },
99 #endif
100 };
101 #define n_boards (sizeof(boards)/sizeof(boards[0]))
102
103 enum {
104         ni_zero_offset_low = 0,
105         ni_zero_offset_high,
106         ni_reference_low,
107         ni_unip_offset_low,
108         ni_ao0_zero_offset,
109         ni_ao0_reference,
110         ni_ao1_zero_offset,
111         ni_ao1_reference,
112 };
113
114 void ni_setup(void)
115 {
116         ni_setup_board();
117         ni_setup_observables();
118         setup_caldacs();
119 }
120
121 void ni_setup_board(void)
122 {
123         int i;
124
125         for(i=0;i<n_boards;i++){
126                 if(!strcmp(devicename,boards[i].name)){
127                         device_status = boards[i].status;
128                         do_cal = boards[i].cal;
129                         return;
130                 }
131         }
132
133 }
134
135 void ni_setup_observables(void)
136 {
137         comedi_insn tmpl;
138         int bipolar_lowgain;
139         int bipolar_highgain;
140         int unipolar_lowgain;
141         double voltage_reference;
142         observable *o;
143
144         bipolar_lowgain = get_bipolar_lowgain(dev,ad_subdev);
145         bipolar_highgain = get_bipolar_highgain(dev,ad_subdev);
146         unipolar_lowgain = get_unipolar_lowgain(dev,ad_subdev);
147
148         voltage_reference = 5.000;
149
150         memset(&tmpl,0,sizeof(tmpl));
151         tmpl.insn = INSN_READ;
152         tmpl.n = 1;
153         tmpl.subdev = ad_subdev;
154
155         /* 0 offset, low gain */
156         o = observables + ni_zero_offset_low;
157         o->name = "ai, bipolar zero offset, low gain";
158         o->observe_insn = tmpl;
159         o->observe_insn.chanspec = CR_PACK(0,bipolar_lowgain,AREF_OTHER);
160         o->target = 0;
161
162         /* 0 offset, high gain */
163         o = observables + ni_zero_offset_high;
164         o->name = "ai, bipolar zero offset, high gain";
165         o->observe_insn = tmpl;
166         o->observe_insn.chanspec = CR_PACK(0,bipolar_highgain,AREF_OTHER);
167         o->target = 0;
168
169         /* voltage reference */
170         o = observables + ni_reference_low;
171         o->name = "ai, bipolar voltage reference, low gain";
172         o->observe_insn = tmpl;
173         o->observe_insn.chanspec = CR_PACK(5,bipolar_lowgain,AREF_OTHER);
174         o->target = voltage_reference;
175
176         n_observables = ni_reference_low + 1;
177
178         if(unipolar_lowgain>=0){
179                 /* unip/bip offset */
180                 o = observables + ni_unip_offset_low;
181                 o->name = "ai, unipolar zero offset, low gain";
182                 o->observe_insn = tmpl;
183                 o->observe_insn.chanspec =
184                         CR_PACK(0,unipolar_lowgain,AREF_OTHER);
185                 o->target = 0;
186
187 #if 0
188                 /* unip gain */
189                 o = observables + ni_unip_reference_low;
190                 o->name = "ai, unipolar voltage reference, low gain";
191                 o->observe_insn = tmpl;
192                 o->observe_insn.chanspec =
193                         CR_PACK(5,unipolar_lowgain,AREF_OTHER);
194                 o->target = voltage_reference;
195                 i++;
196 #endif
197                 n_observables = ni_unip_offset_low + 1;
198         }
199
200         if(da_subdev>=0){
201                 comedi_insn po_tmpl;
202
203                 memset(&po_tmpl,0,sizeof(po_tmpl));
204                 po_tmpl.insn = INSN_WRITE;
205                 po_tmpl.n = 1;
206                 po_tmpl.subdev = da_subdev;
207
208                 /* ao 0, zero offset */
209                 o = observables + ni_ao0_zero_offset;
210                 o->name = "ao 0, zero offset, low gain";
211                 o->preobserve_insn = po_tmpl;
212                 o->preobserve_insn.chanspec = CR_PACK(0,0,0);
213                 o->preobserve_insn.data = &o->preobserve_data;
214                 o->observe_insn = tmpl;
215                 o->observe_insn.chanspec =
216                         CR_PACK(2,bipolar_lowgain,AREF_OTHER);
217                 set_target(ni_ao0_zero_offset,0.0);
218
219                 /* ao 0, gain */
220                 o = observables + ni_ao0_reference;
221                 o->name = "ao 0, reference voltage, low gain";
222                 o->preobserve_insn = po_tmpl;
223                 o->preobserve_insn.chanspec = CR_PACK(0,0,0);
224                 o->preobserve_insn.data = &o->preobserve_data;
225                 o->observe_insn = tmpl;
226                 o->observe_insn.chanspec =
227                         CR_PACK(6,bipolar_lowgain,AREF_OTHER);
228                 set_target(ni_ao0_reference,5.0);
229                 o->target -= voltage_reference;
230
231                 /* ao 1, zero offset */
232                 o = observables + ni_ao1_zero_offset;
233                 o->name = "ao 1, zero offset, low gain";
234                 o->preobserve_insn = po_tmpl;
235                 o->preobserve_insn.chanspec = CR_PACK(1,0,0);
236                 o->preobserve_insn.data = &o->preobserve_data;
237                 o->observe_insn = tmpl;
238                 o->observe_insn.chanspec =
239                         CR_PACK(3,bipolar_lowgain,AREF_OTHER);
240                 set_target(ni_ao1_zero_offset,0.0);
241
242                 /* ao 1, gain */
243                 o = observables + ni_ao1_reference;
244                 o->name = "ao 1, reference voltage, low gain";
245                 o->preobserve_insn = po_tmpl;
246                 o->preobserve_insn.chanspec = CR_PACK(1,0,0);
247                 o->preobserve_insn.data = &o->preobserve_data;
248                 o->observe_insn = tmpl;
249                 o->observe_insn.chanspec =
250                         CR_PACK(7,bipolar_lowgain,AREF_OTHER);
251                 set_target(ni_ao1_reference,5.0);
252                 o->target -= voltage_reference;
253
254                 n_observables = ni_ao1_reference + 1;
255         }
256 }
257
258 void cal_ni_at_mio_16e_2(void)
259 {
260         postgain_cal(ni_zero_offset_low,ni_zero_offset_high,1);
261         cal1(ni_zero_offset_high,0);
262         cal1(ni_reference_low,3);
263         cal1(ni_unip_offset_low,2);
264         if(do_output){
265                 cal1(ni_ao0_zero_offset,5);
266                 cal1(ni_ao0_reference,6);
267                 cal1(ni_ao1_zero_offset,8);
268                 cal1(ni_ao1_reference,9);
269         }
270 }
271
272 /*
273  * Device name: DAQCard-ai-16xe-50
274  * Comedi version: 0.7.60
275  * ai, bipolar zero offset, low gain
276  * offset 5.87(63)e-3, target 0
277  * caldac[0] gain=-2.243(21)e-6 V/bit S_min=208.079 dof=254
278  * caldac[2] gain=1.56378(22)e-4 V/bit S_min=1782.91 dof=254
279  * caldac[8] gain=2.499(14)e-7 V/bit S_min=234.915 dof=254
280  * ai, bipolar zero offset, high gain
281  * offset 4.251(49)e-5, target 0
282  * caldac[0] gain=-2.396(30)e-8 V/bit S_min=231.387 dof=254
283  * caldac[2] gain=1.56428(28)e-6 V/bit S_min=829.096 dof=254
284  * caldac[8] gain=2.61244(18)e-7 V/bit S_min=773.092 dof=254
285  * ai, bipolar voltage reference, low gain
286  * offset 4.99650(81), target 5
287  * caldac[0] gain=-3.78250(23)e-4 V/bit S_min=12207.6 dof=254
288  * caldac[1] gain=-9.878(22)e-6 V/bit S_min=346.795 dof=254
289  * caldac[2] gain=1.57172(23)e-4 V/bit S_min=969.526 dof=254
290  * caldac[8] gain=2.795(14)e-7 V/bit S_min=245.703 dof=254
291  * ai, unipolar zero offset, low gain
292  * offset 0.0133(14), target 0
293  * caldac[0] gain=3.73923(29)e-4 V/bit S_min=2855.79 dof=151
294  * caldac[1] gain=9.784(11)e-6 V/bit S_min=727.295 dof=254
295  * caldac[2] gain=7.8670(11)e-5 V/bit S_min=903.291 dof=254
296  * caldac[8] gain=2.7732(74)e-7 V/bit S_min=415.399 dof=254
297  */
298 void cal_ni_daqcard_ai_16xe_50(void)
299 {
300         postgain_cal(ni_zero_offset_low,ni_zero_offset_high,2);
301         cal1(ni_zero_offset_high,8);
302         cal1(ni_reference_low,0);
303 }
304
305 void cal_ni_at_mio_16e_1(void)
306 {
307         cal_ni_at_mio_16e_2();
308 }
309
310 void cal_ni_pci_mio_16e_1(void)
311 {
312         cal_ni_at_mio_16e_2();
313 }
314
315 void cal_ni_pci_6035e(void)
316 {
317         // 6035e (old)
318         postgain_cal(ni_zero_offset_low,ni_zero_offset_high,1);
319         cal1(ni_zero_offset_high,0);
320         cal1(ni_reference_low,3);
321         if(do_output){
322                 // unknown
323         }
324 }
325
326 void cal_ni_pci_6071e(void)
327 {
328         // 6071e (old)
329         postgain_cal(ni_zero_offset_low,ni_zero_offset_high,1);
330         cal1(ni_zero_offset_high,0);
331         cal1(ni_reference_low,3);
332         if(do_output){
333                 // unknown
334         }
335 }
336
337 void cal_ni_pxi_6071e(void)
338 {
339         // 6071e (old)
340         postgain_cal(ni_zero_offset_low,ni_zero_offset_high,1);
341         cal1(ni_zero_offset_high,0);
342         cal1(ni_reference_low,3);
343         if(do_output){
344                 // unknown
345         }
346 }
347
348 void cal_ni_at_mio_16e_10(void)
349 {
350         // 16e-10 (old)
351         postgain_cal(ni_zero_offset_low,ni_zero_offset_high,1);
352         cal1(ni_zero_offset_high,10);
353         cal1(ni_zero_offset_high,0);
354         cal1(ni_reference_low,3);
355         cal1(ni_unip_offset_low,2);
356         if(do_output){
357                 cal1(ni_ao0_zero_offset,5); // guess
358                 cal1(ni_ao0_reference,6); // guess
359                 cal1(ni_ao0_zero_offset,8); // guess
360                 cal1(ni_ao0_reference,9); // guess
361         }
362 }
363
364 void cal_ni_pci_mio_16xe_50(void)
365 {
366         // 16xe-50 (old) (same as daqcard?)
367         postgain_cal(ni_zero_offset_low,ni_zero_offset_high,2);
368         cal1(ni_zero_offset_high,8);
369         cal1(ni_reference_low,0);
370         if(do_output){
371                 // unknown
372         }
373 }
374
375 void cal_ni_pci_6023e(void)
376 {
377         cal_ni_pci_6035e();
378 }
379
380 double ni_get_reference(int lsb_loc,int msb_loc)
381 {
382         int lsb,msb;
383         int uv;
384         double ref;
385
386         lsb=read_eeprom(lsb_loc);
387         msb=read_eeprom(msb_loc);
388         printf("lsb=%d msb=%d\n",read_eeprom(425),read_eeprom(426));
389
390         uv=lsb | (msb<<8);
391         if(uv>=0x8000)uv-=0x10000;
392         ref=5.000+1.0e-6*uv;
393         printf("ref=%g\n",ref);
394
395         return ref;
396 }
397
398 #if 0
399 void cal_ni_results(void)
400 {
401         comedi_range *range;
402         int bipolar_lowgain;
403         int bipolar_highgain;
404         int unipolar_lowgain;
405         //int have_ao;
406         char s[32];
407
408         bipolar_lowgain = get_bipolar_lowgain(dev,ad_subdev);
409         bipolar_highgain = get_bipolar_highgain(dev,ad_subdev);
410         unipolar_lowgain = get_unipolar_lowgain(dev,ad_subdev);
411
412         /* 0 offset, low gain */
413         range = comedi_get_range(dev,ad_subdev,0,bipolar_lowgain);
414         read_chan2(s,0,bipolar_lowgain);
415         DPRINT(0,"bipolar zero offset, low gain [%g,%g]: %s\n",
416                 range->min,range->max,s);
417
418         /* 0 offset, high gain */
419         range = comedi_get_range(dev,ad_subdev,0,bipolar_highgain);
420         read_chan2(s,0,bipolar_highgain);
421         DPRINT(0,"bipolar zero offset, high gain [%g,%g]: %s\n",
422                 range->min,range->max,s);
423
424         /* unip/bip offset */
425         range = comedi_get_range(dev,ad_subdev,0,unipolar_lowgain);
426         read_chan2(s,0,unipolar_lowgain);
427         DPRINT(0,"unipolar zero offset, low gain [%g,%g]: %s\n",
428                 range->min,range->max,s);
429
430 }
431
432 void ni_mio_ai_postgain_cal(void)
433 {
434         linear_fit_t l;
435         double offset_r0;
436         double offset_r7;
437         double gain;
438         double a;
439
440         check_gain_chan_x(&l,CR_PACK(0,0,AREF_OTHER),1);
441         offset_r0=linear_fit_func_y(&l,caldacs[1].current);
442         printf("offset r0 %g\n",offset_r0);
443
444         check_gain_chan_x(&l,CR_PACK(0,7,AREF_OTHER),1);
445         offset_r7=linear_fit_func_y(&l,caldacs[1].current);
446         printf("offset r7 %g\n",offset_r7);
447
448         gain=l.slope;
449         
450         a=(offset_r0-offset_r7)/(200.0-1.0);
451         a=caldacs[1].current-a/gain;
452
453         printf("%g\n",a);
454
455         caldacs[1].current=rint(a);
456         update_caldac(1);
457 }
458
459 void ni_mio_ai_postgain_cal_2(int chan,int dac,int range_lo,int range_hi,double gain)
460 {
461         double offset_lo,offset_hi;
462         linear_fit_t l;
463         double slope;
464         double a;
465
466         check_gain_chan_x(&l,CR_PACK(chan,range_lo,AREF_OTHER),dac);
467         offset_lo=linear_fit_func_y(&l,caldacs[dac].current);
468         printf("offset lo %g\n",offset_lo);
469
470         check_gain_chan_x(&l,CR_PACK(chan,range_hi,AREF_OTHER),dac);
471         offset_hi=linear_fit_func_y(&l,caldacs[dac].current);
472         printf("offset hi %g\n",offset_hi);
473
474         slope=l.slope;
475         
476         a=(offset_lo-offset_hi)/(gain-1.0);
477         a=caldacs[dac].current-a/slope;
478
479         printf("%g\n",a);
480
481         caldacs[dac].current=rint(a);
482         update_caldac(dac);
483 }
484 #endif
485