2 A little auto-calibration utility, for boards
5 Right now, it only supports NI E series boards,
6 but it should be easily portable.
8 A few things need improvement here:
9 - current system gets "close", but doesn't
11 - no pre/post gain discrimination for the
13 - should read (and use) the actual reference
14 voltage value from eeprom
15 - statistics would be nice, to show how good
17 - doesn't check unipolar ranges
18 - "alternate calibrations" would be cool--to
19 accurately measure 0 in a unipolar range
24 #include <comedilib.h>
48 static caldac caldacs[N_CALDACS];
53 static int eeprom_subdev;
55 double read_chan(int adc,int range);
56 void write_caldac(comedi_t *dev,int subdev,int addr,int val);
57 void check_gain(int ad_chan,int range);
58 double check_gain_chan(int ad_chan,int range,int cdac);
61 void update_caldac(int i);
62 void reset_caldacs(void);
63 void setup_caldacs(void);
64 void cal_ni_mio_E(void);
65 void ni_mio_ai_postgain_cal(void);
66 void channel_dependence(int adc,int range);
67 void caldac_dependence(int caldac);
68 void dump_curve(int adc,int caldac);
69 void chan_cal(int adc,int caldac,int range,double target);
70 int read_eeprom(int addr);
84 double s1,sx,sy,sxy,sxx;
96 int linear_fit_monotonic(linear_fit_t *l);
97 double linear_fit_func_y(linear_fit_t *l,double x);
98 double check_gain_chan_x(linear_fit_t *l,int ad_chan,int range,int cdac);
104 unsigned int chanlist[1];
116 int new_sv_measure(new_sv_t *sv);
117 int new_sv_init(new_sv_t *sv,comedi_t *dev,int subdev,int chan,int range,int aref);
119 int main(int argc, char *argv[])
128 c = getopt(argc, argv, "rf");
136 printf("bad option\n");
141 dev = comedi_open(fn);
150 eeprom_subdev=comedi_find_subdevice_by_type(dev,COMEDI_SUBD_MEMORY,0);
152 drivername=comedi_get_driver_name(dev);
154 if(!strcmp(drivername,"atmio-E") || !strcmp(drivername,"pcimio-E"))
161 void cal_ni_mio_E(void)
167 boardname=comedi_get_board_name(dev);
169 if(!strcmp(boardname,"at-mio-16e-1") ||
170 !strcmp(boardname,"at-mio-16e-2") ||
171 !strcmp(boardname,"at-mio-64e-3") ||
172 !strcmp(boardname,"pci-mio-16e-1")){
176 0 AI pre-gain offset 1.5e-6
177 1 AI post-gain offset 8.1e-4
178 2 AI unipolar offset 7.9e-4
189 printf("last factory calibration %02d/%02d/%02d\n",
190 read_eeprom(508),read_eeprom(507),read_eeprom(506));
192 printf("lsb=%d msb=%d\n",read_eeprom(425),read_eeprom(426));
194 ref=5.000+(1e-6*(read_eeprom(425)+read_eeprom(426)));
195 printf("ref=%g\n",ref);
199 printf("postgain offset\n");
200 ni_mio_ai_postgain_cal();
202 printf("pregain offset\n");
206 printf("unipolar offset\n");
210 printf("gain offset\n");
216 if(!strcmp(boardname,"at-mio-16e-10")){
220 0 AI pre-gain offset 1.5e-6
221 1 AI post-gain offset 8.1e-4
222 2 AI unipolar offset 7.9e-4
230 10 AI pre-gain offset 6.4e-5
233 printf("last factory calibration %02d/%02d/%02d\n",
234 read_eeprom(508),read_eeprom(507),read_eeprom(506));
236 printf("lsb=%d msb=%d\n",read_eeprom(423),read_eeprom(424));
238 ref=5.000+(0.001*(read_eeprom(423)+read_eeprom(424)));
239 printf("ref=%g\n",ref);
243 printf("postgain offset\n");
244 ni_mio_ai_postgain_cal();
246 printf("pregain offset\n");
247 chan_cal(0,10,7,0.0);
251 printf("unipolar offset\n");
255 printf("gain offset\n");
259 printf("results (offset)\n");
271 printf("please send this output to <ds@stm.lbl.gov>\n");
272 printf("%s\n",comedi_get_board_name(dev));
274 n_ranges=comedi_get_n_ranges(dev,ad_subdev,0);
276 printf("channel dependence 0 range 0\n");
277 channel_dependence(0,0);
279 printf("channel dependence 0 range %d\n",n_ranges/2-1);
280 channel_dependence(0,n_ranges/2-1);
282 printf("channel dependence 0 range %d\n",n_ranges/2);
283 channel_dependence(0,n_ranges/2);
285 printf("channel dependence 5 range 0\n");
286 channel_dependence(5,0);
291 void ni_mio_ai_postgain_cal(void)
299 check_gain_chan_x(&l,0,0,1);
300 offset_r0=linear_fit_func_y(&l,caldacs[1].current);
301 printf("offset r0 %g\n",offset_r0);
303 check_gain_chan_x(&l,0,7,1);
304 offset_r7=linear_fit_func_y(&l,caldacs[1].current);
305 printf("offset r7 %g\n",offset_r7);
309 a=(offset_r0-offset_r7)/(200.0-1.0);
310 a=caldacs[1].current-a/gain;
314 caldacs[1].current=rint(a);
318 void chan_cal(int adc,int cdac,int range,double target)
325 check_gain_chan_x(&l,adc,range,cdac);
326 offset=linear_fit_func_y(&l,caldacs[cdac].current);
327 printf("offset %g\n",offset);
330 a=caldacs[cdac].current+(target-offset)/gain;
333 caldacs[cdac].current=rint(a);
339 void channel_dependence(int adc,int range)
344 for(i=0;i<n_caldacs;i++){
345 gain=check_gain_chan(adc,range,i);
349 void caldac_dependence(int caldac)
355 gain=check_gain_chan(i,0,caldac);
361 void dump_curve(int adc,int caldac)
366 check_gain_chan_x(&l,adc,0,caldac);
371 void setup_caldacs(void)
375 s=comedi_find_subdevice_by_type(dev,COMEDI_SUBD_CALIB,0);
377 printf("no calibration subdevice\n");
380 //printf("calibration subdevice is %d\n",s);
382 n_chan=comedi_get_n_channels(dev,s);
384 for(i=0;i<n_chan;i++){
387 caldacs[i].maxdata=comedi_get_maxdata(dev,s,i);
389 caldacs[i].maxdata=255;
390 caldacs[i].current=0;
391 //printf("caldac %d, %d\n",i,caldacs[i].maxdata);
397 void reset_caldacs(void)
401 for(i=0;i<n_caldacs;i++){
402 caldacs[i].current=caldacs[i].maxdata/2;
407 void update_caldac(int i)
411 //printf("update %d %d %d\n",caldacs[i].subdev,caldacs[i].chan,caldacs[i].current);
412 //ret=comedi_data_write(dev,caldacs[i].subdev,caldacs[i].chan,0,0,caldacs[i].current);
413 write_caldac(dev,caldacs[i].subdev,caldacs[i].chan,caldacs[i].current);
416 printf("comedi_data_write() error\n");
420 void check_gain(int ad_chan,int range)
424 for(i=0;i<n_caldacs;i++){
425 check_gain_chan(ad_chan,range,i);
429 double check_gain_chan(int ad_chan,int range,int cdac)
433 return check_gain_chan_x(&l,ad_chan,range,cdac);
436 double check_gain_chan_x(linear_fit_t *l,int ad_chan,int range,int cdac)
443 n=caldacs[cdac].maxdata+1;
444 memset(l,0,sizeof(*l));
445 l->y_data=malloc(n*sizeof(double));
447 orig=caldacs[cdac].current;
449 new_sv_init(&sv,dev,0,ad_chan,range,AREF_OTHER);
453 caldacs[cdac].current=i;
458 l->y_data[i]=sv.average;
459 if(!isnan(sv.average)){
465 caldacs[cdac].current=orig;
468 l->yerr=sum_err/sum_err_count;
473 linear_fit_monotonic(l);
475 printf("caldac[%d] gain=%g V/bit err=%g S_min=%g dof=%g\n",
476 cdac,l->slope,l->err_slope,l->S_min,l->dof);
477 //printf("--> %g\n",fabs(l.slope/l.err_slope));
481 printf("%d %g\n",i,l->y_data[i]);
497 int read_eeprom(int addr)
501 comedi_data_read(dev,eeprom_subdev,addr,0,0,&data);
506 double read_chan(int adc,int range)
511 new_sv_init(&sv,dev,0,adc,range,AREF_OTHER);
513 n=new_sv_measure(&sv);
515 printf("chan=%d ave=%g error=%g\n",adc,sv.average,sv.error);
520 void write_caldac(comedi_t *dev,int subdev,int addr,int val)
523 unsigned int chan=CR_PACK(addr,0,0);
526 memset(&it,0,sizeof(it));
534 if(comedi_trigger(dev,&it)<0){
535 perror("write_caldac");
542 int new_sv_init(new_sv_t *sv,comedi_t *dev,int subdev,int chan,int range,int aref)
544 memset(sv,0,sizeof(*sv));
547 sv->t.flags=TRIG_DITHER;
549 sv->t.chanlist=sv->chanlist;
551 sv->chanlist[0]=CR_PACK(chan,range,aref);
553 sv->maxdata=comedi_get_maxdata(dev,subdev,chan);
554 sv->rng=comedi_get_range(dev,subdev,chan,range);
561 int new_sv_measure(new_sv_t *sv)
570 data=malloc(sizeof(sampl_t)*n);
575 ret=comedi_trigger(dev,&sv->t);
582 a=comedi_to_phys(data[0],sv->rng,sv->maxdata);
584 x=comedi_to_phys(data[i],sv->rng,sv->maxdata);
589 sv->stddev=sqrt(n*s2-s*s)/n;
590 sv->error=sv->stddev/sqrt(n);
600 int new_sv_measure_order(new_sv_t *sv,int order)
608 data=malloc(sizeof(sampl_t)*n);
613 ret=comedi_trigger(dev,&sv->t);
620 a=comedi_to_phys(data[0],sv->rng,sv->maxdata);
622 x=comedi_to_phys(data[i],sv->rng,sv->maxdata);
627 sv->stddev=sqrt(n*s2-s*s)/n;
628 sv->error=sv->stddev/sqrt(n);
642 int calculate_residuals(linear_fit_t *l);
644 int linear_fit_monotonic(linear_fit_t *l)
659 if(isnan(y))continue;
667 sxp=l->sxx-l->sx*l->sx/l->s1;
669 l->ave_x=l->sx/l->s1;
670 l->ave_y=l->sy/l->s1;
671 l->slope=(l->s1*l->sxy-l->sx*l->sy)/(l->s1*l->sxx-l->sx*l->sx);
672 l->err_slope=l->yerr/sqrt(sxp);
673 l->err_ave_y=l->yerr/sqrt(l->s1);
675 calculate_residuals(l);
680 int calculate_residuals(linear_fit_t *l)
688 x=l->x0+i*l->dx-l->ave_x;
691 if(isnan(y))continue;
693 res=l->ave_y+l->slope*x-y;
697 l->S_min=sum_res2/(l->yerr*l->yerr);
703 double linear_fit_func_y(linear_fit_t *l,double x)
705 return l->ave_y+l->slope*(x-l->ave_x);