/* lib/calib.c functions for setting calibration Copyright (C) 2003 Frank Mori Hess #include #include #include #include #include #include #include #include #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 ) { int retval; comedi_calibration_t *parsed_file; if( cal_file_path ) { parsed_file = comedi_parse_calibration_file( cal_file_path ); }else { char *file_path; file_path = comedi_get_default_calibration_path( dev ); if( file_path == NULL ) return -1; parsed_file = comedi_parse_calibration_file( file_path ); free( 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)); }