/*============================================================================= | | NAME | | FFT.cpp | | DESCRIPTION | | This program does an in-place one dimensional complex discrete | Fourier transform. It uses an FFT algorithm for a number of | input points which is a power of two. It's implemented in C++ | as a derived class of the STL vector type. | | AUTHOR | | Sean O'Connor 19 November 2005 | | LEGAL | | FFT Version 1.6 - An FFT utility library in C++. | Copyright (C) 2005-2024 by Sean Erik O'Connor. All Rights Reserved. | | This program is free software: you can redistribute it and/or modify | it under the terms of the GNU General Public License as published by | the Free Software Foundation, either version 3 of the License, or | (at your option) any later version. | | This program is distributed in the hope that it will be useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | GNU General Public License for more details. | | You should have received a copy of the GNU General Public License | along with this program. If not, see . | | The author's address is seanerikoconnor!AT!gmail!DOT!com | with !DOT! replaced by . and the !AT! replaced by @ | +============================================================================*/ /*------------------------------------------------------------------------------ | Include Files | ------------------------------------------------------------------------------*/ #include // Basic stream I/O. #include // File stream I/O. #include // String stream I/O. #include // Basic math functions. #include // Complex data type and operations. #include // STL vector class. #include // Iterators. #include // STL string class. #include // Exceptions. #include // Constants like pi_v using namespace std ; // Let us omit the std:: prefix for every STL class. #include "FFT.hpp" // Our own FFT stuff. /*============================================================================= | | NAME | | FastFourierTransform | | DESCRIPTION | | Default constructor. Just call the base class constructor for | the vector type, then initialize the derived class field. | +============================================================================*/ template FastFourierTransform::FastFourierTransform() : vector< complex >() , direction( false ) { // Get the sine and cosine tables initialized. /// MAX_TABLE_SIZE ) ; starting_multiplier.clear(); } /*============================================================================= | | NAME | | ~FastFourierTransform | | DESCRIPTION | | Destructor. Doesn't need to do much. | +============================================================================*/ template FastFourierTransform::~FastFourierTransform() { } /*============================================================================= | | NAME | | FastFourierTransform | | DESCRIPTION | | Copy constructor. | +============================================================================*/ template FastFourierTransform::FastFourierTransform( const FastFourierTransform & transform ) : vector< complex >( transform ) // Initialize base class first. { // Copy derived class stuff. direction = transform.direction ; } /*============================================================================= | | NAME | | = | | DESCRIPTION | | Assignment operator. | +============================================================================*/ template FastFourierTransform & FastFourierTransform::operator=( const FastFourierTransform & transform ) { // Check for initializing to oneself: pass back a reference to the unchanged object. if (this == &transform) { // Pass back the object so we can concatenate assignments. return *this ; } // Call base class assignment operator first. FastFourierTransform::operator=( transform ) ; // Assignment for derived class fields. direction = transform.direction ; // Pass back the object so we can concatenate assignments. return *this ; } /*============================================================================= | | NAME | | fft | | DESCRIPTION | | | This member function does an in-place one dimensional complex | discrete Fourier transform. It uses an FFT algorithm for a number | of input points which is a power of two. | | PARAMETERS | | direction The direction of the FFT, true for a forward transform and | false for an inverse transform. | | EXCEPTIONS | | Throws FastFourierTransform type exceptions for the cases where | | -Number of points in the transform is less than 2. | -Number of points is greater than the sine/cosine table can handle. | -Number of points is not a power of 2. | | EXAMPLE | | Suppose the vector contains the data | | X ( 0 ) = 1 + i | X ( 1 ) = 2 + 2 i | X ( 2 ) = 3 + 3 i | X ( 3 ) = 4 + 4 i | | then the forward FFT returns | | X ( 0 ) = 5 + 5 i | X ( 1 ) = -2 | X ( 2 ) = -1 - i | X ( 3 ) = - 2 i | | and the inverse FFT returns the original data to within roundoff. | | ^ | The Fourier coefficients X( k ) are defined for k = 0, . . ., N - 1 | by the formula | | N - 1 2 Pi i | ^ 1 ____ - ------ j k | X( k ) = --------- \ N | ----- / X( j ) e | \ / N ---- | v j = 0 | | where e is 2.1718 . . ., Pi = 3.14159. . . and i is the square root | of -1. | | This is the formula for the forward transform. The inverse transform | is the same, except that the minus sign in the exponent above is a | plus sign instead. | --- | The normalizing factor 1 / \/ N in front was picked so that the | forward and inverse transforms look the same. | | The particular type of FFT algorithm we use is a | | Complex, | Two to the N, | Decimation in frequency, | Input in order, output bit reversed | | type of FFT. It is described in the book | | THE FAST FOURIER TRANSFORM, | F. Oran Brigham, Prentice Hall, 1974 | | Also see my notes for an in-depth explanation. | | BUGS | | The bit-reversal routine could be made faster by using bit shifting | operations. | | You might want to use long double precision complex numbers to get more | accuracy. | | Check for a power of 2 probably ought to use the machine's floating point | epsilon (computed automatically). | +============================================================================*/ template void FastFourierTransform::fft( bool direction ) { // Number of complex points in the FFT from the vector's size. unsigned int N = this->size() ; /* * Compute log base 2 of N. Perturb slightly to avoid roundoff error. * For example, (int)( 4.99999999999 ) = 4 (wrong!), but * (int)( 4.999999999 + .01 ) = 5 (correct!) */ int log_of_n = (int) ( log( (FloatType) N ) / ( log( 2.0 ) ) + .01 ) ; /* * Error checking. Print a message to the terminal if any one of these * conditions is true: * * N < 2. * log2( N ) > MAX_TABLE_SIZE + 1. * N is not a power of 2. */ if (N < 2) { ostringstream os ; os << "FFT has less than two points. N = " << N << " file: " << __FILE__ << " line: " << __LINE__ ;; throw FastFourierTransformException( os.str() ) ; } else if ( (log_of_n - 1) > MAX_TABLE_SIZE) { ostringstream os ; os << "FFT has too many points for its sin/cos table. Table size = " << MAX_TABLE_SIZE << " file: " << __FILE__ << " line: " << __LINE__ ; throw FastFourierTransformException( os.str() ) ; } else if ( (int) pow( 2.0, log_of_n ) != N ) { ostringstream os ; os << "Number of points in the FFT is not a power of 2. N = " << N << " file: " << __FILE__ << " line: " << __LINE__ ;; throw FastFourierTransformException( os.str() ) ; } /* * If called_already is false, generate the sine and cosine table for * the first time, then set called_already to true. Now later calls to * this function don't recreate the sine and cosine table. */ if (!called_already) { for (int i = 0 ; i <= MAX_TABLE_SIZE - 1 ; ++i) { // Current angle in sine and cosine table. FloatType angle = -PI / pow( (FloatType) 2, (FloatType) i ) ; // e^t = cos t + i sin t starting_multiplier[ i ] = complex(cos(angle), sin(angle)) ; } called_already = true ; } /* * Compute the FFT according to its signal flow graph with * computations arranged into butterflies, groups of butterflies * with the same multiplier, and columns. */ int butterfly_height = N / 2 ; int number_of_groups = 1 ; /* * A column refers to a column of the FFT signal flow graph. A group is * a collection of butterflies which have the same multiplier W^p. A * butterfly is the basic computational unit of the FFT. */ for (int column = 1 ; column <= log_of_n ; ++column) { // The multiplier W^p for a group of butterflies. complex multiplier = starting_multiplier[ column - 1 ] ; // Modify starting multiplier W^p when computing the inverse transform. if (direction == false) multiplier = conj( multiplier ) ; // Difference between current multiplier and next. complex increment = multiplier ; // Compute the first group. for (int i = 0 ; i <= butterfly_height - 1 ; ++i) { // Lower branch of a butterfly. int offset = i + butterfly_height ; complex temp = (*this)[ i ] + (*this)[ offset ] ; (*this)[ offset ] = (*this)[ i ] - (*this)[ offset ] ; (*this)[ i ] = temp ; } // Now do the remaining groups. for (int group = 2 ; group <= number_of_groups ; ++group) { // Array index of first butterfly in group. int start_of_group = reverse_bits( group - 1, log_of_n ) ; // Array index of last butterfly in group. int end_of_group = start_of_group + butterfly_height - 1 ; // Compute all butterflies in a group. for (int i = start_of_group ; i <= end_of_group ; ++i) { int offset = i + butterfly_height ; // Temporary storage in a butterfly calculation. complex product = (*this)[ offset ] * multiplier ; complex temp = (*this)[ i ] + product ; (*this)[ offset ] = (*this)[ i ] - product ; (*this)[ i ] = temp ; } multiplier *= increment ; } // end group butterfly_height = butterfly_height / 2 ; number_of_groups = number_of_groups * 2 ; } // end column // Normalize the results by \/ N and permute them into order, two at a time. FloatType normalizer = (FloatType) 1.0 / sqrt( (FloatType) N ) ; for (int i = 0 ; i <= N - 1 ; ++i) { // Bit-reversed array index i. int rev_i = reverse_bits( i, log_of_n ) ; if ( rev_i > i ) // Exchange is not yet done. { complex temp = (*this)[ i ] ; (*this)[ i ] = (*this)[ rev_i ] * normalizer ; (*this)[ rev_i ] = temp * normalizer ; } else if ( rev_i == i ) // exchange with itself { (*this)[ i ] = (*this)[ i ] * normalizer ; } } return ; } /* ====================== end of function fft ============================= */ /*============================================================================= | | NAME | | reverse_bits | | DESCRIPTION | | This function reverses the order of the bits in a number. | | INPUT | | n The number whose bits are to be reversed. | num_bits The number of bits in N, counting leading zeros. | | OUTPUT | | The number N with all its num_bits bits reversed. | | EXAMPLE CALLING SEQUENCE | | reverse_bits( (10) , 2 ) = (01) = 1, but | 2 2 | | reverse_bits( (10) , 3 ) = reverse_bits( (010) , 3 ) = (010) = (10) | 2 2 2 2 | = 10 | | | METHOD | | The algorithm works by a method similar to base conversion. | As the low order bits are broken off, one by one, they are | multiplied by 2 and accumulated to generate the bit-reversed | number. | | BUGS | | The bit-reversal routine could be made faster by using C shift operators. | +============================================================================*/ template int FastFourierTransform::reverse_bits( int n, int num_bits ) { int remainder = n, // Bits remaining to be reversed. reversed_n = 0 ; // Bit-reversed value of n. for (int bit_num = 1 ; bit_num <= num_bits ; ++bit_num) { int bit = remainder % 2 ; /* Next least significant bit. */ reversed_n = bit + 2 * reversed_n ; /* Shift left and add to buffer. */ remainder = (int)( remainder / 2 ) ; /* Remaining bits. */ } return( reversed_n ) ; } /* ===================== end of function reverse_bits ===================== */ /*============================================================================== | Forced Template Instantiations | ==============================================================================*/ // C++ doesn't automatically generate templated classes or functions until they // are first used. So depending on the order of compilation, the linker may say // the templated functions are undefined. // // Therefore, explicitly instantiate ALL templates here: template FastFourierTransform::FastFourierTransform() ; template FastFourierTransform::FastFourierTransform( const FastFourierTransform & ) ; template FastFourierTransform::~FastFourierTransform() ; template void FastFourierTransform::fft( bool ) ; template FastFourierTransform::FastFourierTransform() ; template FastFourierTransform::FastFourierTransform( const FastFourierTransform & ) ; template FastFourierTransform::~FastFourierTransform() ; template void FastFourierTransform::fft( bool ) ;