00001 #ifndef TSSPECTRUM_H 00002 #define TSSPECTRUM_H 00003 00004 #include "FatalException.h" 00005 #include <fftw3.h> 00006 #include "types.h" 00007 #include <boost/bind.hpp> 00008 #include <algorithm> 00009 00010 namespace gplib 00011 { 00012 00013 /** \addtogroup tstools Time series analysis methods */ 00014 /* @{ */ 00015 00016 //! The class CTsSpectrum is used to calculate spectra from (real) time series data 00017 /*! CTsSpectrum is basically a wrapper for the fftw3 functionality for real data 00018 * it manages both the plans and the local data needed by fftw3. This class is not 00019 * thread-safe. DO NOT use it within an openmp parallelized loop or any kind of threaded program. 00020 */ 00021 class TsSpectrum 00022 { 00023 private: 00024 //! FFTW3 provides facilities to speed up multiple calculations of similar data, MultiCalc determines whether we want to use this or just a simple fftw 00025 bool MultiCalc; 00026 //! If we called CalcSpectrum before this will be true, a fftw3 calculation plan exists that we might have to take care of 00027 bool ExistsPlanForward; 00028 //! If we called CalcTimeSeries before this will be true, a previous plan exists 00029 bool ExistsPlanReverse; 00030 //! What was the size of the last calculation 00031 int oldsize; 00032 //! fftw3 wants a raw C-array, one for the timedomain 00033 double *timedomain; 00034 //! the raw C-array fftw uses for the frequency domain 00035 fftw_complex *freqdomain; 00036 //! p_forward holds the plan for forward calculations 00037 fftw_plan p_forward; 00038 //! p_reverse holds the plan for reverse calculations 00039 fftw_plan p_reverse; 00040 //! A helper function that does some stuff to prepare calculations 00041 void Prepare_Calculation(const int size); 00042 //! finish up after time series calculations 00043 void Finish_Calculation(const int size); 00044 //! We have to Assign memory for the raw c-arrays of fftw3 in the beginning and when the layout/size changes 00045 void AssignMem(const int size); 00046 //! When we reassign Memory and on exit we have to destroy the old memory structure 00047 void DestroyMem(); 00048 public: 00049 //! The constructor is explicit to prohibit conversion from bool 00050 /*! The construtor takes an optional boolean value as an argument, that determines whether we want to use fftw's multiple calculation 00051 * This makes only sense if we do a lot of calculations in the same direction and with the same length 00052 */ 00053 explicit TsSpectrum(bool WantMultiCalc = false); 00054 virtual ~TsSpectrum(); 00055 //! The member function to calculate a spectrum from a time series 00056 /*! Takes three iterators for input: beginning of input, end of input and beginning of output 00057 * Make sure the structure pointed to by freqbegin can hold at least (tsend -tsbegin)/2 +1 elements 00058 */ 00059 template<typename _InputIterator, typename _OutputIterator> 00060 void CalcSpectrum(_InputIterator tsbegin, _InputIterator tsend, 00061 _OutputIterator freqbegin, _OutputIterator freqend); 00062 //! The member function to calculate a time series from (complex) spectra 00063 /*! Takes three iterators for input: beginning of input, end of input and beginning of output 00064 * Make sure the structure pointed to by tsbegin can hold at least (freqend -freqbegin)*2 - 2 elements 00065 */ 00066 template<typename _InputIterator, typename _OutputIterator> 00067 void CalcTimeSeries(_InputIterator freqbegin, _InputIterator freqend, 00068 _OutputIterator tsbegin, _OutputIterator tsend); 00069 }; 00070 00071 //! Calculate a spectrum from the input range given by tsbegin and tsend and write to output range given by freqbegin,freqend 00072 /*! Calculate the fourier transform of a real valued time series. We specify the start and the end of both structures 00073 * to be able to check that the output structure can hold enough elements. 00074 * @param tsbegin The iterator to the start of the time series segment 00075 * @param tsend The iterator to the end of the time series segment 00076 * @param freqbegin The iterator to the start of the output structure, this has to be able to hold (tsend - tsbegin)/2 + 1 values 00077 * @param freqend The iterator to the end of the output structure, this has to be able to hold (tsend - tsbegin)/2 + 1 values 00078 */ 00079 template<typename _InputIterator, typename _OutputIterator> 00080 void TsSpectrum::CalcSpectrum(_InputIterator tsbegin, _InputIterator tsend, 00081 _OutputIterator freqbegin, _OutputIterator freqend) 00082 { 00083 const int size = distance(tsbegin, tsend); // We calculate the size of the time series 00084 if (size < 0) // if negative we probably swapped iterators, throw error message 00085 throw FatalException("Negative size of input in CalcSpectrum !"); 00086 if (distance(freqbegin, freqend) != size / 2 + 1) 00087 throw FatalException( 00088 "Time Series and Spectrum have incompatible size !"); 00089 Prepare_Calculation(size); // Do the necessary preparations for Spectra calculations 00090 copy(tsbegin, tsend, timedomain); //copy data to ftw3 data structure 00091 fftw_execute(p_forward); // do the calculation 00092 for (int i = 0; i < size / 2 + 1; ++i, ++freqbegin) 00093 //copy back, considering the different layout 00094 *freqbegin = freqdomain[i][0] + I * freqdomain[i][1]; 00095 Finish_Calculation(size); // finish up 00096 } 00097 //! Calculate a times series from the input range given by freqbegin and freqend and write to output range given by tsbegin,tsend 00098 /*! Calculate the inverse fourier transfrom from a given complex array/vector. We specify the start and the end of both structures 00099 * to be able to check that the output structure can hold enough elements. 00100 * @param freqbegin The iterator to the start of the structure holding the spectral elements 00101 * @param freqend The iterator to the end of the structure holding the spectral elements 00102 * @param tsbegin The iterator to the start of the output time series structure 00103 * @param tsend The iterator to the end of the output time series structure 00104 */ 00105 template<typename _InputIterator, typename _OutputIterator> 00106 void TsSpectrum::CalcTimeSeries(_InputIterator freqbegin, 00107 _InputIterator freqend, _OutputIterator tsbegin, _OutputIterator tsend) 00108 { 00109 if (freqbegin > freqend || tsbegin > tsend) // if beginning after end, there is something wrong, throw error 00110 throw FatalException("Negative size of input in CalcTimeSeries !"); 00111 const int size = distance(tsbegin, tsend); // the logical size for fftw3 is the length of the time series, for us too 00112 if (distance(freqbegin, freqend) != size / 2 + 1) 00113 throw FatalException( 00114 "Time Series and Spectrum have incompatible size !"); 00115 Prepare_Calculation(size); // prepare stuff for calculation 00116 for (int i = 0; freqbegin != freqend; ++freqbegin, ++i) // copy frequency data into fftw3 c-array 00117 { 00118 freqdomain[i][0] = (*freqbegin).real(); 00119 freqdomain[i][1] = (*freqbegin).imag(); 00120 } 00121 fftw_execute(p_reverse); // do calculation 00122 double factor = 1. / double(size); // we have to normalize, so that back-and forward transformation yields the same result 00123 //copy to output and multiply each element by factor 00124 std::transform(timedomain, timedomain + size, tsbegin, boost::bind< 00125 double>(std::multiplies<double>(), factor, _1)); 00126 Finish_Calculation(size); // finish up 00127 } 00128 /* @} */ 00129 } 00130 #endif // CTSSPECTRUM_H
1.5.8