FancyFFTs.cxx
1 #include "FancyFFTs.h"
2 
3 std::map<std::pair<int, int>, fftw_plan> fRealToComplex;
4 std::map<std::pair<int, int>, fftw_plan> fComplexToReal;
5 std::map<std::pair<int, int>, double*> fReals;
6 std::map<std::pair<int, int>, complex<double>*> fComplex;
7 
8 
9 
10 
11 
12 //---------------------------------------------------------------------------------------------------------
19 bool FancyFFTs::makeNewPlanIfNeeded(int len, int threadInd){
20 
21  std::pair<int, int> key(len, threadInd);
22  std::map<std::pair<int, int>,fftw_plan>::iterator it = fRealToComplex.find(key);
23  if(it==fRealToComplex.end()){
24  // std::cout << len << "\t" << threadInd << std::endl;
25  fReals[key] = (double*) fftw_malloc(sizeof(double)*len);
26  fComplex[key] = (complex<double>*) fftw_malloc(sizeof(fftw_complex)*len);
27  fRealToComplex[key] = fftw_plan_dft_r2c_1d(len,fReals[key],(fftw_complex*)fComplex[key],FFTW_MEASURE);
28  fComplexToReal[key] = fftw_plan_dft_c2r_1d(len,(fftw_complex*)fComplex[key],fReals[key],FFTW_MEASURE);
29  return true;
30  }
31  else{
32  return false;
33  }
34 }
35 
36 
37 
38 
39 
40 //---------------------------------------------------------------------------------------------------------
52 TGraph* FancyFFTs::getPowerSpectrumTGraph(int len, double* input, double dt, FancyFFTs::conventionFlag normFlag, bool dBScale, int threadInd){
53  double* powSpec = getPowerSpectrum(len, input, dt, normFlag, threadInd);
54  int numFreqs = getNumFreqs(len);
55  if(dBScale==true){
56  for(int freqInd=0; freqInd < numFreqs; freqInd++){
57  powSpec[freqInd] = 10*TMath::Log10(powSpec[freqInd]);
58  }
59  }
60  return new TGraph(numFreqs, getFreqArray(len, dt), powSpec);
61 }
62 
63 
64 
65 
66 //---------------------------------------------------------------------------------------------------------
77 double* FancyFFTs::getPowerSpectrum(int len, double* input, double dt, FancyFFTs::conventionFlag normFlag, int threadInd){
78 
79  return FancyFFTs::getPowerSpectrum(len, input, dt, normFlag, NULL, threadInd);
80 }
81 
82 
83 
84 
85 
86 //---------------------------------------------------------------------------------------------------------
98 double* FancyFFTs::getPowerSpectrum(int len, double* input, double dt, FancyFFTs::conventionFlag normFlag, double* outputPtr, int threadInd){
99 
100  /*
101  FancyFFTs::conventionFlag determines (you guessed it) the normalization of the power spectrum.
102  For a thorough explanation see Ryan's presentation:
103  http://www.hep.ucl.ac.uk/~rjn/saltStuff/fftNormalisation.pdf
104  */
105 
106  const double ohms = 50; // Assuming 50 Ohm termination
107  double conventionNorm = 1;
108  switch (normFlag){
109  case kSum:
110  /* Sum of output powSpec == sum over V[i]*V[i] for each V[i] in input */
111  conventionNorm = 1./len;
112  break;
113 
114  case kAverage:
115  /*
116  Sum of output powSpec == sum over V[i]*V[i]/len for each V[i] in input
117  Tells you power per unit sample? ANITAns probably don't want this one.
118  */
119  conventionNorm = 1./(len*len);
120  break;
121 
122  case kTimeIntegral:
123  /*
124  Sum of output powSpec == sum over dt*V[i]*V[i] for each V[i] in input
125  Tell you total time integrated power.
126  */
127  conventionNorm = dt/len;
128  break;
129 
130 
131  case kPowSpecDensity:
132  /*
133  Sum of df*psd[i] for each psd[i] in output == sum over dt*V[i]*V[i]/df for each V[i] in input
134  == sum over N*dt*dt*V[i]*V[i] for each V[i] in input
135  Makes this function return the "power spectral density".
136  i.e. frequency bins are normalized such that zero padding the waveform won't affect bin content.
137  */
138 
139  conventionNorm = dt*dt; /* since df = 1./(len*dt) */
140  break;
141 
142 
143  case kPowSpecDensity_dBm:
144  /*
145  Sum of df*psd[i] for each psd[i] in output == sum over dt*V[i]*V[i]/df/ohms for each V[i] in input
146  == sum over N*dt*dt*V[i]*V[i]/ohms for each V[i] in input
147  Makes this function return the "power spectral density" (assuming 50 Ohm termination).
148  i.e. frequency bins are normalized such that zero padding the waveform won't affect bin content.
149  */
150 
151  conventionNorm = dt*dt/ohms; /* since df = 1./(len*dt) */
152  break;
153 
154  default:
155  /* You shouldn't get here and now I'm going to tell you that. */
156  std::cerr << "Invalid FancyFFTs::conventionFlag in FancyFFTs::getPowerSpectrum(int len, double* input, double dt, FancyFFTs::conventionFlag normFlag) " << std::endl;
157  }
158 
159 
160  /*
161  Do FFT without putting the output in a new array.
162  we need to normalize the output so lets do that when we move it.
163  */
164  doFFT(len, input, false, threadInd);
165 
166 
167  std::pair<int, int> key(len, threadInd);
168 
169  /* Get the fftw_malloc'd array that the plan uses */
170  complex<double>* rawFftOutputPtr = (complex<double>*) fComplex[key];
171 
172  const int powSpecLen = getNumFreqs(len);
173 
174  double* powSpec = NULL;
175  if(outputPtr==NULL){
176  powSpec = new double[powSpecLen];
177  }
178  else{
179  powSpec = outputPtr;
180  }
181 
182  /* Pedantry with the normalization (only half the nyquist bin if there's an even number of bins) */
183  bool halfPowerNquist = (len%2)==0 ? true : false;
184 
185  for(int i=0; i<powSpecLen; i++){
186 
187  /* Take account of symetry in normalization of power spectrum */
188  double symmetryFactor = 2;
189  if(i==0 || (halfPowerNquist && i==powSpecLen-1)){
190  symmetryFactor = 1;
191  }
192 
193  powSpec[i] = symmetryFactor*conventionNorm*(pow(std::abs(rawFftOutputPtr[i]), 2));
194  }
195  return powSpec;
196 }
197 
198 
199 
200 
201 
202 //---------------------------------------------------------------------------------------------------------
212 complex<double>* FancyFFTs::doFFT(int len, double* input, bool copyOutputToNewArray, int threadInd){
213  return doFFT(len, input, NULL, copyOutputToNewArray, threadInd);
214 }
215 
216 
217 
218 //---------------------------------------------------------------------------------------------------------
228 complex<double>* FancyFFTs::doFFT(int len, double* input, complex<double>* output, int threadInd){
229  return doFFT(len, input, output, true, threadInd);
230 }
231 
232 
233 
234 //---------------------------------------------------------------------------------------------------------
245 complex<double>* FancyFFTs::doFFT(int len, double* input, complex<double>* output, bool copyOutputToNewArray, int threadInd){
246  /*
247  Using complex<double> instead of the typdef fftw_complex double[2]
248  because CINT has a better time with it, even though it's (apparently) exactly the same.
249  */
250 
251  std::pair<int, int> key(len, threadInd);
252  makeNewPlanIfNeeded(len, threadInd);
253 
254  memcpy(fReals[key], input, sizeof(double)*len);
255 
256  fftw_execute(fRealToComplex[key]);
257 
258  if(copyOutputToNewArray==true){
259  int numFreqs = getNumFreqs(len);
260  complex<double>* theOutput = output;
261  if(theOutput==NULL){
262  theOutput = new complex<double>[numFreqs];
263  }
264 
265  /* Seems to work, see http://www.fftw.org/doc/Complex-numbers.html */
266  memcpy(theOutput, fComplex[key], sizeof(fftw_complex)*numFreqs);
267  return theOutput;
268  }
269  else{
270  return NULL;
271  }
272 }
273 
274 
275 
276 
277 
278 //---------------------------------------------------------------------------------------------------------
288 double* FancyFFTs::doInvFFT(int len, complex<double>* input, bool copyOutputToNewArray, int threadInd){
289  return doInvFFT(len, input, NULL, copyOutputToNewArray, threadInd);
290 }
291 
292 
293 
294 
295 
296 //---------------------------------------------------------------------------------------------------------
306 double* FancyFFTs::doInvFFT(int len, complex<double>* input, double* output, int threadInd){
307  return doInvFFT(len, input, output, true, threadInd);
308 }
309 
310 
311 
312 
313 
314 //---------------------------------------------------------------------------------------------------------
325 double* FancyFFTs::doInvFFT(int len, complex<double>* input, double* output, bool copyOutputToNewArray, int threadInd){
326 
327  /*
328  Normalization of 1/N done in this function.
329  Note: fftw_plan_c2r_1d USES AND MESSES UP THE INPUT ARRAY when executed.
330  */
331 
332  std::pair<int, int> key(len, threadInd);
333 
334  makeNewPlanIfNeeded(len, threadInd);
335  int numFreqs = getNumFreqs(len);
336 
337  complex<double>* tempVals = (complex<double>*) fComplex[key];
338 
339  // In the case that we cleverly left the fft in the internal array skip the copy
340  if(tempVals!=input){
341  // In fact this is undefined behaviour!
342  memcpy(tempVals, input, sizeof(fftw_complex)*numFreqs);
343  }
344 
345  // Do inverse FFT.
346  fftw_execute(fComplexToReal[key]);
347 
348  /* Normalization needed on the inverse transform */
349  double* invFftOutPtr = fReals[key];
350  for(int i=0; i<len; i++){
351  invFftOutPtr[i]/=len;
352  }
353 
354  double* theOutput = output;
355  if(copyOutputToNewArray==true){
356  if(theOutput==NULL){
357  theOutput = new double[len];
358  }
359  memcpy(theOutput, invFftOutPtr, sizeof(double)*len);
360  return theOutput;
361  }
362  else{
363  return NULL;
364  }
365 }
366 
367 
368 
369 
370 //---------------------------------------------------------------------------------------------------------
378  return pow(2, TMath::CeilNint(TMath::Log2(len)));
379 }
380 
381 
382 
383 
384 
385 
386 //---------------------------------------------------------------------------------------------------------
394 double* FancyFFTs::getFreqArray(int len, double dt){
395  /* Once and only once... */
396  int numFreq = getNumFreqs(len);
397  double df = 1./(dt*len);
398  double* freqArray = new double[numFreq];
399  for(int i=0; i<numFreq; i++){
400  freqArray[i] = df * i;
401  }
402  return freqArray;
403 }
404 
405 
406 
407 
408 
409 //---------------------------------------------------------------------------------------------------------
417  return (len/2 + 1);
418 }
419 
420 
421 
422 
423 
424 //---------------------------------------------------------------------------------------------------------
436 complex<double>* FancyFFTs::zeroPadFFT(complex<double>* fft, int numSamples, int numSamplesUpsampled){
437  return zeroPadFFT(fft, NULL, numSamples, numSamplesUpsampled);
438 }
439 
440 
441 
442 
443 
444 //---------------------------------------------------------------------------------------------------------
457 complex<double>* FancyFFTs::zeroPadFFT(complex<double>* fft, complex<double>* output, int numSamples, int numSamplesUpsampled){
458 
459  const int numFreqs = getNumFreqs(numSamples);
460  const int numFreqsPadded = getNumFreqs(numSamplesUpsampled);
461 
462  complex<double>* fftPadded = NULL;
463  if(output==NULL){
464  fftPadded = new complex<double>[numFreqsPadded];
465  }
466  else{
467  fftPadded = output;
468  }
469 
470  // this is the mistake!!!!
471  // Double_t scale = numFreqsPadded/numFreqs;
472 
473  // Here I scale the padded FFT so that it is as if I fourier transformed a longer waveform.
474  // (There is a scale factor of length picked up from a forward FFT.)
475  Double_t scale = numSamplesUpsampled/numSamples;
476 
477  for(int freqInd=0; freqInd<numFreqs; freqInd++){
478  fftPadded[freqInd].real(fft[freqInd].real()*scale);
479  fftPadded[freqInd].imag(fft[freqInd].imag()*scale);
480  }
481  for(int freqInd=numFreqs; freqInd<numFreqsPadded; freqInd++){
482  fftPadded[freqInd] = 0;
483  }
484 
485  // this factor undoes the effect of the the nyquist frequency containing half the power relative
486  // to the other bins (due to them containing the negative frequency components)
487  const Double_t sqrtHalf = TMath::Sqrt(0.5);
488  fftPadded[numFreqs-1].real(sqrtHalf*fftPadded[numFreqs-1].real());
489  fftPadded[numFreqs-1].imag(sqrtHalf*fftPadded[numFreqs-1].imag());
490 
491  return fftPadded;
492 }
493 
494 
495 
496 
497 //---------------------------------------------------------------------------------------------------------
502 
503  std::vector<std::pair<int, int> > keys;
504 
505  std::map<std::pair<int, int>,fftw_plan>::iterator it;
506  for(it = fRealToComplex.begin(); it != fRealToComplex.end(); it++){
507  keys.push_back(it->first);
508  }
509 
510  /* Pretty sure in advance of testing that this list is not guarenteed to be sorted. */
511  // std::vector<std::pair<int, int>> sortedIndices(keys.size());
512  // TMath::Sort(int(keys.size()), &keys[0], &sortedIndices[0], kFALSE);
513 
514  /* Print to terminal */
515  std::cout << "Plan lengths in memory = [";
516  for(int i=0; i<int(keys.size()); i++){
517  // int j = sortedIndices.at(i);
518  // std::cout << keys.at(j);
519  std::cout << "(" << keys.at(i).first << ", " << keys.at(i).second << ")";
520  if(i < int(keys.size())-1 ) std::cout << ", ";
521  }
522  std::cout << "]" << std::endl;
523 
524  return int(keys.size());
525 }
526 
527 
528 
529 
530 
531 //---------------------------------------------------------------------------------------------------------
541 double* FancyFFTs::crossCorrelate(int len, double* v1, double* v2, int threadInd){
542  /*
543  Cross correlation is the same as bin-by-bin multiplication in the frequency domain (with a conjugation).
544  Will assume lengths are the same for now.
545  */
546 
547  /* Store output of FFT2 in tempVals */
548  complex<double>* tempVals2 = doFFT(len, v2, true, threadInd);
549 
550  /* Leave output of FFT1 in internal arrays to avoid an unnecessary copy */
551  doFFT(len, v1, false, threadInd);
552 
553  std::pair<int, int> key(len, threadInd);
554 
555  /* Get pointer to internal array */
556  complex<double>* tempVals1 = (complex<double>*) fComplex[key];
557 
558  /* Take the product */
559  int numFreqs = getNumFreqs(len);
560  for(int i=0; i<numFreqs; i++){
561  tempVals1[i] *= std::conj(tempVals2[i]);
562  }
563 
564  delete [] tempVals2;
565 
566  /* Product back to time domain */
567  double* crossCorr = doInvFFT(len, tempVals1, true, threadInd);
568 
569  /*
570  Picked up two factors of len when doing forward FFT, only removed one doing invFFT.
571  This takes out the second factor.
572  */
573  for(int i=0; i<len; i++){
574  crossCorr[i] /= len;
575  }
576 
577  return crossCorr;
578 }
579 
580 
581 
582 
583 //---------------------------------------------------------------------------------------------------------
593 double* FancyFFTs::crossCorrelate(int len, complex<double>* fft1, complex<double>* fft2, int threadInd){
594  return crossCorrelate(len, fft1, fft2, NULL, threadInd);
595 }
596 
597 
598 
599 
600 
601 //---------------------------------------------------------------------------------------------------------
612 double* FancyFFTs::crossCorrelate(int len, complex<double>* fft1, complex<double>* fft2,
613  double* output, int threadInd){
614  /*
615  Cross correlation is the same as bin-by-bin multiplication (and some conjugation) in the frequency domain.
616  Will assume lengths are the same for now.
617  */
618 
619 
620  /* Stops tempVals returning NULL, normally done in doFFT step.
621  But the nice thing about this class is it means that I won't be duplicating work. */
622  makeNewPlanIfNeeded(len, threadInd);
623 
624  std::pair<int, int> key(len, threadInd);
625 
626  /* Grab array associated with plan from internal memory */
627  complex<double>* tempVals = (complex<double>*) fComplex[key];
628 
629  // TThread::Lock();
630  // std::cout << "threadInd = " << threadInd << "\tfComplex[key] = " << tempVals << std::endl << std::endl;
631  // TThread::UnLock();
632 
633 
634  /* Take the product */
635  int numFreqs = getNumFreqs(len);
636 
637  for(int i=0; i<numFreqs; i++){
638  tempVals[i] = fft1[i]*std::conj(fft2[i]);
639  }
640 
641  /* Product back to time domain */
642  double* crossCorr = output;
643  if(crossCorr==NULL){
644  /* Allocates new memory */
645  crossCorr = doInvFFT(len, tempVals, true, threadInd);
646  }
647  else{
648  /* Does not allocate new memory */
649  crossCorr = doInvFFT(len, tempVals, crossCorr, true, threadInd);
650  }
651 
652 
653  /*
654  Picked up two factors of len when doing forward FFT, only removed one doing invFFT.
655  This takes out the second factor.
656  */
657  for(int i=0; i<len; i++){
658  // std::cout << crossCorr[i] << std::endl;
659  crossCorr[i] /= len;
660  }
661 
662  return crossCorr;
663 }
664 
665 
666 //---------------------------------------------------------------------------------------------------------
673 double* FancyFFTs::getRealArray(std::pair<Int_t, Int_t> key){
674  return fReals[key];
675 }
int extendToPowerOfTwo(int len)
Find the next highest power of two.
Definition: FancyFFTs.cxx:377
TGraph * getPowerSpectrumTGraph(int len, double *input, double dt, conventionFlag normFlag, bool dBScale, int threadInd=0)
Creates a power spectrum TGraph from an array of doubles.
Definition: FancyFFTs.cxx:52
double * getPowerSpectrum(int len, double *input, double dt, conventionFlag normFlag, int threadInd=0)
Creates the power spectrum in an array of doubles of length (len/2 + 1).
Definition: FancyFFTs.cxx:77
complex< double > * zeroPadFFT(complex< double > *fft, int numSamples, int numSamplesUpsampled)
Zero padds FFTts,.
Definition: FancyFFTs.cxx:436
double * crossCorrelate(int len, double *v1, double *v2, int threadInd=0)
Cross correlates the two input arrays.
Definition: FancyFFTs.cxx:541
complex< double > * doFFT(int len, double *input, bool copyOutputToNewArray, int threadInd=0)
Does a forward fast fourier transform on a real input array.
Definition: FancyFFTs.cxx:212
int printListOfKeys()
Prints the list of keys of plans to stdout.
Definition: FancyFFTs.cxx:501
double * getFreqArray(int len, double dt)
Get the array of frequencies to go with FFT output, determined by the tranform length and dt...
Definition: FancyFFTs.cxx:394
int getNumFreqs(int len)
Get the number of frequencies from the number of samples.
Definition: FancyFFTs.cxx:416
bool makeNewPlanIfNeeded(int len, int threadInd=0)
Creates new fftw plan to handle 1D transforms of length len if they haven&#39;t been allocated already...
Definition: FancyFFTs.cxx:19
double * getRealArray(std::pair< Int_t, Int_t > key)
Get the real array of the internal fftw memory. Do not delete this!
Definition: FancyFFTs.cxx:673
conventionFlag
Flag to pass to FancyFFT functions specifying normalization of power spectra.
Definition: FancyFFTs.h:54
double * doInvFFT(int len, complex< double > *input, bool copyOutputToNewArray, int threadInd=0)
Does an inverse fast fourier transform on an array of complex<double>s.
Definition: FancyFFTs.cxx:288