#define _GNU_SOURCE
+#include <assert.h>
+#include <math.h>
#include <stdlib.h>
-#include <comedilib.h>
-#include <libinternal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include "libinternal.h"
-static int ph_extract_element( const char *file_path, const char *element,
- char *result, unsigned int result_size )
-{
- char perl_prog[ 1024 ];
- FILE *perl_stdout;
- int retval;
+static int set_calibration( comedi_t *dev, const comedi_calibration_t *parsed_file,
+ unsigned int cal_index );
- snprintf( perl_prog, sizeof( perl_prog ),
- "perl -e '"
- "use strict;"
- "use warnings;"
- "my $hash;"
- "my $cal;"
- "$hash = `cat %s`;"
- "eval \"\\$cal = $hash;\";"
- "print $cal->%s;"
- "'",
- file_path, element );
-
- perl_stdout = popen( perl_prog, "r");
- if( perl_stdout == NULL )
- {
- fprintf( stderr, "popen() failed in ph_extract_element()\n" );
+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( fgets( result, result_size, perl_stdout ) == NULL )
+ if( strcmp( comedi_get_board_name( dev ), parsed_file->board_name ) )
{
- fprintf( stderr, "fgets() returned NULL in ph_extract_element()\n" );
+ COMEDILIB_DEBUG( 3, "board name does not match '%s' from calibration file\n",
+ parsed_file->board_name );
return -1;
}
- retval = pclose( perl_stdout );
- if( retval )
+ 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++ )
{
- fprintf( stderr, "perl returned error %i\n in ph_extract_element()", retval );
- return -1;
+ if( parsed_file->settings[ cal_index ].channels[ i ] == channel )
+ return 1;
}
return 0;
}
-//EXPORT_SYMBOL(comedi_set_calibration,0.7.20);
-int comedi_set_calibration( comedi_t *dev, const char *cal_file_path,
+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 )
{
- struct stat file_stats;
- FILE *cal_file;
+ int num_cals, i, retval;
+ int found_cal = 0;
- if( cal_file_path )
- cal_file = fopen( cal_file_path, "r" );
- else
+ num_cals = parsed_file->num_settings;
+
+ for( i = 0; i < num_cals; i++ )
{
- char *file_path;
+ 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;
- if( fstat( comedi_fileno( dev ), &file_stats ) < 0 )
+ 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 )
{
- fprintf( stderr, "failed to get file stats of comedi device file\n" );
- return -1;
+ *name = '-';
+ name++;
}
+ }
+}
- asprintf( &file_path, "/etc/comedi/calibrations/%s_0x%lx",
- comedi_get_board_name( dev ),
- ( unsigned long ) file_stats.st_ino );
- cal_file = fopen( file_path, "r" );
- free( file_path );
+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 );
}
- if( cal_file == NULL )
+ parsed_file = comedi_parse_calibration_file( file_path );
+ if( parsed_file == NULL )
{
- fprintf( stderr, "failed to open calibration file\n" );
+ 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));
+}