2
A little auto-calibration utility, for boards
5
copyright (C) 1999,2000,2001,2002 by David Schleef
6
copyright (C) 2003 by Frank Mori Hess
10
/***************************************************************************
12
* This program is free software; you can redistribute it and/or modify *
13
* it under the terms of the GNU Lesser General Public License as *
15
* the Free Software Foundation; either version 2.1 of the License, or *
16
* (at your option) any later version. *
18
***************************************************************************/
23
#include "comedilib.h"
36
/* global variables */
42
int (*init_setup)( calibration_setup_t *setup, const char *device_name );
45
struct board_struct drivers[] = {
46
{ "ni_pcimio", ni_id, ni_setup },
47
{ "ni_atmio", ni_id, ni_setup },
48
{ "ni_mio_cs", ni_id, ni_setup },
49
{ "cb_pcidas", cb_id, cb_setup },
50
{ "cb_pcidas64", cb64_id, cb64_setup },
51
{ "ni_labpc", ni_labpc_id, ni_labpc_setup },
53
#define n_drivers (sizeof(drivers)/sizeof(drivers[0]))
57
printf("comedi_calibrate [options] - autocalibrates a Comedi device\n");
58
printf(" --verbose, -v \n");
59
printf(" --quiet, -q \n");
60
printf(" --help, -h \n");
61
printf(" --file, -f [/dev/comediN] \n");
62
printf(" --save-file, -S [filepath] \n");
63
printf(" --driver-name [driver] \n");
64
printf(" --device-name [device] \n");
65
printf(" --[no-]reset \n");
66
printf(" --[no-]calibrate \n");
67
printf(" --[no-]dump \n");
68
printf(" --[no-]results \n");
69
printf(" --[no-]output \n");
84
unsigned int subdevice;
90
static void parse_options( int argc, char *argv[], parsed_options_t *settings )
94
struct option options[] = {
95
{ "verbose", 0, 0, 'v' },
96
{ "quiet", 0, 0, 'q' },
97
{ "file", 1, 0, 'f' },
98
{ "save-file", 1, 0, 'S' },
99
{ "help", 0, 0, 'h' },
100
{ "driver-name", 1, 0, 0x1000 },
101
{ "device-name", 1, 0, 0x1001 },
102
{ "reset", 0, &settings->do_reset, 1 },
103
{ "no-reset", 0, &settings->do_reset, 0 },
104
{ "calibrate", 0, &settings->do_calibrate, 1 },
105
{ "no-calibrate", 0, &settings->do_calibrate, 0 },
106
{ "dump", 0, &settings->do_dump, 1 },
107
{ "no-dump", 0, &settings->do_dump, 0 },
108
{ "results", 0, &settings->do_results, 1 },
109
{ "no-results", 0, &settings->do_results, 0 },
110
{ "output", 0, &settings->do_output, 1 },
111
{ "no-output", 0, &settings->do_output, 0 },
112
{ "subdevice", 1, 0, 's' },
113
{ "channel", 1, 0, 'c' },
114
{ "range", 1, 0, 'r' },
115
{ "aref", 1, 0, 'a' },
120
c = getopt_long(argc, argv, "f:S:vqs:c:r:a:", options, &index);
130
settings->file_path = optarg;
133
settings->save_file_path = optarg;
142
settings->driver_name = optarg;
145
settings->device_name = optarg;
148
settings->subdevice = strtoul( optarg, NULL, 0 );
151
settings->channel = strtoul( optarg, NULL, 0 );
154
settings->range = strtoul( optarg, NULL, 0 );
157
settings->aref = strtoul( optarg, NULL, 0 );
166
int main(int argc, char *argv[])
169
struct board_struct *this_board;
170
int device_status = STATUS_UNKNOWN;
171
calibration_setup_t setup;
173
parsed_options_t options;
175
memset( &setup, 0, sizeof( setup ) );
176
setup.sv_settling_time_ns = 99999;
179
memset( &options, 0, sizeof( options ) );
181
options.do_reset = 0;
182
options.do_calibrate = -1;
183
options.do_results = 0;
184
options.do_output = 1;
185
options.file_path = "/dev/comedi0";
186
parse_options( argc, argv, &options );
187
verbose = options.verbose;
189
setup.dev = comedi_open( options.file_path );
190
if( setup.dev == NULL ) {
191
fprintf( stderr, "comedi_open() failed, with device file name: %s\n",
193
comedi_perror("comedi_open");
197
if( options.save_file_path == NULL )
198
options.save_file_path = comedi_get_default_calibration_path( setup.dev );
199
if(!options.driver_name)
200
options.driver_name=comedi_get_driver_name( setup.dev );
201
if(!options.device_name)
202
options.device_name=comedi_get_board_name( setup.dev );
204
setup.ad_subdev=comedi_find_subdevice_by_type( setup.dev,COMEDI_SUBD_AI,0);
205
setup.da_subdev=comedi_find_subdevice_by_type( setup.dev,COMEDI_SUBD_AO,0);
206
setup.caldac_subdev=comedi_find_subdevice_by_type( setup.dev,COMEDI_SUBD_CALIB,0);
207
setup.eeprom_subdev=comedi_find_subdevice_by_type( setup.dev,COMEDI_SUBD_MEMORY,0);
209
for(i=0;i<n_drivers;i++){
210
if(!strcmp(drivers[i].name,options.driver_name)){
211
this_board = drivers+i;
215
fprintf(stderr, "Driver %s unknown\n", options.driver_name);
220
retval = this_board->init_setup( &setup, options.device_name );
222
fprintf(stderr, "init_setup() failed for %s\n", options.device_name );
225
device_status = setup.status;
227
if(device_status<STATUS_DONE){
228
printf("Warning: device may not be not fully calibrated due to "
229
"insufficient information.\n"
230
"Please file a bug report at https://bugs.comedi.org/ and attach this output.\n"
231
"This output will also allow comedi_calibrate to execute more\n"
232
"quickly in the future.\n");
235
printf("Forcing option: --verbose\n");
237
if(device_status==STATUS_UNKNOWN){
240
options.do_calibrate=0;
241
options.do_results=0;
242
printf("Forcing options: --reset --dump --no-calibrate --no-results\n");
244
if(device_status==STATUS_SOME){
247
options.do_calibrate=1;
248
options.do_results=1;
249
printf("Forcing options: --reset --dump --calibrate --results\n");
251
if(device_status==STATUS_GUESS){
254
options.do_calibrate=1;
255
options.do_results=1;
256
printf("Forcing options: --reset --dump --calibrate --results\n");
260
char *s = "$Id: comedi_calibrate.c,v 1.89 2003/08/08 17:38:59 fmhess Exp $";
262
printf("%.*s\n",(int)strlen(s)-2,s+1);
263
printf("Driver name: %s\n", options.driver_name);
264
printf("Device name: %s\n", options.device_name);
265
printf("%.*s\n",(int)strlen(this_board->id)-2,this_board->id+1);
266
printf("Comedi version: %d.%d.%d\n",
267
(comedi_get_version_code(setup.dev)>>16)&0xff,
268
(comedi_get_version_code(setup.dev)>>8)&0xff,
269
(comedi_get_version_code(setup.dev))&0xff);
272
if( options.do_reset == 0 )
273
setup.old_calibration = comedi_parse_calibration_file( options.save_file_path );
275
setup.old_calibration = NULL;
276
if( options.do_calibrate < 0 )
278
if( setup.old_calibration ) options.do_calibrate = 0;
279
else options.do_calibrate = 1;
281
setup.do_output = options.do_output;
282
setup.cal_save_file_path = options.save_file_path;
284
if(options.do_dump) observe( &setup );
285
if(options.do_calibrate && setup.do_cal)
287
setup.new_calibration = malloc( sizeof( comedi_calibration_t ) );
288
assert( setup.new_calibration );
289
memset( setup.new_calibration, 0, sizeof( comedi_calibration_t ) );
290
setup.new_calibration->driver_name = strdup( comedi_get_driver_name( setup.dev ) );
291
assert( setup.new_calibration->driver_name != NULL );
292
setup.new_calibration->board_name = strdup( comedi_get_board_name( setup.dev ) );
293
assert( setup.new_calibration->board_name != NULL );
294
retval = setup.do_cal( &setup );
297
fprintf( stderr, "calibration function returned error\n" );
301
if(options.do_results) observe( &setup );
303
if( setup.old_calibration ) comedi_cleanup_calibration( setup.old_calibration );
304
if( setup.new_calibration ) comedi_cleanup_calibration( setup.new_calibration );
306
retval = comedi_apply_calibration( setup.dev, options.subdevice,
307
options.channel, options.range, options.aref, setup.cal_save_file_path );
310
DPRINT( 0, "Failed to apply " );
313
DPRINT( 0, "Applied " );
315
DPRINT( 0, "calibration for subdevice %i, channel %i, range %i, aref %i\n",
316
options.subdevice, options.channel, options.range, options.aref );
317
comedi_close(setup.dev);
322
void set_target( calibration_setup_t *setup, int obs,double target)
325
lsampl_t maxdata, data;
327
comedi_set_global_oor_behavior( COMEDI_OOR_NUMBER );
329
range = comedi_get_range(setup->dev,
330
setup->observables[obs].preobserve_insn.subdev,
331
CR_CHAN( setup->observables[obs].preobserve_insn.chanspec ),
332
CR_RANGE( setup->observables[obs].preobserve_insn.chanspec ));
334
maxdata = comedi_get_maxdata( setup->dev,
335
setup->observables[obs].preobserve_insn.subdev,
336
CR_CHAN(setup->observables[obs].preobserve_insn.chanspec));
337
assert( maxdata > 0 );
338
data = comedi_from_phys(target,range,maxdata);
340
setup->observables[obs].preobserve_data[0] = data;
341
setup->observables[obs].target = comedi_to_phys(data,range,maxdata);
344
static void apply_appropriate_cal( calibration_setup_t *setup, comedi_insn insn )
348
if( setup->new_calibration )
350
retval = comedi_apply_parsed_calibration( setup->dev, insn.subdev,
351
CR_CHAN( insn.chanspec ), CR_RANGE( insn.chanspec ),
352
CR_AREF( insn.chanspec ), setup->new_calibration );
353
}else if( setup->old_calibration )
355
retval = comedi_apply_parsed_calibration( setup->dev, insn.subdev,
356
CR_CHAN( insn.chanspec ), CR_RANGE( insn.chanspec ),
357
CR_AREF( insn.chanspec ), setup->old_calibration );
360
reset_caldacs( setup );
364
DPRINT( 1, "failed to apply ");
366
DPRINT( 1, "applied ");
367
DPRINT( 1, "calibration for subdev %i, channel %i, range %i, aref %i\n", insn.subdev,
368
CR_CHAN( insn.chanspec ), CR_RANGE( insn.chanspec ),
369
CR_AREF( insn.chanspec ) );
372
void observe( calibration_setup_t *setup )
377
for( i = 0; i < setup->n_observables; i++){
378
obs = &setup->observables[ i ];
379
if( obs->observe_insn.n == 0 ) continue;
380
preobserve( setup, i);
381
DPRINT(0,"%s\n", setup->observables[i].name);
382
if( obs->preobserve_insn.n != 0){
383
apply_appropriate_cal( setup, obs->preobserve_insn );
385
apply_appropriate_cal( setup, obs->observe_insn );
386
measure_observable( setup, i);
388
observable_dependence( setup, i);
393
int preobserve( calibration_setup_t *setup, int obs)
396
comedi_insn reference_source_config;
397
lsampl_t ref_data[ 2 ];
399
// setup reference source
400
memset( &reference_source_config, 0, sizeof(reference_source_config) );
401
reference_source_config.insn = INSN_CONFIG;
402
reference_source_config.n = 2;
403
reference_source_config.subdev = setup->ad_subdev;
404
reference_source_config.data = ref_data;
405
reference_source_config.data[ 0 ] = INSN_CONFIG_ALT_SOURCE;
406
reference_source_config.data[ 1 ] = setup->observables[obs].reference_source;
408
retval = comedi_do_insn( setup->dev, &reference_source_config );
409
/* ignore errors for now since older ni driver doesn't
410
* support reference config insn */
412
perror("preobserve() ignoring reference config error" );
415
if( setup->observables[obs].preobserve_insn.n != 0){
416
retval = comedi_do_insn( setup->dev, &setup->observables[obs].preobserve_insn);
419
perror("preobserve()");
424
void measure_observable( calibration_setup_t *setup, int obs)
430
my_sv_init(&sv, setup,
431
setup->observables[obs].observe_insn.subdev,
432
setup->observables[obs].observe_insn.chanspec);
433
n = new_sv_measure(setup->dev, &sv);
435
sci_sprint_alt(s,sv.average,sv.error);
436
DPRINT(0,"reading %s, target %g\n",s, setup->observables[obs].target);
437
assert( isnan( setup->observables[obs].target) == 0 );
440
void observable_dependence(calibration_setup_t *setup, int obs)
445
for( i = 0; i < setup->n_caldacs; i++){
446
check_gain_chan_x( setup, &l,
447
setup->observables[obs].observe_insn.chanspec, i);
453
void postgain_cal( calibration_setup_t *setup, int obs1, int obs2, int dac)
455
double offset1,offset2;
457
double slope1,slope2;
460
comedi_range *range1,*range2;
462
DPRINT(0,"postgain: %s; %s\n", setup->observables[obs1].name,
463
setup->observables[obs2].name);
464
preobserve(setup, obs1);
465
check_gain_chan_x( setup, &l, setup->observables[obs1].observe_insn.chanspec, dac);
466
offset1=linear_fit_func_y(&l, setup->caldacs[dac].current);
467
DPRINT(2,"obs1: [%d] offset %g\n",obs1,offset1);
468
range1 = comedi_get_range(setup->dev, setup->observables[obs1].observe_insn.subdev,
469
CR_CHAN( setup->observables[obs1].observe_insn.chanspec),
470
CR_RANGE( setup->observables[obs1].observe_insn.chanspec));
473
preobserve( setup, obs2);
474
check_gain_chan_x( setup, &l, setup->observables[obs2].observe_insn.chanspec,dac);
475
offset2=linear_fit_func_y(&l, setup->caldacs[dac].current);
476
DPRINT(2,"obs2: [%d] offset %g\n",obs2,offset2);
477
range2 = comedi_get_range(setup->dev, setup->observables[obs2].observe_insn.subdev,
478
CR_CHAN( setup->observables[obs2].observe_insn.chanspec),
479
CR_RANGE( setup->observables[obs2].observe_insn.chanspec));
482
gain = (range1->max-range1->min)/(range2->max-range2->min);
483
DPRINT(4,"range1 %g range2 %g\n", range1->max-range1->min,
484
range2->max-range2->min);
485
DPRINT(3,"gain: %g\n",gain);
487
DPRINT(3,"difference: %g\n",offset2-offset1);
489
a = (offset1-offset2)/(slope1-slope2);
490
a=setup->caldacs[dac].current-a;
492
update_caldac( setup, dac, rint(a) );
493
usleep(caldac_settle_usec);
495
DPRINT(0,"caldac[%d] set to %g (%g)\n",dac,rint(a),a);
498
preobserve( setup, obs1);
499
measure_observable( setup, obs1);
500
preobserve( setup, obs2);
501
measure_observable( setup, obs2);
505
void cal1( calibration_setup_t *setup, int obs, int dac)
510
DPRINT(0,"linear: %s\n", setup->observables[obs].name);
511
preobserve( setup, obs);
512
check_gain_chan_x( setup, &l, setup->observables[obs].observe_insn.chanspec,dac);
513
a=linear_fit_func_x(&l, setup->observables[obs].target);
515
update_caldac( setup, dac, rint(a) );
516
usleep(caldac_settle_usec);
518
DPRINT(0,"caldac[%d] set to %g (%g)\n",dac,rint(a),a);
520
measure_observable( setup, obs);
524
void cal1_fine( calibration_setup_t *setup, int obs, int dac )
529
DPRINT(0,"linear fine: %s\n", setup->observables[obs].name);
530
preobserve( setup, obs);
531
check_gain_chan_fine( setup, &l, setup->observables[obs].observe_insn.chanspec,dac);
532
a=linear_fit_func_x(&l,setup->observables[obs].target);
534
update_caldac( setup, dac, rint(a) );
535
usleep(caldac_settle_usec);
537
DPRINT(0,"caldac[%d] set to %g (%g)\n",dac,rint(a),a);
539
measure_observable( setup, obs);
543
void peg_binary( calibration_setup_t *setup, int obs, int dac, int maximize )
548
unsigned int chanspec = setup->observables[obs].observe_insn.chanspec;
551
DPRINT(0,"binary peg: %s\n", setup->observables[obs].name);
552
preobserve( setup, obs);
554
comedi_set_global_oor_behavior( COMEDI_OOR_NUMBER );
556
my_sv_init(&sv, setup, setup->ad_subdev, chanspec);
558
x0 = setup->caldacs[dac].maxdata;
559
update_caldac( setup, dac, x0 );
560
usleep(caldac_settle_usec);
561
new_sv_measure( setup->dev, &sv);
565
update_caldac( setup, dac, x1 );
566
usleep(caldac_settle_usec);
567
new_sv_measure( setup->dev, &sv);
570
if( (y0 - y1) > 0.0 ) polarity = 1;
575
if( polarity > 0 ) x = x0;
579
if( polarity > 0 ) x = x1;
582
update_caldac( setup, dac, x );
583
DPRINT(0,"caldac[%d] set to %d\n",dac,x);
585
measure_observable( setup, obs);
589
void cal_binary( calibration_setup_t *setup, int obs, int dac)
595
double target = setup->observables[obs].target;
596
unsigned int chanspec = setup->observables[obs].observe_insn.chanspec;
599
DPRINT(0,"binary: %s\n", setup->observables[obs].name);
600
preobserve( setup, obs);
602
comedi_set_global_oor_behavior( COMEDI_OOR_NUMBER );
604
my_sv_init(&sv, setup, setup->ad_subdev, chanspec);
606
x0 = setup->caldacs[dac].maxdata;
607
update_caldac( setup, dac, x0 );
608
usleep(caldac_settle_usec);
609
new_sv_measure( setup->dev, &sv);
613
update_caldac( setup, dac, x1 );
614
usleep(caldac_settle_usec);
615
new_sv_measure( setup->dev, &sv);
616
y1 = y2 = sv.average;
618
if( (y0 - y1) > 0.0 ) polarity = 1;
622
while( ( bit << 1 ) < setup->caldacs[dac].maxdata )
624
for( ; bit; bit >>= 1 ){
627
update_caldac( setup, dac, x2 );
628
usleep(caldac_settle_usec);
629
new_sv_measure( setup->dev, &sv);
631
DPRINT(3,"trying %d, result %g, target %g\n",x2,y2,target);
633
if( (y2 - target) * polarity < 0.0 ){
639
measure_observable( setup, obs);
643
// get that least signficant bit right
644
if( fabs( y1 - target ) < fabs( y2 - target ) )
648
update_caldac( setup, dac, x );
649
DPRINT(0,"caldac[%d] set to %d\n",dac,x);
650
if( x >= setup->caldacs[dac].maxdata || x <= 0 )
651
DPRINT(0,"WARNING: caldac[%d] pegged!\n", dac );
653
measure_observable( setup, obs);
657
void cal_postgain_binary( calibration_setup_t *setup, int obs1, int obs2, int dac)
659
cal_relative_binary( setup, obs1, obs2, dac );
662
void cal_relative_binary( calibration_setup_t *setup, int obs1, int obs2, int dac)
664
int x0, x1, x2, x, polarity;
667
double target = setup->observables[obs1].target - setup->observables[obs2].target;
668
unsigned int chanspec1 = setup->observables[obs1].observe_insn.chanspec;
669
unsigned int chanspec2 = setup->observables[obs2].observe_insn.chanspec;
672
DPRINT(0,"relative binary: %s, %s\n", setup->observables[obs1].name,
673
setup->observables[obs2].name);
675
comedi_set_global_oor_behavior( COMEDI_OOR_NUMBER );
677
x0 = setup->caldacs[dac].maxdata;
678
update_caldac( setup, dac, x0 );
679
usleep(caldac_settle_usec);
680
preobserve( setup, obs1);
681
my_sv_init(&sv1, setup, setup->ad_subdev,chanspec1);
682
new_sv_measure( setup->dev, &sv1);
684
preobserve( setup, obs2);
685
my_sv_init(&sv2, setup, setup->ad_subdev,chanspec2);
686
new_sv_measure( setup->dev, &sv2);
687
y0 = sv1.average - sv2.average;
690
update_caldac( setup, dac, x1 );
691
usleep(caldac_settle_usec);
692
preobserve( setup, obs1);
693
new_sv_measure( setup->dev, &sv1);
695
preobserve( setup, obs2);
696
new_sv_measure( setup->dev, &sv2);
697
y1 = y2 = sv1.average - sv2.average;
699
if( (y0 - y1) > 0.0 ) polarity = 1;
703
while( ( bit << 1 ) < setup->caldacs[dac].maxdata )
705
for( ; bit; bit >>= 1 )
709
update_caldac( setup, dac, x2 );
710
usleep(caldac_settle_usec);
712
preobserve( setup, obs1);
713
new_sv_measure( setup->dev, &sv1);
715
preobserve( setup, obs2);
716
new_sv_measure( setup->dev, &sv2);
717
y2 = sv1.average - sv2.average;
719
DPRINT(3,"trying %d, result %g, target %g\n",x2,y2,target);
721
if( (y2 - target) * polarity < 0.0 ){
727
preobserve( setup, obs1);
728
measure_observable( setup, obs1);
729
preobserve( setup, obs2);
730
measure_observable( setup, obs2);
734
if( fabs( y1 - target ) < fabs( y2 - target ) )
738
update_caldac( setup, dac, x );
739
DPRINT(0,"caldac[%d] set to %d\n",dac,x);
740
if( x >= setup->caldacs[dac].maxdata || x <= 0 )
741
DPRINT(0,"WARNING: caldac[%d] pegged!\n", dac );
743
preobserve( setup, obs1);
744
measure_observable( setup, obs1);
745
preobserve( setup, obs2);
746
measure_observable( setup, obs2);
750
void cal_linearity_binary( calibration_setup_t *setup, int obs1, int obs2, int obs3, int dac)
752
int x0, x1, x2, x, polarity;
754
new_sv_t sv1, sv2, sv3;
755
double target = ( setup->observables[obs3].target - setup->observables[obs2].target ) /
756
( setup->observables[obs2].target - setup->observables[obs1].target );
757
unsigned int chanspec1 = setup->observables[obs1].observe_insn.chanspec;
758
unsigned int chanspec2 = setup->observables[obs2].observe_insn.chanspec;
759
unsigned int chanspec3 = setup->observables[obs3].observe_insn.chanspec;
762
DPRINT(0,"linearity binary: %s,\n%s,\n%s\n", setup->observables[obs1].name,
763
setup->observables[obs2].name,setup->observables[obs3].name);
765
comedi_set_global_oor_behavior( COMEDI_OOR_NUMBER );
767
x0 = setup->caldacs[dac].maxdata;
768
update_caldac( setup, dac, x0 );
769
usleep(caldac_settle_usec);
771
preobserve( setup, obs1);
772
my_sv_init(&sv1, setup, setup->ad_subdev,chanspec1);
773
new_sv_measure( setup->dev, &sv1);
775
preobserve( setup, obs2);
776
my_sv_init(&sv2, setup, setup->ad_subdev,chanspec2);
777
new_sv_measure( setup->dev, &sv2);
779
preobserve( setup, obs3);
780
my_sv_init(&sv3, setup, setup->ad_subdev,chanspec3);
781
new_sv_measure( setup->dev, &sv3);
783
y0 = ( sv3.average - sv2.average ) / ( sv2.average - sv1.average );
786
update_caldac( setup, dac, x1 );
787
usleep(caldac_settle_usec);
789
preobserve( setup, obs1);
790
new_sv_measure( setup->dev, &sv1);
792
preobserve( setup, obs2);
793
new_sv_measure( setup->dev, &sv2);
795
preobserve( setup, obs3);
796
new_sv_measure( setup->dev, &sv3);
798
y1 = y2 = ( sv3.average - sv2.average ) / ( sv2.average - sv1.average );
800
if( (y0 - y1) > 0.0 ) polarity = 1;
804
while( ( bit << 1 ) < setup->caldacs[dac].maxdata )
806
for( ; bit; bit >>= 1 )
810
update_caldac( setup, dac, x2 );
811
usleep(caldac_settle_usec);
813
preobserve( setup, obs1);
814
new_sv_measure( setup->dev, &sv1);
816
preobserve( setup, obs2);
817
new_sv_measure( setup->dev, &sv2);
819
preobserve( setup, obs3);
820
new_sv_measure( setup->dev, &sv3);
822
y2 = ( sv3.average - sv2.average ) / ( sv2.average - sv1.average );
824
DPRINT(3,"trying %d, result %g, target %g\n",x2,y2,target);
826
if( (y2 - target) * polarity < 0.0 ){
832
preobserve( setup, obs1);
833
measure_observable( setup, obs1);
834
preobserve( setup, obs2);
835
measure_observable( setup, obs2);
836
preobserve( setup, obs3);
837
measure_observable( setup, obs3);
841
if( fabs( y1 - target ) < fabs( y2 - target ) )
845
update_caldac( setup, dac, x );
846
DPRINT(0,"caldac[%d] set to %d\n",dac,x);
847
if( x >= setup->caldacs[dac].maxdata || x <= 0 )
848
DPRINT(0,"WARNING: caldac[%d] pegged!\n", dac );
850
preobserve( setup, obs1);
851
measure_observable( setup, obs1);
852
preobserve( setup, obs2);
853
measure_observable( setup, obs2);
854
preobserve( setup, obs3);
855
measure_observable( setup, obs3);
860
void chan_cal(int adc,int cdac,int range,double target)
868
check_gain_chan_x(&l,CR_PACK(adc,range,AREF_OTHER),cdac);
869
offset=linear_fit_func_y(&l,caldacs[cdac].current);
872
a=caldacs[cdac].current+(target-offset)/gain;
874
update_caldac( setup, cdac, rint(a));
876
read_chan2(s,adc,range);
877
DPRINT(1,"caldac[%d] set to %g, offset=%s\n",cdac,a,s);
883
void channel_dependence(int adc,int range)
888
for(i=0;i<n_caldacs;i++){
889
gain=check_gain_chan(adc,range,i);
895
void caldac_dependence(int caldac)
901
gain=check_gain_chan(i,0,caldac);
907
void setup_caldacs( calibration_setup_t *setup, int caldac_subdev )
911
if( caldac_subdev < 0 ){
912
printf("no calibration subdevice\n");
916
// XXX check subdevice type is really calibration
917
// XXX check we dont exceed max number of allowable caldacs
919
n_chan = comedi_get_n_channels( setup->dev, caldac_subdev );
921
for(i = 0; i < n_chan; i++){
922
setup->caldacs[ setup->n_caldacs + i ].subdev = caldac_subdev;
923
setup->caldacs[ setup->n_caldacs + i ].chan = i;
924
setup->caldacs[ setup->n_caldacs + i ].maxdata = comedi_get_maxdata( setup->dev, caldac_subdev, i);
925
setup->caldacs[ setup->n_caldacs + i ].current=0;
928
setup->n_caldacs += n_chan;
931
void reset_caldac( calibration_setup_t *setup, int caldac_index )
933
if( caldac_index < 0 ) return;
934
assert( caldac_index < setup->n_caldacs );
935
update_caldac( setup, caldac_index, setup->caldacs[ caldac_index ].maxdata / 2 );
938
void reset_caldacs( calibration_setup_t *setup )
942
for( i = 0; i < setup->n_caldacs; i++){
943
reset_caldac( setup, i );
947
void update_caldac( calibration_setup_t *setup, int caldac_index,
953
if( caldac_index < 0 ) return;
954
if( caldac_index > setup->n_caldacs )
956
fprintf( stderr, "invalid caldac index\n" );
959
dac = &setup->caldacs[ caldac_index ];
960
dac->current = value;
961
DPRINT(4,"update %d %d %d\n", dac->subdev, dac->chan, dac->current);
962
if( dac->current < 0 ){
963
DPRINT(1,"caldac set out of range (%d<0)\n", dac->current);
966
if( dac->current > dac->maxdata ){
967
DPRINT(1,"caldac set out of range (%d>%d)\n",
968
dac->current, dac->maxdata);
969
dac->current = dac->maxdata;
972
ret = comedi_data_write( setup->dev, dac->subdev, dac->chan, 0, 0,
974
if(ret < 0) perror("update_caldac()");
978
void check_gain(int ad_chan,int range)
982
for(i=0;i<n_caldacs;i++){
983
check_gain_chan(ad_chan,range,i);
989
double check_gain_chan(int ad_chan,int range,int cdac)
993
return check_gain_chan_x(&l,CR_PACK(ad_chan,range,AREF_OTHER),cdac);
998
double check_gain_chan_x( calibration_setup_t *setup, linear_fit_t *l,unsigned int ad_chanspec,int cdac)
1004
int sum_err_count=0;
1007
n = setup->caldacs[cdac].maxdata+1;
1008
memset(l,0,sizeof(*l));
1014
l->y_data=malloc(n*sizeof(double)/step);
1015
if(l->y_data == NULL)
1017
perror( __FUNCTION__ );
1021
orig = setup->caldacs[cdac].current;
1023
my_sv_init(&sv, setup, setup->ad_subdev,ad_chanspec);
1025
update_caldac( setup, cdac, 0 );
1026
usleep(caldac_settle_usec);
1028
new_sv_measure( setup->dev, &sv);
1031
for(i=0;i*step<n;i++){
1032
update_caldac( setup, cdac, i*step );
1033
usleep(caldac_settle_usec);
1035
new_sv_measure( setup->dev, &sv);
1037
l->y_data[i]=sv.average;
1038
if(!isnan(sv.average)){
1045
update_caldac( setup, cdac, orig );
1047
l->yerr=sum_err/sqrt(sum_err_count);
1051
linear_fit_monotonic(l);
1053
if(verbose>=2 || (verbose>=1 && fabs(l->slope/l->err_slope)>4.0)){
1054
sci_sprint_alt(str,l->slope,l->err_slope);
1055
printf("caldac[%d] gain=%s V/bit S_min=%g dof=%g\n",
1056
cdac,str,l->S_min,l->dof);
1057
//printf("--> %g\n",fabs(l.slope/l.err_slope));
1060
if(verbose>=3)dump_curve(l);
1068
double check_gain_chan_fine( calibration_setup_t *setup, linear_fit_t *l,unsigned int ad_chanspec,int cdac)
1074
int sum_err_count=0;
1079
memset(l,0,sizeof(*l));
1084
l->y_data=malloc(n*sizeof(double)/step);
1085
if(l->y_data == NULL)
1087
perror( __FUNCTION__ );
1091
orig = setup->caldacs[cdac].current;
1093
my_sv_init(&sv, setup, setup->ad_subdev,ad_chanspec);
1095
update_caldac( setup, cdac, 0 );
1096
usleep(caldac_settle_usec);
1098
new_sv_measure( setup->dev, &sv);
1102
update_caldac( setup, cdac, i+orig-fine_size );
1103
usleep(caldac_settle_usec);
1105
new_sv_measure( setup->dev, &sv);
1107
l->y_data[i]=sv.average;
1108
if(!isnan(sv.average)){
1115
update_caldac( setup, cdac, orig );
1117
l->yerr=sum_err/sqrt(sum_err_count);
1119
l->x0=orig-fine_size;
1121
linear_fit_monotonic(l);
1123
if(verbose>=2 || (verbose>=1 && fabs(l->slope/l->err_slope)>4.0)){
1124
sci_sprint_alt(str,l->slope,l->err_slope);
1125
printf("caldac[%d] gain=%s V/bit S_min=%g dof=%g\n",
1126
cdac,str,l->S_min,l->dof);
1127
//printf("--> %g\n",fabs(l.slope/l.err_slope));
1130
if(verbose>=3)dump_curve(l);
1141
int is_unipolar( comedi_t *dev, unsigned int subdevice,
1142
unsigned int channel, unsigned int range )
1144
comedi_range *range_ptr;
1146
range_ptr = comedi_get_range( dev, subdevice, channel, range );
1147
assert( range_ptr != NULL );
1148
/* This method is better than a direct test, which might fail */
1149
if( fabs( range_ptr->min ) < fabs( range_ptr->max * 0.001 ) )
1155
int is_bipolar( comedi_t *dev, unsigned int subdevice,
1156
unsigned int channel, unsigned int range )
1158
comedi_range *range_ptr;
1160
range_ptr = comedi_get_range( dev, subdevice, channel, range );
1161
assert( range_ptr != NULL );
1162
/* This method is better than a direct test, which might fail */
1163
if( fabs( range_ptr->max + range_ptr->min ) < fabs( range_ptr->max * 0.001 ) )
1169
int get_bipolar_lowgain(comedi_t *dev,int subdev)
1173
int n_ranges = comedi_get_n_ranges(dev,subdev,0);
1175
comedi_range *range;
1177
for(i=0;i<n_ranges;i++){
1178
range = comedi_get_range(dev,subdev,0,i);
1179
if( is_bipolar( dev, subdev, 0, i ) == 0 ) continue;
1189
int get_bipolar_highgain(comedi_t *dev,int subdev)
1193
int n_ranges = comedi_get_n_ranges(dev,subdev,0);
1194
double min = HUGE_VAL;
1195
comedi_range *range;
1197
for(i=0;i<n_ranges;i++){
1198
range = comedi_get_range(dev,subdev,0,i);
1199
if( is_bipolar( dev, subdev, 0, i ) == 0 ) continue;
1209
int get_unipolar_lowgain(comedi_t *dev,int subdev)
1213
int n_ranges = comedi_get_n_ranges(dev,subdev,0);
1215
comedi_range *range;
1217
for(i=0;i<n_ranges;i++){
1218
range = comedi_get_range(dev,subdev,0,i);
1219
if( is_unipolar( dev, subdev, 0, i ) == 0 ) continue;
1229
int get_unipolar_highgain(comedi_t *dev,int subdev)
1233
int n_ranges = comedi_get_n_ranges(dev,subdev,0);
1234
double max = HUGE_VAL;
1235
comedi_range *range;
1237
for(i=0;i<n_ranges;i++){
1238
range = comedi_get_range(dev,subdev,0,i);
1239
if( is_unipolar( dev, subdev, 0, i ) == 0 ) continue;
1240
if(range->max < max){
1249
int read_eeprom( calibration_setup_t *setup, int addr)
1254
retval = comedi_data_read( setup->dev, setup->eeprom_subdev, addr,0,0,&data);
1257
perror( "read_eeprom()" );
1264
double read_chan( calibration_setup_t *setup, int adc,int range)
1270
my_sv_init(&sv, setup, setup->ad_subdev,CR_PACK(adc,range,AREF_OTHER));
1272
n=new_sv_measure( setup->dev, &sv);
1274
sci_sprint_alt(str,sv.average,sv.error);
1275
printf("chan=%d ave=%s\n",adc,str);
1280
int read_chan2( calibration_setup_t *setup, char *s,int adc,int range)
1285
my_sv_init(&sv, setup, setup->ad_subdev,CR_PACK(adc,range,AREF_OTHER));
1287
n=new_sv_measure( setup->dev, &sv);
1289
return sci_sprint_alt(s,sv.average,sv.error);
1293
void set_ao(comedi_t *dev,int subdev,int chan,int range,double value)
1295
comedi_range *r = comedi_get_range(dev,subdev,chan,range);
1296
lsampl_t maxdata = comedi_get_maxdata(dev,subdev,chan);
1299
data = comedi_from_phys(value,r,maxdata);
1301
comedi_data_write(dev,subdev,chan,range,AREF_GROUND,data);
1305
int my_sv_init( new_sv_t *sv, const calibration_setup_t *setup, int subdev,
1306
unsigned int chanspec )
1309
retval = new_sv_init( sv, setup->dev, subdev, chanspec );
1310
sv->settling_time_ns = setup->sv_settling_time_ns;
1311
sv->order = setup->sv_order;
1315
int new_sv_init(new_sv_t *sv,comedi_t *dev,int subdev,unsigned int chanspec)
1317
memset(sv,0,sizeof(*sv));
1320
//sv->t.flags=TRIG_DITHER;
1321
sv->chanspec = chanspec;
1323
//sv->chanlist[0]=CR_PACK(chan,range,aref);
1325
sv->maxdata=comedi_get_maxdata(dev,subdev,CR_CHAN(chanspec));
1326
sv->rng=comedi_get_range(dev,subdev,
1327
CR_CHAN(chanspec), CR_RANGE(chanspec));
1334
int new_sv_measure( comedi_t *dev, new_sv_t *sv)
1343
data=malloc(sizeof(lsampl_t)*n);
1346
perror( __FUNCTION__ );
1350
ret = comedi_data_read_hint(dev, sv->subd, sv->chanspec, 0, 0);
1352
printf("hint barf\n");
1355
comedi_nanodelay(dev, sv->settling_time_ns);
1357
ret = comedi_data_read_n(dev, sv->subd, sv->chanspec, 0, 0, data, n);
1365
for(i = 0; i < n; i++){
1366
x = comedi_to_phys(data[i], sv->rng, sv->maxdata);
1373
sv->stddev=sqrt( ( ( n + 1 ) / n ) * ( s2 - s * s ) );
1374
sv->error=sv->stddev / sqrt( n );
1384
int new_sv_measure_order( comedi_t *dev, new_sv_t *sv,int order)
1392
data=malloc(sizeof(lsampl_t)*n);
1395
perror( __FUNCTION__ );
1399
ret = comedi_data_read_n(dev, sv->subd, sv->chanspec, 0, 0, data, n);
1401
printf("barf order\n");
1407
for(i = 0; i < n; i++){
1408
x = comedi_to_phys(data[i], sv->rng, sv->maxdata);
1415
sv->stddev=sqrt( ( ( n + 1 ) / n ) * ( s2 - s * s ) );
1416
sv->error=sv->stddev / sqrt( n );
1428
/* linear fitting */
1430
int calculate_residuals(linear_fit_t *l);
1432
int linear_fit_monotonic(linear_fit_t *l)
1445
for(i=0;i<l->n;i++){
1449
if(isnan(y))continue;
1451
if(l->y_data[i]<l->min)l->min=l->y_data[i];
1452
if(l->y_data[i]>l->max)l->max=l->y_data[i];
1459
sxp=l->sxx-l->sx*l->sx/l->s1;
1461
l->ave_x=l->sx/l->s1;
1462
l->ave_y=l->sy/l->s1;
1463
l->slope=(l->s1*l->sxy-l->sx*l->sy)/(l->s1*l->sxx-l->sx*l->sx);
1464
l->err_slope=l->yerr/sqrt(sxp);
1465
l->err_ave_y=l->yerr/sqrt(l->s1);
1467
calculate_residuals(l);
1472
int calculate_residuals(linear_fit_t *l)
1475
double res,sum_res2;
1479
for(i=0;i<l->n;i++){
1480
x=l->x0+i*l->dx-l->ave_x;
1483
if(isnan(y))continue;
1485
res=l->ave_y+l->slope*x-y;
1489
l->S_min=sum_res2/(l->yerr*l->yerr);
1495
double linear_fit_func_y(linear_fit_t *l,double x)
1497
return l->ave_y+l->slope*(x-l->ave_x);
1500
double linear_fit_func_x(linear_fit_t *l,double y)
1502
return l->ave_x+(y-l->ave_y)/l->slope;
1505
void dump_curve(linear_fit_t *l)
1507
static int dump_number=0;
1511
printf("start dump %d\n",dump_number);
1512
for(i=0;i<l->n;i++){
1513
x=l->x0+i*l->dx-l->ave_x;
1515
printf("D%d: %d %g %g %g\n",dump_number,i,y,
1516
l->ave_y+l->slope*x,
1517
l->ave_y+l->slope*x-y);
1519
printf("end dump\n");
1524
/* printing of scientific numbers (with errors) */
1526
int sci_sprint(char *s,double x,double y)
1535
errsig = floor(log10(y));
1536
maxsig = floor(log10(x));
1537
mindigit = pow(10,errsig);
1539
if(maxsig<errsig)maxsig=errsig;
1541
sigfigs = maxsig-errsig+2;
1543
mantissa = x*pow(10,-maxsig);
1544
error = y*pow(10,-errsig+1);
1546
return sprintf(s,"%0.*f(%2.0f)e%d",sigfigs-1,mantissa,error,maxsig);
1549
int sci_sprint_alt(char *s,double x,double y)
1558
errsig = floor(log10(fabs(y)));
1559
maxsig = floor(log10(fabs(x)));
1560
mindigit = pow(10,errsig);
1562
if(maxsig<errsig)maxsig=errsig;
1564
sigfigs = maxsig-errsig+2;
1566
mantissa = x*pow(10,-maxsig);
1567
error = y*pow(10,-errsig+1);
1570
return sprintf(s,"%g",x);
1572
if(errsig==1 && maxsig<4 && maxsig>1){
1573
return sprintf(s,"%0.0f(%2.0f)",x,error);
1575
if(maxsig<=0 && maxsig>=-2){
1576
return sprintf(s,"%0.*f(%2.0f)",sigfigs-1-maxsig,
1577
mantissa*pow(10,maxsig),error);
1579
return sprintf(s,"%0.*f(%2.0f)e%d",sigfigs-1,mantissa,error,maxsig);
1582
double very_low_target( comedi_t *dev, unsigned int subdevice,
1583
unsigned int channel, unsigned int range )
1585
comedi_range *range_ptr;
1588
range_ptr = comedi_get_range( dev, subdevice, channel, range );
1589
assert( range_ptr != NULL );
1590
max_data = comedi_get_maxdata( dev, subdevice, 0 );
1591
assert( max_data > 0 );
1593
return comedi_to_phys( 1, range_ptr, max_data ) / 2.0;
1596
double fractional_offset( calibration_setup_t *setup, int subdevice,
1597
unsigned int channel, unsigned int range, int obs )
1599
comedi_range *range_ptr;
1602
unsigned int chanspec;
1605
if( subdevice < 0 || obs < 0 ) return 0.0;
1607
chanspec = setup->observables[obs].observe_insn.chanspec;
1608
target = setup->observables[obs].target;
1610
range_ptr = comedi_get_range( setup->dev, subdevice, channel, range );
1611
assert( range_ptr != NULL );
1613
comedi_set_global_oor_behavior( COMEDI_OOR_NUMBER );
1614
preobserve( setup, obs);
1616
my_sv_init( &sv, setup, setup->ad_subdev, chanspec );
1617
new_sv_measure( setup->dev, &sv );
1618
reading = sv.average;
1620
return ( reading - target ) / ( range_ptr->max - range_ptr->min );
1623
double get_tolerance( calibration_setup_t *setup, int subdevice,
1628
if( subdevice < 0 ) return INFINITY;
1630
maxdata = comedi_get_maxdata( setup->dev, subdevice, 0 );
1631
assert( maxdata > 0 );
1632
return num_bits / maxdata;