1 /*=============================================================================
  2 |
  3 | NAME
  4 |  
  5 |      testFFT.cpp
  6 |
  7 | DESCRIPTION
  8 |  
  9 |      Test program for FFT.cpp
 10 |    
 11 | AUTHOR
 12 |
 13 |      Sean O'Connor
 14 |
 15 | LEGAL
 16 |
 17 |     FFT Version 1.4 - An FFT utility library in C++.
 18 |     Copyright (C) 2005-2024 by Sean Erik O'Connor.  All Rights Reserved.
 19 |
 20 |     This program is free software: you can redistribute it and/or modify
 21 |     it under the terms of the GNU General Public License as published by
 22 |     the Free Software Foundation, either version 3 of the License, or
 23 |     (at your option) any later version.
 24 |
 25 |     This program is distributed in the hope that it will be useful,
 26 |     but WITHOUT ANY WARRANTY; without even the implied warranty of
 27 |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 28 |     GNU General Public License for more details.
 29 |
 30 |     You should have received a copy of the GNU General Public License
 31 |     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 32 |     
 33 |     The author's address is seanerikoconnor!AT!gmail!DOT!com
 34 |     with !DOT! replaced by . and the !AT! replaced by @
 35 |     
 36 +============================================================================*/
 37 
 38 /*------------------------------------------------------------------------------
 39 |                                Include Files                                 |
 40 ------------------------------------------------------------------------------*/
 41 
 42 #include <iostream>     // Basic stream I/O.
 43 #include <cmath>        // Basic math functions.
 44 #include <complex>      // Complex data type and operations.
 45 #include <vector>       // STL vector class.
 46 #include <fstream>      // File stream I/O.
 47 #include <algorithm>    // Iterators.
 48 #include <string>       // STL string class.
 49 #include <stdexcept>    // Exceptions.
 50 #include <limits>       // Numeric limits, e.g. floating point.
 51 #include <iomanip>      // Floating point formating.
 52 
 53 using namespace std ;   // Let us omit the std:: prefix for every STL class.
 54 
 55 #include "FFT.h"        // Our own FFT stuff.
 56 
 57 string legalNotice
 58 (
 59     "\n"
 60     "FFT Version 1.2 - An FFT utility in C++.\n"
 61     "Copyright (C) 2005-2024 by Sean Erik O'Connor.  All Rights Reserved.\n"
 62    "\n"
 63     "FFT comes with ABSOLUTELY NO WARRANTY; for details see the\n"
 64     "GNU General Public License.  This is free software, and you are welcome\n"
 65     "to redistribute it under certain conditions; see the GNU General Public License\n"
 66     "for details.\n\n"
 67 ) ;
 68 
 69 
 70 /*=============================================================================
 71 |
 72 | NAME
 73 |  
 74 |     main
 75 |
 76 | DESCRIPTION
 77 |
 78 |     This program tests the FFT implementation by performing forward and
 79 |     inverse transforms on the data and testing error conditions.
 80 |    
 81 | INPUT
 82 |   
 83 |     File containing 
 84 |        
 85 |       - Number of complex points in the transform.
 86 |       - The data points (complex numbers).
 87 |
 88 |    OUTPUT
 89 |    
 90 |       - DFT of the input.
 91 |       - inverse DFT of the first DFT.
 92 |    
 93 |    EXAMPLE
 94 |
 95 |        From the command line, call
 96 |
 97 |        $  FastFourierTransform fftIn.txt fftOut.txt
 98 | 
 99 |        where the input file fftIn.txt contains
100 |
101 |        4
102 |        (1, 1)
103 |        (2, 2)
104 |        (3, 3)
105 |        (4, 4)
106 |     
107 |        and the output file fftOut.txt contains
108 |
109 |        num_points = 4
110 |        Point num. 0= (1,1)
111 |        Point num. 1= (2,2)
112 |        Point num. 2= (3,3)
113 |        Point num. 3= (4,4)
114 |        The transform of the input data is . . . 
115 |        
116 |        Point num. 0 =  (5,5)
117 |        Point num. 1 =  (-2,-1.11022e-016)
118 |        Point num. 2 =  (-1,-1)
119 |        Point num. 3 =  (0,-2)
120 |        The inverse transform of the transform above is . . .
121 |        
122 |        Point num. 0 = (1,1)
123 |        Point num. 1 = (2,2)
124 |        Point num. 2 = (3,3)
125 |        Point num. 3 = (4,4)
126 |        Point num. 0 = (1,1)
127 |        Point num. 1 = (2,2)
128 |        Point num. 2 = (3,3)
129 |        Point num. 3 = (4,4)
130 |
131 |        Calling FFT with too many points.
132 |        FFT has too many points for its sin/cos table
133 |
134 +============================================================================*/
135 
136 int main( int argc, char * argv[] )
137 {
138     cout << legalNotice ;
139 
140     int num_points = 0 ;   // Number of points in the transform.
141     int i = 0 ;
142 
143     // Open file streams for input and output getting file names
144     // from the command line.
145     if (argc != 3)
146     {
147         cerr << "Error:  Missing the names of the input and output "
148                  "files or too many files on the command line." ;
149         return 1 ;
150     }
151 
152     char * inputFileName  = argv[ 1 ] ;
153     char * outputFileName = argv[ 2 ] ;
154 
155     fstream fin ( inputFileName,  ios::in  ) ;
156     fstream fout( outputFileName, ios::out ) ;
157 
158     // Set a really high precision for our printouts.
159     // Of course some binary numbers will have repeating decimals, but we are checking to see if we get the same rounding results
160     // different computer architectures.
161     fout << scientific << setprecision( 70 ) << setw( 80 );
162 
163     if (!(fin.is_open() && fout.is_open()))
164     {
165         cerr << "Error:  Problem opening the input or output files. "
166                 "Check the file names and permissions." ;
167         return 1 ;
168     }
169 
170     try
171     {
172         //////////////////////////////////////////////////////////////////////////
173         /// Single precision FFT
174         //////////////////////////////////////////////////////////////////////////
175 
176         // Create our DFT object.
177         FastFourierTransform<float> dft_single ;
178 
179         // Clear the fft object.
180         dft_single.clear() ;
181 
182         // Read lines until end of file.
183         while (!fin.eof())
184         {
185             // Read the number of points in the transform.
186             fin >> num_points ;
187 
188             //  Check to see that the number of points in the FFT is valid.
189             if (num_points <= 0)
190                 throw FastFourierTransformException( "The number of points is out of range.\n" ) ;
191 
192             // Read in the complex data points from file into the vector.
193             for (i = 0 ;  i < num_points ;  ++i)
194             {
195                 complex<float> c ;
196                 fin >> c ;
197 
198                 dft_single.push_back( c ) ;
199             }
200         }
201 
202         fout << "* * * * * * Single precision FFT" << endl ;
203 
204         //  Print out the points just read in.
205         fout << "\n\n\nNumber of points = " << num_points << endl ;
206 
207         for (i = 0 ;  i <= num_points - 1 ;  ++i)
208             fout <<  "Point num. " << i << "= " << dft_single[ i ] << endl ;
209 
210         // Try a forward FFT.
211         dft_single.fft( true ) ;
212 
213         //  Print the transform results.
214         fout << "\n\nThe discrete Fourier transform of the input data is . . . \n\n" ;
215 
216         for (i = 0 ;  i <= num_points - 1 ;  ++i)
217             fout << "Point num. " << i << " =  " << dft_single[ i ] << endl ;
218 
219         //  Try an inverse transform to get the input data back, with roundoff error.
220         dft_single.fft( false ) ;
221 
222         // Print the inverse transform.
223         fout << "\n\nThe inverse transform of the transform above is . . .\n\n" ;
224 
225         for (i = 0 ;  i <= num_points - 1 ;  ++i)
226             fout << "Point num. " << i << " = " << dft_single[ i ] << endl ;
227 
228         //////////////////////////////////////////////////////////////////////////
229         /// Double precision FFT
230         //////////////////////////////////////////////////////////////////////////
231 
232         // Rewind the input file to the begining.
233         fin.clear() ;              // Forget we hit the end of file.
234         fin.seekg( 0, ios::beg ) ; // Move to the start of the file.
235 
236         // Create our DFT object.
237         FastFourierTransform<double> dft_double ;
238 
239         // Clear the fft object.
240         dft_double.clear() ;
241 
242         // Read lines until end of file.
243         while (!fin.eof())
244         {
245             // Read the number of points in the transform.
246             fin >> num_points ;
247 
248             //  Check to see that the number of points in the FFT is valid.
249             if (num_points <= 0)
250                 throw FastFourierTransformException( "The number of points is out of range.\n" ) ;
251 
252             // Read in the complex data points from file into the vector.
253             for (i = 0 ;  i < num_points ;  ++i)
254             {
255                 complex<double> c ;
256                 fin >> c ;
257 
258                 dft_double.push_back( c ) ;
259             }
260         }
261 
262         fout << "* * * * * * Double precision FFT" << endl ;
263 
264         //  Print out the points just read in.
265         fout << "\n\n\nNumber of points = " << num_points << endl ;
266 
267         for (i = 0 ;  i <= num_points - 1 ;  ++i)
268             fout <<  "Point num. " << i << "= " << dft_double[ i ] << endl ;
269 
270         // Try a forward FFT.
271         dft_double.fft( true ) ;
272 
273         //  Print the transform results.
274         fout << "\n\nThe discrete Fourier transform of the input data is . . . \n\n" ;
275 
276         for (i = 0 ;  i <= num_points - 1 ;  ++i)
277             fout << "Point num. " << i << " =  " << dft_double[ i ] << endl ;
278 
279         //  Try an inverse transform to get the input data back, with roundoff error.
280         dft_double.fft( false ) ;
281 
282         // Print the inverse transform.
283         fout << "\n\nThe inverse transform of the transform above is . . .\n\n" ;
284 
285         for (i = 0 ;  i <= num_points - 1 ;  ++i)
286             fout << "Point num. " << i << " = " << dft_double[ i ] << endl ;
287 
288         // =======================================
289         // Test the copy constructor.
290         // =======================================
291 
292         fout << "\n\nTest the copy constructor" << endl ;
293 
294         FastFourierTransform copy_of_dft_single( dft_single ) ;
295         for (i = 0 ;  i <= num_points - 1 ;  ++i)
296             fout << "Point num. " << i << " = " << copy_of_dft_single[ i ] << endl ;
297 
298         // =======================================
299         // Floating point implementation details.
300         // =======================================
301 
302         // Set a really high precision for our printouts.
303         fout << scientific << setprecision( 50 ) << setw( 60 ) << endl ;
304         fout << "= floating point epsilons =" << endl ;
305         fout << "single precision floating point epsilon = " << numeric_limits<float>::epsilon() << endl ;
306         fout << "single precision test: (1.0f + 0.999999f * epsilon) - 1.0f = " << ((1.0f + 0.5f * numeric_limits<float>::epsilon()) - 1.0f) << endl ;
307         fout << "single precision test: (1.0f + epsilon - 1.0f              = " << ((1.0f + numeric_limits<float>::epsilon()) - 1.0f) << endl << endl ;
308 
309         fout << "double precision floating point epsilon = " << numeric_limits<double>::epsilon() << endl ;
310         fout << "double precision test: (1.0f + 0.999999 * epsilon) - 1.0f = " << ((1.0f + 0.5f * numeric_limits<double>::epsilon()) - 1.0f) << endl ;
311         fout << "double precision test: (1.0f + epsilon - 1.0f             = " << ((1.0f + numeric_limits<double>::epsilon()) - 1.0f) << endl;
312 
313         // =======================================
314         // Test error conditions.  Only the first will execute due to
315         // the throw, so comment out and recompile to test them all.
316         // =======================================
317 
318         //  Number of points is less than 2.
319         fout << "\n\nCalling FFT with 1 point.\n" ;
320 
321         // Make sure we flush all the data to file, when we're debugging. Then close the files.
322         fout << flush ;
323         fin.close() ;
324         fout.close() ;
325 
326         FastFourierTransform<double> dft_single2 ;
327         dft_single2.push_back( complex<double>(1.0, 2.0) ) ;
328         dft_single2.fft( true ) ;
329     }
330 
331     // Catch exceptions and print their text explanations.
332     // Clean up resources.
333     catch( FastFourierTransformException & e )
334     {
335         fout << "FFT exception:  " << e.what() << endl ;
336 
337         fin.close() ;
338         fout.close() ;
339 
340         return 1 ;
341     }
342     catch( runtime_error & e )
343     {
344         fout << "Unexpected runtime exception:  Please email the author." << e.what() << endl ;
345 
346         fin.close() ;
347         fout.close() ;
348 
349         return 1 ;
350     }
351    catch( ... )
352     {
353         fout << "Unexpected exception:  Please email the author." << endl ;
354 
355         fin.close() ;
356         fout.close() ;
357 
358         return 1 ;
359     }
360 
361 
362     return 0 ;
363 
364 } /* ====================== end of function main ============================ */