1/*=============================================================================
2|
3| NAME
4|
5| FFT.cpp
6|
7| DESCRIPTION
8|
9| This program does an in-place one dimensional complex discrete
10| Fourier transform. It uses an FFT algorithm for a number of
11| input points which is a power of two. It's implemented in C++
12| as a derived class of the STL vector type.
13|
14| AUTHOR
15|
16| Sean O'Connor 19 November 2005
17|
18| LEGAL
19|
20| FFT Version 1.6 - An FFT utility library in C++.
21| Copyright (C) 2005-2024 by Sean Erik O'Connor. All Rights Reserved.
22|
23| This program is free software: you can redistribute it and/or modify
24| it under the terms of the GNU General Public License as published by
25| the Free Software Foundation, either version 3 of the License, or
26| (at your option) any later version.
27|
28| This program is distributed in the hope that it will be useful,
29| but WITHOUT ANY WARRANTY; without even the implied warranty of
30| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31| GNU General Public License for more details.
32|
33| You should have received a copy of the GNU General Public License
34| along with this program. If not, see <http://www.gnu.org/licenses/>.
35|
36| The author's address is seanerikoconnor!AT!gmail!DOT!com
37| with !DOT! replaced by . and the !AT! replaced by @
38|
39+============================================================================*/
40
41/*------------------------------------------------------------------------------
42| Include Files |
43------------------------------------------------------------------------------*/
44
45#include <iostream> // Basic stream I/O.
46#include <fstream> // File stream I/O.
47#include <sstream> // String stream I/O.
48#include <cmath> // Basic math functions.
49#include <complex> // Complex data type and operations.
50#include <vector> // STL vector class.
51#include <algorithm> // Iterators.
52#include <string> // STL string class.
53#include <stdexcept> // Exceptions.
54#include <numbers> // Constants like pi_v<float type>
55
56using namespace std ; // Let us omit the std:: prefix for every STL class.
57
58#include "FFT.hpp" // Our own FFT stuff.
59
60
61/*=============================================================================
62|
63| NAME
64|
65| FastFourierTransform
66|
67| DESCRIPTION
68|
69| Default constructor. Just call the base class constructor for
70| the vector type, then initialize the derived class field.
71|
72+============================================================================*/
73
74template <typename FloatType>
75FastFourierTransform<FloatType>::FastFourierTransform()
76 : vector< complex<FloatType> >()
77 , direction( false )
78{
79 // Get the sine and cosine tables initialized.
80 /// MAX_TABLE_SIZE ) ;
81 starting_multiplier.clear();
82}
83
84
85/*=============================================================================
86|
87| NAME
88|
89| ~FastFourierTransform
90|
91| DESCRIPTION
92|
93| Destructor. Doesn't need to do much.
94|
95+============================================================================*/
96
97template <typename FloatType>
98FastFourierTransform<FloatType>::~FastFourierTransform()
99{
100}
101
102
103
104/*=============================================================================
105|
106| NAME
107|
108| FastFourierTransform
109|
110| DESCRIPTION
111|
112| Copy constructor.
113|
114+============================================================================*/
115
116template <typename FloatType>
117FastFourierTransform<FloatType>::FastFourierTransform( const FastFourierTransform & transform )
118 : vector< complex<FloatType> >( transform ) // Initialize base class first.
119{
120 // Copy derived class stuff.
121 direction = transform.direction ;
122}
123
124
125/*=============================================================================
126|
127| NAME
128|
129| =
130|
131| DESCRIPTION
132|
133| Assignment operator.
134|
135+============================================================================*/
136
137template <typename FloatType>
138FastFourierTransform<FloatType> & FastFourierTransform<FloatType>::operator=( const FastFourierTransform & transform )
139{
140 // Check for initializing to oneself: pass back a reference to the unchanged object.
141 if (this == &transform)
142 {
143 // Pass back the object so we can concatenate assignments.
144 return *this ;
145 }
146
147 // Call base class assignment operator first.
148 FastFourierTransform<FloatType>::operator=( transform ) ;
149
150 // Assignment for derived class fields.
151 direction = transform.direction ;
152
153
154 // Pass back the object so we can concatenate assignments.
155 return *this ;
156}
157
158
159/*=============================================================================
160|
161| NAME
162|
163| fft
164|
165| DESCRIPTION
166|
167|
168| This member function does an in-place one dimensional complex
169| discrete Fourier transform. It uses an FFT algorithm for a number
170| of input points which is a power of two.
171|
172| PARAMETERS
173|
174| direction The direction of the FFT, true for a forward transform and
175| false for an inverse transform.
176|
177| EXCEPTIONS
178|
179| Throws FastFourierTransform type exceptions for the cases where
180|
181| -Number of points in the transform is less than 2.
182| -Number of points is greater than the sine/cosine table can handle.
183| -Number of points is not a power of 2.
184|
185| EXAMPLE
186|
187| Suppose the vector contains the data
188|
189| X ( 0 ) = 1 + i
190| X ( 1 ) = 2 + 2 i
191| X ( 2 ) = 3 + 3 i
192| X ( 3 ) = 4 + 4 i
193|
194| then the forward FFT returns
195|
196| X ( 0 ) = 5 + 5 i
197| X ( 1 ) = -2
198| X ( 2 ) = -1 - i
199| X ( 3 ) = - 2 i
200|
201| and the inverse FFT returns the original data to within roundoff.
202|
203| ^
204| The Fourier coefficients X( k ) are defined for k = 0, . . ., N - 1
205| by the formula
206|
207| N - 1 2 Pi i
208| ^ 1 ____ - ------ j k
209| X( k ) = --------- \ N
210| ----- / X( j ) e
211| \ / N ----
212| v j = 0
213|
214| where e is 2.1718 . . ., Pi = 3.14159. . . and i is the square root
215| of -1.
216|
217| This is the formula for the forward transform. The inverse transform
218| is the same, except that the minus sign in the exponent above is a
219| plus sign instead.
220| ---
221| The normalizing factor 1 / \/ N in front was picked so that the
222| forward and inverse transforms look the same.
223|
224| The particular type of FFT algorithm we use is a
225|
226| Complex,
227| Two to the N,
228| Decimation in frequency,
229| Input in order, output bit reversed
230|
231| type of FFT. It is described in the book
232|
233| THE FAST FOURIER TRANSFORM,
234| F. Oran Brigham, Prentice Hall, 1974
235|
236| Also see my notes for an in-depth explanation.
237|
238| BUGS
239|
240| The bit-reversal routine could be made faster by using bit shifting
241| operations.
242|
243| You might want to use long double precision complex numbers to get more
244| accuracy.
245|
246| Check for a power of 2 probably ought to use the machine's floating point
247| epsilon (computed automatically).
248|
249+============================================================================*/
250
251template <typename FloatType>
252void FastFourierTransform<FloatType>::fft( bool direction )
253{
254// Number of complex points in the FFT from the vector's size.
255unsigned int N = this->size() ;
256
257/*
258 * Compute log base 2 of N. Perturb slightly to avoid roundoff error.
259 * For example, (int)( 4.99999999999 ) = 4 (wrong!), but
260 * (int)( 4.999999999 + .01 ) = 5 (correct!)
261 */
262int log_of_n = (int) ( log( (FloatType) N ) / ( log( 2.0 ) ) + .01 ) ;
263
264
265/*
266 * Error checking. Print a message to the terminal if any one of these
267 * conditions is true:
268 *
269 * N < 2.
270 * log2( N ) > MAX_TABLE_SIZE + 1.
271 * N is not a power of 2.
272 */
273if (N < 2)
274{
275 ostringstream os ;
276 os << "FFT has less than two points. N = " << N << " file: " << __FILE__ << " line: " << __LINE__ ;;
277 throw FastFourierTransformException( os.str() ) ;
278}
279else if ( (log_of_n - 1) > MAX_TABLE_SIZE)
280{
281 ostringstream os ;
282 os << "FFT has too many points for its sin/cos table. Table size = " << MAX_TABLE_SIZE << " file: " << __FILE__ << " line: " << __LINE__ ;
283 throw FastFourierTransformException( os.str() ) ;
284
285}
286else if ( (int) pow( 2.0, log_of_n ) != N )
287{
288 ostringstream os ;
289 os << "Number of points in the FFT is not a power of 2. N = " << N << " file: " << __FILE__ << " line: " << __LINE__ ;;
290 throw FastFourierTransformException( os.str() ) ;
291}
292
293
294/*
295 * If called_already is false, generate the sine and cosine table for
296 * the first time, then set called_already to true. Now later calls to
297 * this function don't recreate the sine and cosine table.
298 */
299if (!called_already)
300{
301 for (int i = 0 ; i <= MAX_TABLE_SIZE - 1 ; ++i)
302 {
303 // Current angle in sine and cosine table.
304 FloatType angle = -PI / pow( (FloatType) 2, (FloatType) i ) ;
305
306 // e^t = cos t + i sin t
307 starting_multiplier[ i ] = complex<FloatType>(cos(angle), sin(angle)) ;
308 }
309
310 called_already = true ;
311}
312
313
314
315/*
316 * Compute the FFT according to its signal flow graph with
317 * computations arranged into butterflies, groups of butterflies
318 * with the same multiplier, and columns.
319 */
320int butterfly_height = N / 2 ;
321int number_of_groups = 1 ;
322
323
324/*
325 * A column refers to a column of the FFT signal flow graph. A group is
326 * a collection of butterflies which have the same multiplier W^p. A
327 * butterfly is the basic computational unit of the FFT.
328 */
329for (int column = 1 ; column <= log_of_n ; ++column)
330{
331 // The multiplier W^p for a group of butterflies.
332 complex<FloatType> multiplier = starting_multiplier[ column - 1 ] ;
333
334 // Modify starting multiplier W^p when computing the inverse transform.
335 if (direction == false)
336 multiplier = conj( multiplier ) ;
337
338 // Difference between current multiplier and next.
339 complex<FloatType> increment = multiplier ;
340
341 // Compute the first group.
342 for (int i = 0 ; i <= butterfly_height - 1 ; ++i)
343 {
344 // Lower branch of a butterfly.
345 int offset = i + butterfly_height ;
346
347 complex<FloatType> temp = (*this)[ i ] + (*this)[ offset ] ;
348 (*this)[ offset ] = (*this)[ i ] - (*this)[ offset ] ;
349 (*this)[ i ] = temp ;
350 }
351
352 // Now do the remaining groups.
353 for (int group = 2 ; group <= number_of_groups ; ++group)
354 {
355 // Array index of first butterfly in group.
356 int start_of_group = reverse_bits( group - 1, log_of_n ) ;
357
358 // Array index of last butterfly in group.
359 int end_of_group = start_of_group + butterfly_height - 1 ;
360
361 // Compute all butterflies in a group.
362 for (int i = start_of_group ; i <= end_of_group ; ++i)
363 {
364 int offset = i + butterfly_height ;
365
366 // Temporary storage in a butterfly calculation.
367 complex<FloatType> product = (*this)[ offset ] * multiplier ;
368
369 complex<FloatType> temp = (*this)[ i ] + product ;
370 (*this)[ offset ] = (*this)[ i ] - product ;
371 (*this)[ i ] = temp ;
372
373 }
374
375 multiplier *= increment ;
376 } // end group
377
378 butterfly_height = butterfly_height / 2 ;
379 number_of_groups = number_of_groups * 2 ;
380
381} // end column
382
383
384
385// Normalize the results by \/ N and permute them into order, two at a time.
386FloatType normalizer = (FloatType) 1.0 / sqrt( (FloatType) N ) ;
387
388
389for (int i = 0 ; i <= N - 1 ; ++i)
390{
391 // Bit-reversed array index i.
392 int rev_i = reverse_bits( i, log_of_n ) ;
393
394 if ( rev_i > i ) // Exchange is not yet done.
395 {
396 complex<FloatType> temp = (*this)[ i ] ;
397 (*this)[ i ] = (*this)[ rev_i ] * normalizer ;
398 (*this)[ rev_i ] = temp * normalizer ;
399 }
400 else if ( rev_i == i ) // exchange with itself
401 {
402 (*this)[ i ] = (*this)[ i ] * normalizer ;
403 }
404}
405
406return ;
407
408} /* ====================== end of function fft ============================= */
409
410
411
412/*=============================================================================
413|
414| NAME
415|
416| reverse_bits
417|
418| DESCRIPTION
419|
420| This function reverses the order of the bits in a number.
421|
422| INPUT
423|
424| n The number whose bits are to be reversed.
425| num_bits The number of bits in N, counting leading zeros.
426|
427| OUTPUT
428|
429| The number N with all its num_bits bits reversed.
430|
431| EXAMPLE CALLING SEQUENCE
432|
433| reverse_bits( (10) , 2 ) = (01) = 1, but
434| 2 2
435|
436| reverse_bits( (10) , 3 ) = reverse_bits( (010) , 3 ) = (010) = (10)
437| 2 2 2 2
438| = 10
439|
440|
441| METHOD
442|
443| The algorithm works by a method similar to base conversion.
444| As the low order bits are broken off, one by one, they are
445| multiplied by 2 and accumulated to generate the bit-reversed
446| number.
447|
448| BUGS
449|
450| The bit-reversal routine could be made faster by using C shift operators.
451|
452+============================================================================*/
453
454template <typename FloatType>
455int FastFourierTransform<FloatType>::reverse_bits( int n, int num_bits )
456{
457 int
458 remainder = n, // Bits remaining to be reversed.
459 reversed_n = 0 ; // Bit-reversed value of n.
460
461 for (int bit_num = 1 ; bit_num <= num_bits ; ++bit_num)
462 {
463 int bit = remainder % 2 ; /* Next least significant bit. */
464 reversed_n = bit + 2 * reversed_n ; /* Shift left and add to buffer. */
465 remainder = (int)( remainder / 2 ) ; /* Remaining bits. */
466 }
467
468 return( reversed_n ) ;
469
470} /* ===================== end of function reverse_bits ===================== */
471
472
473/*==============================================================================
474| Forced Template Instantiations |
475==============================================================================*/
476
477// C++ doesn't automatically generate templated classes or functions until they
478// are first used. So depending on the order of compilation, the linker may say
479// the templated functions are undefined.
480//
481// Therefore, explicitly instantiate ALL templates here:
482
483template FastFourierTransform<float>::FastFourierTransform() ;
484template FastFourierTransform<float>::FastFourierTransform( const FastFourierTransform<float> & ) ;
485template FastFourierTransform<float>::~FastFourierTransform() ;
486template void FastFourierTransform<float>::fft( bool ) ;
487
488template FastFourierTransform<double>::FastFourierTransform() ;
489template FastFourierTransform<double>::FastFourierTransform( const FastFourierTransform<double> & ) ;
490template FastFourierTransform<double>::~FastFourierTransform() ;
491template void FastFourierTransform<double>::fft( bool ) ;