/*=============================================================================
|
| NAME
|
| testFFT.cpp
|
| DESCRIPTION
|
| Test program for FFT.cpp
|
| AUTHOR
|
| Sean O'Connor
|
| 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 // Basic math functions.
#include // Complex data type and operations.
#include // STL vector class.
#include // File stream I/O.
#include // Iterators.
#include // STL string class.
#include // Exceptions.
#include // Numeric limits, e.g. floating point.
#include // Floating point formating.
using namespace std ; // Let us omit the std:: prefix for every STL class.
using namespace std::complex_literals ; // Let's us do complex c = 1 +2i
#include "FFT.hpp" // Our own FFT stuff.
// Pick just one.
#define SIMPLE_WAY_TO_READ_NUMBERS_FROM_FILE
//#define COMPLICATED_WAY_TO_READ_NUMBERS_FROM_FILE
//#define HARDCODED_DATA
string legalNotice
(
"\n"
"FFT Version 1.2 - An FFT utility in C++.\n"
"Copyright (C) 2005-2024 by Sean Erik O'Connor. All Rights Reserved.\n"
"\n"
"FFT comes with ABSOLUTELY NO WARRANTY; for details see the\n"
"GNU General Public License. This is free software, and you are welcome\n"
"to redistribute it under certain conditions; see the GNU General Public License\n"
"for details.\n\n"
) ;
/*=============================================================================
|
| NAME
|
| main
|
| DESCRIPTION
|
| This program tests the FFT implementation by performing forward and
| inverse transforms on the data and testing error conditions.
|
| INPUT
|
| File containing
|
| - Number of complex points in the transform.
| - The data points (complex numbers).
|
| OUTPUT
|
| - DFT of the input.
| - inverse DFT of the first DFT.
|
| EXAMPLE
|
| From the command line, call
|
| $ FastFourierTransform fftIn.txt fftOut.txt
|
| where the input file fftIn.txt contains
|
| 4
| (1, 1)
| (2, 2)
| (3, 3)
| (4, 4)
|
| and the output file fftOut.txt contains
|
| num_points = 4
| Point num. 0= (1,1)
| Point num. 1= (2,2)
| Point num. 2= (3,3)
| Point num. 3= (4,4)
| The transform of the input data is . . .
|
| Point num. 0 = (5,5)
| Point num. 1 = (-2,-1.11022e-016)
| Point num. 2 = (-1,-1)
| Point num. 3 = (0,-2)
| The inverse transform of the transform above is . . .
|
| Point num. 0 = (1,1)
| Point num. 1 = (2,2)
| Point num. 2 = (3,3)
| Point num. 3 = (4,4)
| Point num. 0 = (1,1)
| Point num. 1 = (2,2)
| Point num. 2 = (3,3)
| Point num. 3 = (4,4)
|
| Calling FFT with too many points.
| FFT has too many points for its sin/cos table
|
+============================================================================*/
int main( int argc, char * argv[] )
{
cout << legalNotice ;
int num_points = 0 ; // Number of points in the transform.
int i = 0 ;
// Open file streams for input and output getting file names
// from the command line.
if (argc != 3)
{
cerr << "Error: Missing the names of the input and output "
"files or too many files on the command line." ;
return 1 ;
}
char * inputFileName = argv[ 1 ] ;
char * outputFileName = argv[ 2 ] ;
fstream fin ( inputFileName, ios::in ) ;
fstream fout( outputFileName, ios::out ) ;
// Set a really high precision for our printouts.
// Of course some binary numbers will have repeating decimals, but we are checking to see if we get the same rounding results
// different computer architectures.
fout << scientific << setprecision( 100 ) << setw( 100 );
if (!(fin.is_open() && fout.is_open()))
{
cerr << "Error: Problem opening the input or output files. "
"Check the file names and permissions." ;
return 1 ;
}
try
{
//////////////////////////////////////////////////////////////////////////
/// Single precision FFT
//////////////////////////////////////////////////////////////////////////
// Create our DFT object.
FastFourierTransform dft_single ;
// Clear the fft object.
dft_single.clear() ;
// Read lines until end of file.
while (!fin.eof())
{
// Read the number of points in the transform.
fin >> num_points ;
// Check to see that the number of points in the FFT is valid.
if (num_points <= 0)
throw FastFourierTransformException( "The number of points is out of range.\n" ) ;
// Read in the complex data points from file into the vector.
for (i = 0 ; i < num_points ; ++i)
{
complex c ;
fin >> c ;
dft_single.push_back( c ) ;
}
}
fout << "* * * * * * Single precision FFT" << endl ;
// Print out the points just read in.
fout << "\n\n\nNumber of points = " << num_points << endl ;
for (i = 0 ; i <= num_points - 1 ; ++i)
fout << "Point num. " << i << " = " << dft_single[ i ] << endl ;
// Try a forward FFT.
dft_single.fft( true ) ;
// Print the transform results.
fout << "\n\nThe discrete Fourier transform of the input data is . . . \n\n" ;
for (i = 0 ; i <= num_points - 1 ; ++i)
fout << "Point num. " << i << " = " << dft_single[ i ] << endl ;
// Try an inverse transform to get the input data back, with roundoff error.
dft_single.fft( false ) ;
// Print the inverse transform.
fout << "\n\nThe inverse transform of the transform above is . . .\n\n" ;
for (i = 0 ; i <= num_points - 1 ; ++i)
fout << "Point num. " << i << " = " << dft_single[ i ] << endl ;
//////////////////////////////////////////////////////////////////////////
/// Double precision FFT
//////////////////////////////////////////////////////////////////////////
fout << endl << endl << endl << "* * * * * * Double precision FFT" << endl ;
// Create our DFT object.
FastFourierTransform dft_double ;
// Clear the fft object.
dft_double.clear() ;
#if defined( SIMPLE_WAY_TO_READ_NUMBERS_FROM_FILE ) || defined( COMPLICATED_WAY_TO_READ_NUMBERS_FROM_FILE )
// Rewind the input file to the begining.
fin.clear() ; // Forget we hit the end of file.
fin.seekg( 0, ios::beg ) ; // Move to the start of the file.
#endif // defined( SIMPLE_WAY_TO_READ_NUMBERS_FROM_FILE ) || defined( COMPLICATED_WAY_TO_READ_NUMBERS_FROM_FILE )
#ifdef COMPLICATED_WAY_TO_READ_NUMBERS_FROM_FILE
// Store the lines of file as a vector of strings.
vector buffer ;
buffer.clear() ;
// Add each line of the file to the vector.
string line_of_file ;
while (getline( fin, line_of_file ))
buffer.push_back( line_of_file ) ;
// Now the whole file is in memory.
// Parse the first line which is the number of points.
istringstream is ;
is.clear() ;
is.str( buffer[ 0 ] ) ;
is >> num_points ;
// Check to see that the number of points in the FFT is valid.
if (num_points <= 0)
throw FastFourierTransformException( "The number of points is out of range.\n" ) ;
// Now parse all the other lines which are the data points.
int line_num = 1 ;
while (line_num < buffer.size())
{
// Get the next line in memory and advance.
string line_of_file = buffer[ line_num++ ] ;
// Parse the complex number.
istringstream is ;
is.clear() ;
is.str( line_of_file ) ;
complex c ;
is >> c ;
dft_double.push_back( c ) ;
}
#endif // COMPLICATED_WAY_TO_READ_NUMBERS_FROM_FILE
#ifdef SIMPLE_WAY_TO_READ_NUMBERS_FROM_FILE
if (!fin.eof())
{
// Read the number of points in the transform.
fin >> num_points ;
// Check to see that the number of points in the FFT is valid.
if (num_points <= 0)
{
ostringstream os ;
os << "The number of points = " << num_points << " is out of range.\n" ;
throw FastFourierTransformException( os.str() ) ;
}
}
// Read in the complex data points from file into the vector.
for (int index = 1 ; index <= num_points ; ++index)
{
complex c = 0.0 + 0.0i ; // Floating literals default to double.
if (!fin.eof())
fin >> c ;
else
{
ostringstream os ;
os << "The number of data points was too short.\n" ;
throw FastFourierTransformException( os.str() ) ;
}
dft_double.push_back( c ) ;
}
#endif // SIMPLE_WAY_TO_READ_NUMBERS_FROM_FILE
#ifdef HARDCODED_DATA
num_points = 16 ;
dft_double.push_back( 1.0 + 1.0i ) ;
dft_double.push_back( 2.0 + 2.0i ) ;
dft_double.push_back( 3.0 + 3.0i ) ;
dft_double.push_back( 4.0 + 4.0i ) ;
dft_double.push_back( 5.0 + 5.0i ) ;
dft_double.push_back( 6.0 + 6.0i ) ;
dft_double.push_back( 7.0 + 7.0i ) ;
dft_double.push_back( 8.0 + 8.0i ) ;
dft_double.push_back( 9.0 + 9.0i ) ;
dft_double.push_back( 10.0 + 10.0i ) ;
dft_double.push_back( 11.0 + 11.0i ) ;
dft_double.push_back( 12.0 + 12.0i ) ;
dft_double.push_back( 13.0 + 13.0i ) ;
dft_double.push_back( 14.0 + 14.0i ) ;
dft_double.push_back( 15.0 + 15.0i ) ;
dft_double.push_back( 16.0 + 16.0i ) ;
#endif // HARDCODED_DATA
// Print out the points just read in.
fout << "\n\n\nNumber of points = " << num_points << endl ;
for (i = 0 ; i <= num_points - 1 ; ++i)
fout << "Point num. " << i << "= " << dft_double[ i ] << endl ;
// Try a forward FFT.
dft_double.fft( true ) ;
// Print the transform results.
fout << "\n\nThe discrete Fourier transform of the input data is . . . \n\n" ;
for (i = 0 ; i <= num_points - 1 ; ++i)
fout << "Point num. " << i << " = " << dft_double[ i ] << endl ;
// Try an inverse transform to get the input data back, with roundoff error.
dft_double.fft( false ) ;
// Print the inverse transform.
fout << "\n\nThe inverse transform of the transform above is . . .\n\n" ;
for (i = 0 ; i <= num_points - 1 ; ++i)
fout << "Point num. " << i << " = " << dft_double[ i ] << endl ;
// =======================================
// Test the copy constructor.
// =======================================
fout << "\n\nTest the copy constructor" << endl ;
FastFourierTransform copy_of_dft_single( dft_single ) ;
for (i = 0 ; i <= num_points - 1 ; ++i)
fout << "Point num. " << i << " = " << copy_of_dft_single[ i ] << endl ;
// =======================================
// Floating point implementation details.
// =======================================
// Set a really high precision for our printouts.
fout << scientific << setprecision( 50 ) << setw( 60 ) << endl ;
fout << "= floating point epsilons =" << endl ;
fout << "single precision floating point epsilon = " << numeric_limits::epsilon() << endl ;
fout << "single precision test: (1.0f + 0.999999f * epsilon) - 1.0f = " << ((1.0f + 0.5f * numeric_limits::epsilon()) - 1.0f) << endl ;
fout << "single precision test: (1.0f + epsilon - 1.0f = " << ((1.0f + numeric_limits::epsilon()) - 1.0f) << endl << endl ;
fout << "double precision floating point epsilon = " << numeric_limits::epsilon() << endl ;
fout << "double precision test: (1.0f + 0.999999 * epsilon) - 1.0f = " << ((1.0f + 0.5f * numeric_limits::epsilon()) - 1.0f) << endl ;
fout << "double precision test: (1.0f + epsilon - 1.0f = " << ((1.0f + numeric_limits::epsilon()) - 1.0f) << endl;
// =======================================
// Test error conditions. Only the first will execute due to
// the throw, so comment out and recompile to test them all.
// =======================================
// Number of points is less than 2.
fout << "\n\nCalling FFT with 1 point.\n" ;
// Make sure we flush all the data to file, when we're debugging. Then close the files.
fout << flush ;
fin.close() ;
fout.close() ;
FastFourierTransform dft_single2 ;
dft_single2.push_back( complex(1.0, 2.0) ) ;
dft_single2.fft( true ) ;
}
// Catch exceptions and print their text explanations.
// Clean up resources.
catch( FastFourierTransformException & e )
{
fout << "FFT exception: " << e.what() << endl ;
fin.close() ;
fout.close() ;
return 1 ;
}
catch( runtime_error & e )
{
fout << "Unexpected runtime exception: Please email the author." << e.what() << endl ;
fin.close() ;
fout.close() ;
return 1 ;
}
catch( ... )
{
fout << "Unexpected exception: Please email the author." << endl ;
fin.close() ;
fout.close() ;
return 1 ;
}
return 0 ;
} /* ====================== end of function main ============================ */