comedilib/comedi_calibrate/cal_common.c
2004-08-02 00:09:51 +00:00

490 lines
18 KiB
C

/***************************************************************************
cal_common.c - shared calibration routines
-------------------
begin : Fri May 2, 2003
copyright : (C) 2003 by Frank Mori Hess
email : fmhess@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by *
* the Free Software Foundation; either version 2.1 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#define _GNU_SOURCE
#include "calib.h"
#include <assert.h>
#include <stdlib.h>
#include <math.h>
void generic_do_cal( calibration_setup_t *setup,
comedi_calibration_setting_t *saved_cal, int observable, int caldac )
{
if( caldac < 0 || observable < 0 ) return;
cal_binary( setup, observable, caldac );
sc_push_caldac( saved_cal, setup->caldacs[ caldac ] );
}
void generic_do_relative( calibration_setup_t *setup,
comedi_calibration_setting_t *saved_cal, int observable1, int observable2, int caldac )
{
if( caldac < 0 || observable1 < 0 || observable2 < 0 ) return;
cal_relative_binary( setup, observable1, observable2, caldac );
sc_push_caldac( saved_cal, setup->caldacs[ caldac ] );
}
void generic_do_linearity( calibration_setup_t *setup,
comedi_calibration_setting_t *saved_cal, int observable1, int observable2,
int observable3, int caldac )
{
if( caldac < 0 || observable1 < 0 || observable2 < 0 || observable3 < 0 )
return;
cal_linearity_binary( setup, observable1, observable2, observable3, caldac );
sc_push_caldac( saved_cal, setup->caldacs[ caldac ] );
}
void generic_peg( calibration_setup_t *setup, int observable, int caldac,
int maximize )
{
if( caldac < 0 || observable < 0 ) return;
peg_binary( setup, observable, caldac, maximize );
}
void generic_prep_adc_caldacs( calibration_setup_t *setup,
const generic_layout_t *layout, unsigned int channel, unsigned int range )
{
int retval;
if( setup->ad_subdev < 0 ) return;
if( setup->old_calibration == NULL )
{
reset_caldac( setup, layout->adc_offset( channel ) );
reset_caldac( setup, layout->adc_gain( channel ) );
reset_caldac( setup, layout->adc_offset_fine( channel ) );
reset_caldac( setup, layout->adc_gain_fine( channel ) );
reset_caldac( setup, layout->adc_postgain_offset( channel ) );
}else
{
retval = comedi_apply_parsed_calibration( setup->dev, setup->ad_subdev,
channel, range, AREF_GROUND, setup->old_calibration );
if( retval < 0 )
{
DPRINT( 0, "Failed to apply existing calibration, reseting dac caldacs.\n" );
reset_caldac( setup, layout->adc_offset( channel ) );
reset_caldac( setup, layout->adc_gain( channel ) );
reset_caldac( setup, layout->adc_offset_fine( channel ) );
reset_caldac( setup, layout->adc_gain_fine( channel ) );
reset_caldac( setup, layout->adc_postgain_offset( channel ) );
}
}
}
void generic_prep_dac_caldacs( calibration_setup_t *setup,
const generic_layout_t *layout, unsigned int channel, unsigned int range )
{
int retval;
if( setup->da_subdev < 0 ) return;
if( setup->old_calibration == NULL )
{
reset_caldac( setup, layout->dac_offset( channel ) );
reset_caldac( setup, layout->dac_gain( channel ) );
reset_caldac( setup, layout->dac_linearity( channel ) );
reset_caldac( setup, layout->dac_offset_fine( channel ) );
reset_caldac( setup, layout->dac_gain_fine( channel ) );
reset_caldac( setup, layout->dac_linearity_fine( channel ) );
}else
{
retval = comedi_apply_parsed_calibration( setup->dev, setup->da_subdev,
channel, range, AREF_GROUND, setup->old_calibration );
if( retval < 0 )
{
DPRINT( 0, "Failed to apply existing calibration, reseting dac caldacs.\n" );
reset_caldac( setup, layout->dac_offset( channel ) );
reset_caldac( setup, layout->dac_gain( channel ) );
reset_caldac( setup, layout->dac_linearity( channel ) );
reset_caldac( setup, layout->dac_offset_fine( channel ) );
reset_caldac( setup, layout->dac_gain_fine( channel ) );
reset_caldac( setup, layout->dac_linearity_fine( channel ) );
}
}
}
static void generic_prep_adc_for_dac( calibration_setup_t *setup,
comedi_calibration_t *calibration, int observable )
{
unsigned int adc_channel, adc_range;
int chanspec;
if( observable < 0 ) return;
chanspec = setup->observables[ observable ].observe_insn.chanspec;
adc_channel = CR_CHAN( chanspec );
adc_range = CR_RANGE( chanspec );
comedi_apply_parsed_calibration( setup->dev, setup->ad_subdev,
adc_channel, adc_range, 0, calibration );
}
static int dac_cal_is_good( calibration_setup_t *setup, const generic_layout_t *layout,
unsigned int channel, unsigned int range )
{
if( fabs( fractional_offset( setup, setup->da_subdev, channel, range,
layout->dac_ground_observable( setup, channel, range ) ) ) > layout->dac_fractional_tolerance )
return 0;
else if( fabs( fractional_offset( setup, setup->da_subdev, channel, range,
layout->dac_high_observable( setup, channel, range ) ) ) > layout->dac_fractional_tolerance )
return 0;
return 1;
}
static void generic_do_dac_channel( calibration_setup_t *setup, const generic_layout_t *layout ,
comedi_calibration_t *calibration, comedi_calibration_setting_t *current_cal,
unsigned int channel, unsigned int range )
{
static const int max_iterations = 4;
int i;
current_cal->subdevice = setup->da_subdev;
generic_prep_adc_for_dac( setup, calibration,
layout->dac_ground_observable( setup, channel, range ) );
for( i = 0; i < max_iterations; i++ )
{
generic_do_linearity(setup, current_cal, layout->dac_ground_observable( setup, channel, range ),
layout->dac_mid_observable( setup, channel, range ),
layout->dac_high_observable( setup, channel, range ),
layout->dac_linearity(channel));
generic_do_relative( setup, current_cal, layout->dac_high_observable( setup, channel, range ),
layout->dac_ground_observable( setup, channel, range ),layout->dac_gain( channel ) );
generic_do_cal( setup, current_cal, layout->dac_ground_observable( setup, channel, range ),
layout->dac_offset( channel ) );
generic_do_linearity(setup, current_cal, layout->dac_ground_observable( setup, channel, range ),
layout->dac_mid_observable( setup, channel, range ),
layout->dac_high_observable( setup, channel, range ),
layout->dac_linearity_fine(channel));
generic_do_relative( setup, current_cal, layout->dac_high_observable( setup, channel, range ),
layout->dac_ground_observable( setup, channel, range ), layout->dac_gain_fine( channel ) );
generic_do_cal( setup, current_cal, layout->dac_ground_observable( setup, channel, range ),
layout->dac_offset_fine( channel ) );
if( dac_cal_is_good( setup, layout, channel, range ) ) break;
}
if( i == max_iterations )
DPRINT(0, "WARNING: unable to calibrate dac channel %i, range %i to desired %g tolerance\n",
channel, range, layout->dac_fractional_tolerance );
sc_push_channel( current_cal, channel );
sc_push_range( current_cal, range );
sc_push_aref( current_cal, SC_ALL_AREFS );
}
static int adc_cal_is_good( calibration_setup_t *setup, const generic_layout_t *layout,
unsigned int channel, unsigned int range )
{
if( fabs( fractional_offset( setup, setup->ad_subdev, channel, range,
layout->adc_ground_observable( setup, channel, range ) ) ) > layout->adc_fractional_tolerance )
return 0;
else if( fabs( fractional_offset( setup, setup->ad_subdev, channel, range,
layout->adc_high_observable( setup, channel, range ) ) ) > layout->adc_fractional_tolerance )
return 0;
return 1;
}
static void generic_do_adc_channel( calibration_setup_t *setup, const generic_layout_t *layout,
comedi_calibration_setting_t *current_cal, unsigned int channel, unsigned int range )
{
static const int max_iterations = 4;
int i;
current_cal->subdevice = setup->ad_subdev;
for( i = 0; i < max_iterations; i++ )
{
generic_do_relative( setup, current_cal, layout->adc_high_observable( setup, channel, range ),
layout->adc_ground_observable( setup, channel, range ), layout->adc_gain( channel ) );
generic_do_cal( setup, current_cal, layout->adc_ground_observable( setup, channel, range ),
layout->adc_offset( channel ) );
generic_do_relative( setup, current_cal, layout->adc_high_observable( setup, channel, range ),
layout->adc_ground_observable( setup, channel, range ), layout->adc_gain_fine( channel ) );
generic_do_cal( setup, current_cal, layout->adc_ground_observable( setup, channel, range ),
layout->adc_offset_fine( channel ) );
if( adc_cal_is_good( setup, layout, channel, range ) ) break;
}
if( i == max_iterations )
DPRINT(0, "WARNING: unable to calibrate adc channel %i, range %i to desired %g tolerance\n",
channel, range, layout->adc_fractional_tolerance );
sc_push_channel( current_cal, channel );
sc_push_range( current_cal, range );
sc_push_aref( current_cal, SC_ALL_AREFS );
}
static void generic_do_adc_postgain_offset( calibration_setup_t *setup, const generic_layout_t *layout,
comedi_calibration_setting_t *current_cal, unsigned int channel, int unipolar )
{
int lowgain, highgain;
int bip_lowgain;
current_cal->subdevice = setup->ad_subdev;
bip_lowgain = get_bipolar_lowgain( setup->dev, setup->ad_subdev );
if( unipolar )
{
lowgain = get_unipolar_lowgain( setup->dev, setup->ad_subdev );
highgain = get_unipolar_highgain( setup->dev, setup->ad_subdev );
}else
{
lowgain = bip_lowgain;
highgain = get_bipolar_highgain( setup->dev, setup->ad_subdev );
}
generic_prep_adc_caldacs( setup, layout, channel, highgain );
if( unipolar )
{
/* Need to make sure we aren't stuck on zero for unipolar,
* by setting pregain offset to maximum. Use bipolar lowgain
* for pegs to make sure we aren't out-of-range. */
generic_peg( setup, layout->adc_ground_observable( setup, channel, bip_lowgain ),
layout->adc_offset( channel ), 1 );
generic_peg( setup, layout->adc_ground_observable( setup, channel, bip_lowgain ),
layout->adc_offset_fine( channel ), 1 );
}
generic_do_relative( setup, current_cal, layout->adc_ground_observable( setup, channel, lowgain ),
layout->adc_ground_observable( setup, channel, highgain ), layout->adc_postgain_offset( channel ) );
sc_push_channel( current_cal, channel );
sc_push_aref( current_cal, SC_ALL_AREFS );
}
int generic_cal_by_channel_and_range( calibration_setup_t *setup,
const generic_layout_t *layout )
{
int range, channel, num_ai_ranges, num_ai_channels, num_ao_ranges,
num_ao_channels, retval, num_ai_calibrations;
comedi_calibration_setting_t *current_cal;
assert( comedi_range_is_chan_specific( setup->dev, setup->ad_subdev ) == 0 );
num_ai_ranges = comedi_get_n_ranges( setup->dev, setup->ad_subdev, 0 );
if( num_ai_ranges < 0 ) return -1;
num_ai_channels = comedi_get_n_channels( setup->dev, setup->ad_subdev );
if( num_ai_channels < 0 ) return -1;
if(setup->da_subdev >= 0 && setup->do_output )
{
assert( comedi_range_is_chan_specific( setup->dev, setup->da_subdev ) == 0 );
num_ao_ranges = comedi_get_n_ranges( setup->dev, setup->da_subdev, 0 );
if( num_ao_ranges < 0 ) return -1;
num_ao_channels = comedi_get_n_channels( setup->dev, setup->da_subdev );
if( num_ao_channels < 0 ) return -1;
}else
num_ao_ranges = num_ao_channels = 0;
num_ai_calibrations = num_ai_ranges * num_ai_channels;
for( channel = 0; channel < num_ai_channels; channel++ )
{
int postgain_bip, postgain_unip;
if( layout->adc_postgain_offset( 0 ) >= 0 )
{
/* bipolar postgain */
current_cal = sc_alloc_calibration_setting( setup );
generic_do_adc_postgain_offset( setup, layout, current_cal, channel, 0 );
for( range = 0; range < num_ai_ranges; range++ )
if( is_bipolar( setup->dev, setup->ad_subdev, channel, range ) )
sc_push_range( current_cal, range );
postgain_bip = setup->caldacs[ layout->adc_postgain_offset( channel ) ].current;
/* unipolar postgain */
if( layout->do_adc_unipolar_postgain )
{
current_cal = sc_alloc_calibration_setting( setup );
generic_do_adc_postgain_offset( setup, layout, current_cal, channel, 1 );
}
for( range = 0; range < num_ai_ranges; range++ )
if( is_unipolar( setup->dev, setup->ad_subdev, channel, range ) )
sc_push_range( current_cal, range );
postgain_unip = setup->caldacs[ layout->adc_postgain_offset( channel ) ].current;
}else
postgain_bip = postgain_unip = -1;
for( range = 0; range < num_ai_ranges; range++ )
{
current_cal = sc_alloc_calibration_setting( setup );
generic_prep_adc_caldacs( setup, layout, channel, range );
if( is_unipolar( setup->dev, setup->ad_subdev, channel, range ) )
update_caldac( setup, layout->adc_postgain_offset( channel ), postgain_unip );
else
update_caldac( setup, layout->adc_postgain_offset( channel ), postgain_bip );
generic_do_adc_channel( setup, layout, current_cal, channel, range );
}
}
for( channel = 0; channel < num_ao_channels; channel++ )
{
for( range = 0; range < num_ao_ranges; range++ )
{
current_cal = sc_alloc_calibration_setting( setup );
generic_prep_dac_caldacs( setup, layout, channel, range );
generic_do_dac_channel( setup, layout, setup->new_calibration,
current_cal, channel, range );
}
}
retval = write_calibration_file( setup );
return retval;
}
int generic_cal_by_range( calibration_setup_t *setup,
const generic_layout_t *layout )
{
int channel, range, num_ai_ranges, num_ao_ranges,
num_ao_channels, retval;
comedi_calibration_setting_t *current_cal;
int postgain_bip, postgain_unip;
assert( comedi_range_is_chan_specific( setup->dev, setup->ad_subdev ) == 0 );
num_ai_ranges = comedi_get_n_ranges( setup->dev, setup->ad_subdev, 0 );
if( num_ai_ranges < 0 ) return -1;
if(setup->da_subdev >= 0 && setup->do_output )
{
assert( comedi_range_is_chan_specific( setup->dev, setup->da_subdev ) == 0 );
num_ao_ranges = comedi_get_n_ranges( setup->dev, setup->da_subdev, 0 );
if( num_ao_ranges < 0 ) return -1;
num_ao_channels = comedi_get_n_channels( setup->dev, setup->da_subdev );
if( num_ao_channels < 0 ) return -1;
}else
num_ao_ranges = num_ao_channels = 0;
if( layout->adc_postgain_offset( 0 ) >= 0 )
{
/* bipolar postgain */
current_cal = sc_alloc_calibration_setting( setup );
generic_do_adc_postgain_offset( setup, layout, current_cal, 0, 0 );
sc_push_channel( current_cal, SC_ALL_CHANNELS );
for( range = 0; range < num_ai_ranges; range++ )
if( is_bipolar( setup->dev, setup->ad_subdev, 0, range ) )
sc_push_range( current_cal, range );
postgain_bip = setup->caldacs[ layout->adc_postgain_offset( 0 ) ].current;
/* unipolar postgain */
if( layout->do_adc_unipolar_postgain )
{
current_cal = sc_alloc_calibration_setting( setup );
generic_do_adc_postgain_offset( setup, layout, current_cal, 0, 1 );
sc_push_channel( current_cal, SC_ALL_CHANNELS );
}
for( range = 0; range < num_ai_ranges; range++ )
if( is_unipolar( setup->dev, setup->ad_subdev, 0, range ) )
sc_push_range( current_cal, range );
postgain_unip = setup->caldacs[ layout->adc_postgain_offset( 0 ) ].current;
}else
postgain_bip = postgain_unip = -1;
for( range = 0; range < num_ai_ranges; range++ )
{
current_cal = sc_alloc_calibration_setting( setup );
generic_prep_adc_caldacs( setup, layout, 0, range );
if( is_unipolar( setup->dev, setup->ad_subdev, 0, range ) )
update_caldac( setup, layout->adc_postgain_offset( 0 ), postgain_unip );
else
update_caldac( setup, layout->adc_postgain_offset( 0 ), postgain_bip );
generic_do_adc_channel( setup, layout, current_cal, 0, range );
sc_push_channel( current_cal, SC_ALL_CHANNELS );
}
for( channel = 0; channel < num_ao_channels; channel++ )
{
for( range = 0; range < num_ao_ranges; range++ )
{
current_cal = sc_alloc_calibration_setting( setup );
generic_prep_dac_caldacs( setup, layout, channel, range );
generic_do_dac_channel( setup, layout, setup->new_calibration,
current_cal, channel, range );
}
}
retval = write_calibration_file( setup );
return retval;
}
int generic_cal_ao(calibration_setup_t *setup,
const generic_layout_t *layout )
{
int channel, range, num_ao_ranges,
num_ao_channels, retval;
comedi_calibration_setting_t *current_cal;
if(setup->da_subdev >= 0 && setup->do_output)
{
assert( comedi_range_is_chan_specific( setup->dev, setup->da_subdev ) == 0 );
num_ao_ranges = comedi_get_n_ranges( setup->dev, setup->da_subdev, 0 );
if( num_ao_ranges < 0 ) return -1;
num_ao_channels = comedi_get_n_channels( setup->dev, setup->da_subdev );
if( num_ao_channels < 0 ) return -1;
}else
num_ao_ranges = num_ao_channels = 0;
for( channel = 0; channel < num_ao_channels; channel++ )
{
for( range = 0; range < num_ao_ranges; range++ )
{
current_cal = sc_alloc_calibration_setting( setup );
generic_prep_dac_caldacs( setup, layout, channel, range );
generic_do_dac_channel( setup, layout, setup->new_calibration,
current_cal, channel, range );
}
}
retval = write_calibration_file( setup );
return retval;
}
static int dummy_caldac( unsigned int channel )
{
return -1;
}
static int dummy_observable( const calibration_setup_t *setup,
unsigned int channel, unsigned int range )
{
return -1;
}
void init_generic_layout( generic_layout_t *layout )
{
layout->adc_offset = dummy_caldac;
layout->adc_offset_fine = dummy_caldac;
layout->adc_gain = dummy_caldac;
layout->adc_gain_fine = dummy_caldac;
layout->adc_postgain_offset = dummy_caldac;
layout->dac_offset = dummy_caldac;
layout->dac_offset_fine = dummy_caldac;
layout->dac_linearity = dummy_caldac;
layout->dac_linearity_fine = dummy_caldac;
layout->dac_gain = dummy_caldac;
layout->dac_gain_fine = dummy_caldac;
layout->adc_high_observable = dummy_observable;
layout->adc_ground_observable = dummy_observable;
layout->dac_high_observable = dummy_observable;
layout->dac_mid_observable = dummy_observable;
layout->dac_ground_observable = dummy_observable;
layout->adc_fractional_tolerance = INFINITY;
layout->adc_fractional_tolerance = INFINITY;
layout->do_adc_unipolar_postgain = 0;
}