Mercurial > hg > aim92
diff tools/scale.c @ 0:5242703e91d3 tip
Initial checkin for AIM92 aimR8.2 (last updated May 1997).
author | tomwalters |
---|---|
date | Fri, 20 May 2011 15:19:45 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/scale.c Fri May 20 15:19:45 2011 +0100 @@ -0,0 +1,271 @@ +/* + scale.c shift and scale a data stream by given constants or to + ------- fit a given range (top - bottom). + + Shifting and scaling is done for all input x using the formula: + y = ( x - shift ) * scale + + If only a shift term is given, then the default scale is 1.0 + If only a scale factor is given, then the default shift is 0. + + If neither shift nor scale are given, then appropriate values are + chosen so that the output fits the range specified by `top' and `bottom'. + For an input which ranges between extreme values of `max' and `min', the + shift and scale factors to give a required range between `top' and `bottom' + are found by simultaneously solving: + top = ( max - shift ) * scale + bottom = ( min - shift ) * scale + + If top < bottom then the input is inverted in the resulting range. + + The input stream depends upon the `type' option. The output datatype is + the same as that selected for input. + With no filename arguments, data is expected on the stdin, and the + scaled result is written on the stdout. Otherwise each given filename is + processed in turn. When the `output' option is "off" output overwrites the + respective input file. (The respective output for the stdin is the stdout). + Otherwise all scaled input files are written to the given output file + (which is the stdout by default). + + The `range' option sets the start and duration of the process. + Its arguments are of the form: range=a-b (where start=a and duration=b-a+1) + or: range=a (where start=a and duration=1 ) + The arguments can be in time units (ms, s) or samples (no units), and both + "min" (start of file) and "max" (end of file) are recognised. + Samples in a file are numbered 0,1,2,...,max. + + Examples: + +1. Invert the range of a given pulse train to obtain negative-going pulses. + (Two ways): + +ptrain amp=512 | scale top=-512 bot=0 | x11plot +ptrain amp=512 | scale scale=-1 | x11plot + +*/ + + +#include <stdio.h> +#include <math.h> +#include "options.h" +#include "units.h" +#include "strmatch.h" + +char applic[] = "waveform shifting and scaling: y = (x - shift) * scale" ; + +static char *helpstr, *debugstr, *sampstr, *rangestr, *outstr ; +static char *topstr, *botstr, *scalestr, *typestr, *sizestr ; +static char *shiftstr, *normstr ; + +static Options option[] = { + { "help" , "off" , &helpstr , "help" , DEBUG }, + { "debug" , "off" , &debugstr , "debugging switch" , DEBUG }, + { "samplerate", "20kHz" , &sampstr , "samplerate " , VAL }, + { "range" , "0-max" , &rangestr , "start-finish limits in data" , VAL }, + { "top" , "1000" , &topstr , "max of scaled data" , VAL }, + { "bottom" , "-1000" , &botstr , "min of scaled data" , VAL }, + { "shift" , "off" , &shiftstr , "shift term" , VAL }, + { "scale" , "off" , &scalestr , "scale factor" , VAL }, + { "normalize" , "off" , &normstr , "set zero mean and unit std dev", VAL }, + { "type" , "short" , &typestr , "datatype" , VAL }, + { "output" , "stdout" , &outstr , "output filename (off = overwrite input)" , VAL }, + { "SIZE" , "262144p" , &sizestr , "buffer size (s, ms, or p)" , SVAL }, + ( char * ) 0 } ; + + +int samplerate ; +int type ; /* datatype index */ +int bytes ; +int SIZE ; /* buffer size */ + +int FIT_TO_RANGE = 0 ; +int NORMALIZE = 0 ; + +float shift, scale ; + +float *data ; + +FILE *ofp ; + +main (argc, argv) +int argc; +char **argv; +{ + FILE *fp ; + int i, j, a, b, n ; + + i = getopts( option, argc, argv ) ; + if ( !isoff( helpstr ) ) + helpopts( helpstr, argv[0], applic, option ) ; + + samplerate = to_Hz( sampstr ) ; + SIZE = to_p( sizestr, samplerate ) ; + + if ( ( type = typeindex( typestr ) ) < 0 ) { + fprintf( stderr, "scale: bad type [%s]\n", typestr ) ; + exit( 1 ) ; + } + bytes = typebytes( type ) ; + + if ( range( rangestr, &a, &b, samplerate ) == 0 ) { + fprintf(stderr,"scale: bad range [%s]\n", rangestr ) ; + exit( 1 ) ; + } + + + if ( ison( normstr ) ) NORMALIZE = 1 ; + else if ( isoff( shiftstr ) && isoff( scalestr ) ) FIT_TO_RANGE = 1 ; + else if ( isoff( shiftstr ) ) { + shift = 0 ; + scale = atof( scalestr ) ; + } + else if ( isoff( scalestr ) ) { + shift = atof( shiftstr ) ; + scale = 1. ; + } + else { + shift = atof( shiftstr ) ; + scale = atof( scalestr ) ; + } + + if ( ( data = (float *)malloc( SIZE * sizeof(float) ) ) == NULL ) { + fprintf( stderr, "malloc out of space\n" ) ; + exit( 1 ) ; + } + + + do { + + if ( i == 0 ) fp = stdin ; + else if ( ( fp = fopen( argv[argc-i], "r" ) ) == (FILE *)0 ) { + fprintf( stderr,"scale: can't open %s\n", argv[argc-i] ) ; + exit( 1 ) ; + } + + if ( seekstart( a, bytes, fp ) < a ) { + fprintf( stderr,"scale: insufficient data in file\n" ) ; + exit( 1 ) ; + } + + for ( j = a, n = 0 ; ( j <= b || b == (-1) ) && n < SIZE && readitem( &data[n], type, 1, fp ) ; j++, n++ ) + ; + if ( n == SIZE ) + fprintf( stderr, "scale warning: file %s exceeded buffer size\n", argv[argc-i] ) ; + + fclose( fp ) ; + + + if ( NORMALIZE ) + getnorm( data, n, &shift, &scale ) ; + else if ( FIT_TO_RANGE ) + getrange( data, n, atof( topstr ), atof( botstr ), &shift, &scale ) ; + + + if ( isstr( outstr, "print" ) ) + printf( "%8.3f %8.3f \n", shift, scale ) ; + else { + open_output( argc, argv , i ) ; + for ( j = 0 ; j < n ; j++ ) { + data[j] = ( data[j] - shift ) * scale ; + writeitem( &data[j], type, 1, ofp ) ; + } + } + + } while ( --i > 0 ) ; + + fclose( ofp ) ; +} + + +/* +Open ouput file pointer, ofp. +Use the input filename (argv[argc-i]) if outstr is "off", otherwise use +the given name outstr. +*/ + +open_output( argc, argv , i ) +int argc ; +char **argv ; +int i ; +{ + static int first = 1 ; + + if ( first ) { + + if ( isoff( outstr ) ) { + if ( i == 0 ) ofp = stdout ; + else if ( ( ofp = fopen( argv[argc-i], "w" ) ) == (FILE *)0 ) { + fprintf( stderr,"scale: can't create %s\n", argv[argc-i] ) ; + exit( 1 ) ; + } + } + else if ( isstr( outstr, "stdout") ) ofp = stdout ; + else if ( ( ofp = fopen( outstr, "w" ) ) == (FILE *)0 ) { + fprintf( stderr,"scale: can't create %s\n", outstr ) ; + exit( 1 ) ; + } + first = 0 ; + } + + else { + if ( isoff( outstr ) ) { + fclose ( ofp ) ; + if ( i == 0 ) ofp = stdout ; + else if ( ( ofp = fopen( argv[argc-i], "w" ) ) == (FILE *)0 ) { + fprintf( stderr,"scale: can't create %s\n", argv[argc-i] ) ; + exit( 1 ) ; + } + } + } +} + + +/* +Return parameters `shift' and `scale' to normalize the data for zero mean +and unit std dev: + shift = mean + scale = 1 / sqrt(variance) +*/ + +getnorm( data, n, shift, scale ) +float *data ; +int n ; +float *shift, *scale ; +{ + int i ; + float sum = 0, sumsq = 0 ; + + for ( i = 0 ; i < n ; i++ ) { + sum += data[i] ; + sumsq += data[i] * data[i] ; + } + + *shift = sum / n ; + *scale = 1. / sqrt( sumsq / n - *shift * *shift ) ; +} + + +/* +Return parameters `shift' and `scale' to fit the given float data into the +amplitude range delimited by `top' and `bottom'. +*/ + +getrange( data, n, top, bottom, shift, scale ) +float *data ; +int n ; +float top, bottom ; +float *shift, *scale ; +{ + float min = 1e+8, max = (-1e+8) ; + int i ; + + for ( i = 0 ; i < n ; i++ ) { + if ( data[i] > max ) max = data[i] ; + if ( data[i] < min ) min = data[i] ; + } + + *scale = ( top - bottom ) / ( max - min ) ; + *shift = ( min * top - max * bottom ) / ( top - bottom ) ; +} + +