comedilib/lib/calib.c
2011-01-05 15:02:44 +00:00

353 lines
9.3 KiB
C

/*
lib/calib.c
functions for setting calibration
Copyright (C) 2003 Frank Mori Hess <fmhess@users.sourceforge.net
This library 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, version 2.1
of the License.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
*/
#define _GNU_SOURCE
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include "libinternal.h"
static int set_calibration( comedi_t *dev, const comedi_calibration_t *parsed_file,
unsigned int cal_index );
static int check_cal_file( comedi_t *dev, const comedi_calibration_t *parsed_file )
{
if( strcmp( comedi_get_driver_name( dev ), parsed_file->driver_name ) )
{
COMEDILIB_DEBUG( 3, "driver name does not match '%s' from calibration file\n",
parsed_file->driver_name );
return -1;
}
if( strcmp( comedi_get_board_name( dev ), parsed_file->board_name ) )
{
COMEDILIB_DEBUG( 3, "board name does not match '%s' from calibration file\n",
parsed_file->board_name );
return -1;
}
return 0;
}
static inline int valid_channel( const comedi_calibration_t *parsed_file,
unsigned int cal_index, unsigned int channel )
{
int num_channels, i;
num_channels = parsed_file->settings[ cal_index ].num_channels;
if( num_channels == 0 ) return 1;
for( i = 0; i < num_channels; i++ )
{
if( parsed_file->settings[ cal_index ].channels[ i ] == channel )
return 1;
}
return 0;
}
static inline int valid_range( const comedi_calibration_t *parsed_file,
unsigned int cal_index, unsigned int range )
{
int num_ranges, i;
num_ranges = parsed_file->settings[ cal_index ].num_ranges;
if( num_ranges == 0 ) return 1;
for( i = 0; i < num_ranges; i++ )
{
if( parsed_file->settings[ cal_index ].ranges[ i ] == range )
return 1;
}
return 0;
}
static inline int valid_aref( const comedi_calibration_t *parsed_file,
unsigned int cal_index, unsigned int aref )
{
int num_arefs, i;
num_arefs = parsed_file->settings[ cal_index ].num_arefs;
if( num_arefs == 0 ) return 1;
for( i = 0; i < num_arefs; i++ )
{
if( parsed_file->settings[ cal_index ].arefs[ i ] == aref )
return 1;
}
return 0;
}
static int apply_calibration( comedi_t *dev, const comedi_calibration_t *parsed_file,
unsigned int subdev, unsigned int channel, unsigned int range, unsigned int aref )
{
int num_cals, i, retval;
int found_cal = 0;
num_cals = parsed_file->num_settings;
for( i = 0; i < num_cals; i++ )
{
if( parsed_file->settings[ i ].subdevice != subdev ) continue;
if( valid_range( parsed_file, i, range ) == 0 ) continue;
if( valid_channel( parsed_file, i, channel ) == 0 ) continue;
if( valid_aref( parsed_file, i, aref ) == 0 ) continue;
retval = set_calibration( dev, parsed_file, i );
if( retval < 0 ) return retval;
found_cal = 1;
}
if( found_cal == 0 )
{
COMEDILIB_DEBUG( 3, "failed to find matching calibration\n" );
return -1;
}
return 0;
}
static int set_calibration( comedi_t *dev, const comedi_calibration_t *parsed_file,
unsigned int cal_index )
{
int i, retval, num_caldacs;
num_caldacs = parsed_file->settings[ cal_index ].num_caldacs;
COMEDILIB_DEBUG( 4, "num_caldacs %i\n", num_caldacs );
for( i = 0; i < num_caldacs; i++ )
{
comedi_caldac_t caldac;
caldac = parsed_file->settings[ cal_index ].caldacs[ i ];
COMEDILIB_DEBUG( 4, "subdev %i, ch %i, val %i\n", caldac.subdevice,
caldac.channel,caldac.value);
retval = comedi_data_write( dev, caldac.subdevice, caldac.channel,
0, 0, caldac.value );
if( retval < 0 ) return retval;
}
return 0;
}
EXPORT_ALIAS_DEFAULT(_comedi_apply_parsed_calibration,comedi_apply_parsed_calibration,0.7.20);
int _comedi_apply_parsed_calibration( comedi_t *dev, unsigned int subdev, unsigned int channel,
unsigned int range, unsigned int aref, const comedi_calibration_t *calibration )
{
int retval;
if(!valid_dev(dev)) return -1;
retval = check_cal_file( dev, calibration );
if( retval < 0 ) return retval;
retval = apply_calibration( dev, calibration, subdev, channel, range, aref );
return retval;
}
/* munge characters in board name that will cause problems with file paths */
static void fixup_board_name( char *name )
{
while( ( name = strchr( name, '/' ) ) )
{
if( name )
{
*name = '-';
name++;
}
}
}
EXPORT_ALIAS_DEFAULT(_comedi_get_default_calibration_path,comedi_get_default_calibration_path,0.7.20);
char* _comedi_get_default_calibration_path( comedi_t *dev )
{
struct stat file_stats;
char *file_path;
const char *temp;
char *board_name;
const char *driver_name;
int err;
if(!valid_dev(dev)) return NULL;
if( fstat( comedi_fileno( dev ), &file_stats ) < 0 )
{
COMEDILIB_DEBUG( 3, "failed to get file stats of comedi device file\n" );
return NULL;
}
driver_name = comedi_get_driver_name( dev );
if( driver_name == NULL )
{
return NULL;
}
temp = comedi_get_board_name( dev );
if( temp == NULL )
{
return NULL;
}
board_name = strdup( temp );
fixup_board_name( board_name );
err = asprintf( &file_path, LOCALSTATEDIR "/lib/comedi/calibrations/%s_%s_comedi%li",
driver_name, board_name, ( unsigned long ) minor( file_stats.st_rdev ) );
free( board_name );
if( err < 0 )
{
return NULL;
}
return file_path;
}
EXPORT_ALIAS_DEFAULT(_comedi_apply_calibration,comedi_apply_calibration,0.7.20);
int _comedi_apply_calibration( comedi_t *dev, unsigned int subdev, unsigned int channel,
unsigned int range, unsigned int aref, const char *cal_file_path )
{
char file_path[ 1024 ];
int retval;
comedi_calibration_t *parsed_file;
if( cal_file_path )
{
strncpy( file_path, cal_file_path, sizeof( file_path ) );
}else
{
char *temp;
temp = comedi_get_default_calibration_path( dev );
if( temp == NULL ) return -1;
strncpy( file_path, temp, sizeof( file_path ) );
free( temp );
}
parsed_file = comedi_parse_calibration_file( file_path );
if( parsed_file == NULL )
{
COMEDILIB_DEBUG( 3, "failed to parse calibration file\n" );
return -1;
}
retval = comedi_apply_parsed_calibration( dev, subdev, channel, range, aref, parsed_file );
comedi_cleanup_calibration( parsed_file );
return retval;
}
EXPORT_ALIAS_DEFAULT(_comedi_get_hardcal_converter, comedi_get_hardcal_converter, 0.8.0);
int _comedi_get_hardcal_converter(
comedi_t *dev, unsigned subdevice, unsigned channel, unsigned range,
enum comedi_conversion_direction direction,
comedi_polynomial_t* polynomial)
{
comedi_range *range_ptr = comedi_get_range(dev, subdevice, channel, range);
lsampl_t maxdata;
if(range_ptr == NULL)
{
return -1;
}
maxdata = comedi_get_maxdata(dev, subdevice, channel);
if(maxdata == 0)
{
return -1;
}
polynomial->order = 1;
switch(direction)
{
case COMEDI_TO_PHYSICAL:
polynomial->expansion_origin = 0.;
polynomial->coefficients[0] = range_ptr->min;
polynomial->coefficients[1] = (range_ptr->max - range_ptr->min) / maxdata;
break;
case COMEDI_FROM_PHYSICAL:
polynomial->expansion_origin = range_ptr->min;
polynomial->coefficients[0] = 0.;
polynomial->coefficients[1] = maxdata / (range_ptr->max - range_ptr->min);
break;
}
return 0;
}
EXPORT_ALIAS_DEFAULT(_comedi_get_softcal_converter, comedi_get_softcal_converter, 0.8.0);
int _comedi_get_softcal_converter(
unsigned subdevice, unsigned channel, unsigned range,
enum comedi_conversion_direction direction,
const comedi_calibration_t *calibration, comedi_polynomial_t* polynomial)
{
unsigned i;
for(i = 0; i < calibration->num_settings; ++i)
{
if(calibration->settings[i].subdevice != subdevice) continue;
if(valid_channel(calibration, i, channel) == 0) continue;
if(valid_range(calibration, i, range) == 0) continue;
switch(direction)
{
case COMEDI_TO_PHYSICAL:
if(calibration->settings[i].soft_calibration.to_phys == NULL)
{
continue;
}
*polynomial = *calibration->settings[i].soft_calibration.to_phys;
break;
case COMEDI_FROM_PHYSICAL:
if(calibration->settings[i].soft_calibration.from_phys == NULL)
{
continue;
}
*polynomial = *calibration->settings[i].soft_calibration.from_phys;
break;
}
return 0;
}
return -1;
}
static double apply_polynomial(const comedi_polynomial_t *polynomial, double input)
{
double value = 0.;
double term = 1.;
unsigned i;
assert(polynomial->order < COMEDI_MAX_NUM_POLYNOMIAL_COEFFICIENTS);
for(i = 0; i <= polynomial->order; ++i)
{
value += polynomial->coefficients[i] * term;
term *= input - polynomial->expansion_origin;
}
return value;
}
EXPORT_ALIAS_DEFAULT(_comedi_to_physical, comedi_to_physical, 0.8.0);
double _comedi_to_physical(lsampl_t data,
const comedi_polynomial_t *conversion_polynomial)
{
return apply_polynomial(conversion_polynomial, data);
}
EXPORT_ALIAS_DEFAULT(_comedi_from_physical, comedi_from_physical, 0.8.0);
lsampl_t _comedi_from_physical(double data,
const comedi_polynomial_t *conversion_polynomial)
{
return nearbyint(apply_polynomial(conversion_polynomial, data));
}