annotate tools/fft.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
rev   line source
tomwalters@0 1 /*
tomwalters@0 2 FFT program adapted from routines given in Numerical Recipes.
tomwalters@0 3
tomwalters@0 4 The output framewidth = ( input framewidth + padding ) / 2 spectral points.
tomwalters@0 5
tomwalters@0 6 Padding with zeroes is recommended to counter end-effects (See NR, p416).
tomwalters@0 7 It is also necessary since framewidth + padding must be a power of 2
tomwalters@0 8 (sample points at given rate).
tomwalters@0 9 This is because the fft method is "radix-2", and because the output is
tomwalters@0 10 symmetrical so that just half the points are unique.
tomwalters@0 11 Therefore each input frame is padded with zeroes to the next power of 2
tomwalters@0 12 larger than the input framewidth.
tomwalters@0 13
tomwalters@0 14 If necessary, extra padding can be enforced using the padding option to
tomwalters@0 15 add extra zeroes, padding to a larger power of 2.
tomwalters@0 16 The amount of extra padding is "exponential", expanding the basic size to:
tomwalters@0 17 ( framewidth + padding ) * 2**n
tomwalters@0 18 where the padding option is n.
tomwalters@0 19 (n=0 by default, so that no extra padding is added. When n=1 then padding is
tomwalters@0 20 added to double the size, and when n=2 the size is quadrupled, etc.).
tomwalters@0 21
tomwalters@0 22 Frames are selected from the input stream using the "frames" option,
tomwalters@0 23 which has syntax: [[-]frame=a[-b]]. Input frames are numbered 1,2,...
tomwalters@0 24 For example:
tomwalters@0 25 frame=a Select just the a'th frame.
tomwalters@0 26 frame=a-b Select frames from the a'th to b'th inclusive.
tomwalters@0 27 The strings "min" and "max" can be used as specifiers, meaning eg:
tomwalters@0 28 frame=min Select the first frame.
tomwalters@0 29 frame=max Select the last frame.
tomwalters@0 30 frame=a-max Select frames from the a'th to the last inclusive.
tomwalters@0 31 frame=min-b Select frames from the first to the b'th inclusive.
tomwalters@0 32 The default selects all frames, (ie frame=min-max).
tomwalters@0 33
tomwalters@0 34 Several forms of fft processing are provided, according to the "spectrum"
tomwalters@0 35 option: log/magnitude/phase/complex/inverse/verbose.
tomwalters@0 36
tomwalters@0 37 Log and magnitude are both magnitude spectra (log is log10 of magnitude).
tomwalters@0 38 Phase is the phase spectrum.
tomwalters@0 39 Complex is the full complex spectrum, in <real,imag> pairs.
tomwalters@0 40 Inverse transform reads framewidth numbers which are interpreted as
tomwalters@0 41 <real,imag> pairs, (ie framewidth/2 complex numbers),
tomwalters@0 42 and outputs the inverse transform scaled by 1/framewidth.
tomwalters@0 43 Verbose prints the spectrum in ASCII on the stdout.
tomwalters@0 44
tomwalters@0 45
tomwalters@0 46 Examples:
tomwalters@0 47
tomwalters@0 48 1. To print the input and output frame sizes in sample points, eg for a
tomwalters@0 49 subsequent plotting program, use the size option:
tomwalters@0 50
tomwalters@0 51 fft ... size=on
tomwalters@0 52
tomwalters@0 53 2. An fft of a waveform sampled at 10kHz, computed within a frame of 12.8ms,
tomwalters@0 54 plotting the 2nd frame in a sequence of frames with half-frame overlap.
tomwalters@0 55
tomwalters@0 56 fft samp=10kHz width=12.8ms frstep=6.4ms frame=2 file | x11plot
tomwalters@0 57
tomwalters@0 58 3. An animated plot of successive fft spectra of a waveform sampled at 10kHz,
tomwalters@0 59 each computed within a frame of 12.8ms, and shifted by 2 sample points.
tomwalters@0 60
tomwalters@0 61 fft samp=10kHz width=12.8ms frstep=2p file | x11play -n64
tomwalters@0 62
tomwalters@0 63 4. Using the complex output from fft, and inverse transform without windowing
tomwalters@0 64 to recover original input.
tomwalters@0 65
tomwalters@0 66 fft samp=10kHz frame=2 spec=complex window=off file > foo
tomwalters@0 67 fft samp=10kHz frame=1 spec=inverse window=off foo | x11plot
tomwalters@0 68
tomwalters@0 69 */
tomwalters@0 70
tomwalters@0 71 #include <stdio.h>
tomwalters@0 72 #include <math.h>
tomwalters@0 73 #include "options.h"
tomwalters@0 74 #include "units.h"
tomwalters@0 75 #include "strmatch.h"
tomwalters@0 76 #include "sigproc.h"
tomwalters@0 77
tomwalters@0 78 char applic[] = "fast Fourier transform of contiguous frames.\n i/p and o/p data in binary shorts." ;
tomwalters@0 79
tomwalters@0 80 static char *helpstr, *debugstr, *sampstr, *widthstr, *padstr, *wstr, *sstr, *sizestr ;
tomwalters@0 81 static char *startstr, *shiftstr, *framestr, *smagstr, *slogstr, *sphasestr, *echostr ;
tomwalters@0 82
tomwalters@0 83 static Options option[] = {
tomwalters@0 84 { "help" , "off" , &helpstr , "help" , DEBUG },
tomwalters@0 85 { "debug" , "off" , &debugstr , "debugging switch" , DEBUG },
tomwalters@0 86 { "samplerate", "20kHz" , &sampstr , "samplerate " , VAL },
tomwalters@0 87 { "start" , "0" , &startstr , "Start point in i/p file." , VAL },
tomwalters@0 88 { "frames" , "1-max" , &framestr , "Select frames inclusively" , VAL },
tomwalters@0 89 { "frstep" , "16ms" , &shiftstr , "Step between input frames." , VAL },
tomwalters@0 90 { "width" , "32ms" , &widthstr , "Width of input frames." , VAL },
tomwalters@0 91 { "padding" , "0" , &padstr , "Exponential frame padding" , SVAL },
tomwalters@0 92 { "window" , "on" , &wstr , "Hamming window" , SETFLAG },
tomwalters@0 93 { "spectrum" , "magnitude" , &sstr , "log/magnitude/phase/complex/inverse/verbose" , SETFLAG },
tomwalters@0 94 { "scalemag" , "0.08" , &smagstr , "scale magnitude spectrum" , SVAL },
tomwalters@0 95 { "scalelog" , "10" , &slogstr , "scale log spectrum" , SVAL },
tomwalters@0 96 { "scalephase", "100" , &sphasestr , "scale phase spectrum" , SVAL },
tomwalters@0 97 { "size" , "off" , &sizestr , "print input/output frame size in samples" , SETFLAG },
tomwalters@0 98 { "echo" , "off" , &echostr , "echo buffered input without processing" , SVAL },
tomwalters@0 99 ( char * ) 0 } ;
tomwalters@0 100
tomwalters@0 101
tomwalters@0 102 int samplerate ;
tomwalters@0 103 int width ;
tomwalters@0 104 int step ;
tomwalters@0 105 int zeroes ;
tomwalters@0 106 int dowindow ;
tomwalters@0 107 double scale ;
tomwalters@0 108 int echo ;
tomwalters@0 109
tomwalters@0 110 float *vec, *window ;
tomwalters@0 111 short *obuf ;
tomwalters@0 112
tomwalters@0 113 int allbins ;
tomwalters@0 114 int halfbins ;
tomwalters@0 115
tomwalters@0 116 #define MAG 1 /* magnitude spectrum */
tomwalters@0 117 #define PHASE 2 /* phase spectrum */
tomwalters@0 118 #define INV 3 /* inverse transform */
tomwalters@0 119 #define VERB 4 /* verbose (ASCII) */
tomwalters@0 120 #define LOG 5 /* log magnitude */
tomwalters@0 121 #define COMP 6 /* complex spectrum */
tomwalters@0 122
tomwalters@0 123 int output ;
tomwalters@0 124
tomwalters@0 125 main (argc, argv)
tomwalters@0 126 int argc;
tomwalters@0 127 char **argv;
tomwalters@0 128 {
tomwalters@0 129 FILE *fp ;
tomwalters@0 130 short *buf ;
tomwalters@0 131 int a, b ;
tomwalters@0 132 int isign ;
tomwalters@0 133
tomwalters@0 134 fp = openopts( option,argc,argv ) ;
tomwalters@0 135 if ( !isoff( helpstr ) )
tomwalters@0 136 helpopts( helpstr, argv[0], applic, option ) ;
tomwalters@0 137
tomwalters@0 138 samplerate = to_Hz( sampstr ) ;
tomwalters@0 139 dowindow = ison( wstr ) ;
tomwalters@0 140 echo = ison( echostr ) ;
tomwalters@0 141 width = to_p( widthstr, samplerate ) ;
tomwalters@0 142 step = to_p( shiftstr, samplerate ) ;
tomwalters@0 143 zeroes = ( getpower( width ) << atoi( padstr ) ) - width ;
tomwalters@0 144
tomwalters@0 145 allbins = width + zeroes ;
tomwalters@0 146 halfbins = allbins / 2 ;
tomwalters@0 147
tomwalters@0 148 if ( iststr( sstr, "magnitude" ) ) {
tomwalters@0 149 output = MAG ;
tomwalters@0 150 scale = atof( smagstr ) ;
tomwalters@0 151 isign = 1 ;
tomwalters@0 152 }
tomwalters@0 153 else if ( iststr( sstr, "log" ) ) {
tomwalters@0 154 output = LOG ;
tomwalters@0 155 scale = atof( slogstr ) ;
tomwalters@0 156 isign = 1 ;
tomwalters@0 157 }
tomwalters@0 158 else if ( iststr( sstr, "phase" ) ) {
tomwalters@0 159 output = PHASE ;
tomwalters@0 160 scale = atof( sphasestr ) ;
tomwalters@0 161 isign = 1 ;
tomwalters@0 162 }
tomwalters@0 163 else if ( iststr( sstr, "complex" ) ) {
tomwalters@0 164 output = COMP ;
tomwalters@0 165 isign = 1 ;
tomwalters@0 166 }
tomwalters@0 167 else if ( iststr( sstr, "inverse" ) ) {
tomwalters@0 168 output = INV ;
tomwalters@0 169 scale = 1./(double)width ;
tomwalters@0 170 isign = (-1) ;
tomwalters@0 171 }
tomwalters@0 172 else if ( iststr( sstr, "verbose" ) ) {
tomwalters@0 173 output = VERB ;
tomwalters@0 174 isign = 1 ;
tomwalters@0 175 }
tomwalters@0 176 else {
tomwalters@0 177 fprintf(stderr,"unknown spectrum \n");
tomwalters@0 178 exit( 1 ) ;
tomwalters@0 179 }
tomwalters@0 180
tomwalters@0 181 /* frame size printout */
tomwalters@0 182
tomwalters@0 183 if ( ison( sizestr ) ) {
tomwalters@0 184 fprintf(stderr,"fft sizes in sample points:\n" ) ;
tomwalters@0 185 fprintf(stderr," input frame size = %d (framewidth=%d + padding=%d)\n", width+zeroes, width, zeroes ) ;
tomwalters@0 186 fprintf(stderr," output frame size = %d \n", (width + zeroes)/2 ) ;
tomwalters@0 187 exit( 0 ) ;
tomwalters@0 188 }
tomwalters@0 189
tomwalters@0 190 /* parse bounds on number of frames */
tomwalters@0 191
tomwalters@0 192 if ( selector( framestr, &a, &b ) == 0 ) {
tomwalters@0 193 fprintf(stderr,"fft: bad frame selector [%s]\n", framestr ) ;
tomwalters@0 194 exit( 1 ) ;
tomwalters@0 195 }
tomwalters@0 196
tomwalters@0 197 /* Allocate working space */
tomwalters@0 198
tomwalters@0 199 obuf = (short *)malloc( halfbins * sizeof(short) ) ;
tomwalters@0 200 vec = (float *)malloc( allbins * sizeof(float) ) ;
tomwalters@0 201 if ( dowindow )
tomwalters@0 202 window = hamming( width ) ;
tomwalters@0 203
tomwalters@0 204
tomwalters@0 205 /* Compute fft for each frame of width shorts in the input stream */
tomwalters@0 206
tomwalters@0 207 if ( seekbytes( fp, (int)( to_p( startstr, samplerate ) * sizeof(short) ) ) == 0 ) {
tomwalters@0 208 fprintf(stderr,"improper seek\n") ;
tomwalters@0 209 exit( 1 ) ;
tomwalters@0 210 }
tomwalters@0 211
tomwalters@0 212 while ( ( buf = getframe( fp, a, width, step ) ) != (short *)0 && ( a<=b || b==0 ) ) {
tomwalters@0 213 if ( echo ) fwrite( buf, sizeof(short), width, stdout ) ;
tomwalters@0 214 else process( buf, isign ) ;
tomwalters@0 215 a++ ;
tomwalters@0 216 }
tomwalters@0 217 if ( a<=b && b>0 )
tomwalters@0 218 fprintf(stderr,"warning: not enough frames for request\n");
tomwalters@0 219
tomwalters@0 220 fclose( fp ) ;
tomwalters@0 221 }
tomwalters@0 222
tomwalters@0 223
tomwalters@0 224 process( buf, isign )
tomwalters@0 225 short *buf ;
tomwalters@0 226 int isign ;
tomwalters@0 227 {
tomwalters@0 228 short *bptr = buf ;
tomwalters@0 229 short *endptr = buf + width ;
tomwalters@0 230 short *optr = obuf ;
tomwalters@0 231 short *endbin = obuf + halfbins ;
tomwalters@0 232 float *wptr = window ;
tomwalters@0 233 float *vptr = vec ;
tomwalters@0 234 float *endvec = vec + allbins ;
tomwalters@0 235
tomwalters@0 236 if ( dowindow )
tomwalters@0 237 while ( bptr < endptr )
tomwalters@0 238 *vptr++ = *bptr++ * *wptr++ ;
tomwalters@0 239 else
tomwalters@0 240 while ( bptr < endptr )
tomwalters@0 241 *vptr++ = *bptr++ ;
tomwalters@0 242
tomwalters@0 243 while ( vptr < endvec ) /* padding */
tomwalters@0 244 *vptr++ = 0 ;
tomwalters@0 245
tomwalters@0 246 fft( vec, width+zeroes, isign ) ;
tomwalters@0 247
tomwalters@0 248 vptr = vec ;
tomwalters@0 249 switch ( output ) {
tomwalters@0 250
tomwalters@0 251 case COMP : while ( vptr < endvec )
tomwalters@0 252 *optr++ = (short)( *vptr++ ) ;
tomwalters@0 253 fwrite( obuf, sizeof(short), allbins, stdout ) ;
tomwalters@0 254 break;
tomwalters@0 255 case PHASE : phase( vec, allbins ) ;
tomwalters@0 256 while ( optr < endbin )
tomwalters@0 257 *optr++ = (short)( scale * *vptr++ ) ;
tomwalters@0 258 fwrite( obuf, sizeof(short), halfbins, stdout ) ;
tomwalters@0 259 break;
tomwalters@0 260 case MAG : mag( vec, allbins ) ;
tomwalters@0 261 while ( optr < endbin )
tomwalters@0 262 *optr++ = (short)( scale * *vptr++ ) ;
tomwalters@0 263 fwrite( obuf, sizeof(short), halfbins, stdout ) ;
tomwalters@0 264 break;
tomwalters@0 265 case LOG : mag( vec, allbins ) ;
tomwalters@0 266 while ( optr < endbin )
tomwalters@0 267 *optr++ = (short)( scale * 10 * log10(*vptr++) ) ;
tomwalters@0 268 fwrite( obuf, sizeof(short), halfbins, stdout ) ;
tomwalters@0 269 break;
tomwalters@0 270 case INV : while ( vptr < endvec )
tomwalters@0 271 *optr++ = (short)( scale * *vptr++ ) ;
tomwalters@0 272 fwrite( obuf, sizeof(short), allbins, stdout ) ;
tomwalters@0 273 break;
tomwalters@0 274 case VERB : print_complex_spectrum( (complex *)vec, halfbins, samplerate ) ;
tomwalters@0 275 break;
tomwalters@0 276
tomwalters@0 277 }
tomwalters@0 278 }
tomwalters@0 279
tomwalters@0 280
tomwalters@0 281 /*
tomwalters@0 282 Pretty-print complex k-spectrum on the stdout.
tomwalters@0 283 */
tomwalters@0 284
tomwalters@0 285 print_complex_spectrum(C,k,samplerate)
tomwalters@0 286 complex *C;
tomwalters@0 287 int k, samplerate;
tomwalters@0 288 {
tomwalters@0 289 float res; /* frequency resolution. */
tomwalters@0 290 int i;
tomwalters@0 291
tomwalters@0 292 res = (float)(samplerate>>1) / (float)k;
tomwalters@0 293 for (i=0; i<k ; i++) {
tomwalters@0 294 printf ("%3d. %6.1fHz. Re=%9.2f Im=%9.2f mod=%8.2f arg=%4.2f\n",
tomwalters@0 295 i, i*res, Re(&C[i]), Im(&C[i]), mod(&C[i]), arg(&C[i]) );
tomwalters@0 296 }
tomwalters@0 297 }
tomwalters@0 298