view 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 source
/*
  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 ) ;
}