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
|