FFT: How to rescale x-axis


ROOT Version: 6.16/00
Platform: Ubuntu 20.04
Compiler: gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.2)


Dear co-rooters,

I am trying to perform a FFT on a simple sinusodial function, copying the example FFT.C.

I am creating a TF1 object and then a TH1D histogram that samples from that function. Here’s how it looks

This function has a period of 2Pi seconds (assume it’s a signal) and therefore a frequency of 1/2Pi=0.16 Hz. If I do a Fourier transform, I would expect a peak at 0.16 Hz.

I understand that TH1::FFT won’t give me the actual frequency and I will need to scale the x-axis. The unscaled FFT looks like this:

There’s a peak at 0 (no idea why) and a peak around 250 which I think corresponds to the number of bins of my input histogram. If I change the number of bins to 25, then the peak appears at 25. So the units are bins or indices. If that’s correct, I would expect the frequency to be ta 1/125, since one period is completed at bin# 125 (there are 2 periods or cycles over the range of the input histogram).

I (think) I’m properly rescaling the x-axis, but I get the following:

Instead of a peak at 0.16, I get a peak at 20 and a peak at 0.2.
What am I doing wrong?

Here’s my code

#include "TF1.h"
#include "TH1D.h"
#include "TMath.h"
#include "TVirtualFFT.h"

void FFT() {

   // Define sin(x) function   
   TF1 *fsin = new TF1("fsin", "sin(x)", 0, 4*TMath::Pi());
   fsin->Draw();

   // Define histogram and fill with sin(x) values
   int n = 250;  // number of bins
   TH1D *hsin = new TH1D("hsin", "hsin", n+1, 0, 4*TMath::Pi());
   
   // Fill the histogram with function values
    double x;
   for (int i=0; i<n; ++i) {
      x = (Double_t(i)/n)*(4*TMath::Pi());
      hsin->SetBinContent(i+1, fsin->Eval(x));
   }
   hsin->Draw("same");

   // Compute the Fourier transform and look at the magnitude of the output
   TH1 *hm = nullptr;
   TVirtualFFT::SetTransform(nullptr);
   hm = hsin->FFT(hm, "MAG");
   hm->SetTitle("Magnitude of the Fourier Transform");
   //hm->Scale(1./sqrt(n));  // Rescale by sqrt(n)

   // Now we need to rescale the x-axis to show real frequencies
   double low_end_sin   = hsin->GetBinLowEdge(1);
   double upper_end_sin = hsin->GetBinCenter(n)+hsin->GetBinWidth(n)/2.;
   double range_sin     = upper_end_sin - low_end_sin;
   
   int bins_FFT         = hm->GetNbinsX();
   double low_end_FFT   = hm->GetBinLowEdge(1)/range_sin;
   double upper_end_FFT = ( hm->GetBinCenter(bins_FFT)+hm->GetBinWidth(bins_FFT)/2. )/range_sin;
   
   TH1D *h_FFT = new TH1D("h_FFT", "Real Frequencies", bins_FFT, low_end_FFT, upper_end_FFT);

   // Set the new x-axis (real frequencies) and fill the histogram with magnitudes
   for (int i=0; i <bins_FFT; ++i) {
   		h_FFT->SetBinContent(i+1, hm->GetBinContent(i+1));
   }

   hm->Draw("histo");
   h_FFT->Draw("histo");
}

Any help will be much appreciated!

When i run you macro I get the follwing error mesage:

 warning: null passed to a callee that requires a non-null argument [-Wnonnull]
   hm->SetTitle("Magnitude of the Fourier Transform");
   ^~

Do you get it also ?

Side note:
An easier way to rescale your x axis is to just call h->GetXaxis()->SetLimits(min, max);

Side note 2:
See TH1::TransformHisto helper function

@couet that’s maybe because of missing fftw3 package installation

No, I don’t get this error. I’m no compiling it though; I’m just loading .L macro.C

Thanks for the tip! Much easier!
The thing I’m not sure about is if the x-axis rescaling I’m doing, is the correct one.

Find attached this example where you can see how the x axis rescaling should work.

#include "TF1.h"
#include "TH1D.h"
#include "TMath.h"
#include "TVirtualFFT.h"
#include "TCanvas.h"

void fft() {
   // Define histogram and fill with sin(2pi*f*x) values
   const int n = 2500;  // number of bins
   TH1D *hsin = new TH1D("hsin", "hsin", n, 0, 10);
   hsin->GetXaxis()->SetTitle("Time / s");
   // Fill the histogram with function values
   const double freq = 20; // Hz
   for (int i=0; i<n; ++i) {
      hsin->SetBinContent(i+1, TMath::Sin(hsin->GetXaxis()->GetBinCenter(i+1)*freq*2.*TMath::Pi()));
   }
   auto c0 = new TCanvas();
   hsin->Draw();
   // fsin->Draw("same");

   // Compute the Fourier transform and look at the magnitude of the output
   TH1 *hm = nullptr;
   TVirtualFFT::SetTransform(nullptr);
   hm = hsin->FFT(hm, "MAG");
   hm->SetTitle("Magnitude of the Fourier Transform");
   //hm->Scale(1./sqrt(n));  // Rescale by sqrt(n)

   auto c = new TCanvas();
   c->SetLogy();
   hm->GetXaxis()->SetTitle("f / Hz");
   hm->GetXaxis()->SetLimits(0, n/hsin->GetXaxis()->GetBinUpEdge(n+1)); // Rescale x axis to Hz
   hm->Draw("histo");
}

The “peak” at the end is related to the FFT mirroring, see discrete signals - Why is the FFT "mirrored"? - Signal Processing Stack Exchange (actually, everything between 125 to 250 Hz is the mirror of 0 to 125 Hz). matlab - Why can the FFT always be mirrored in the middle of the x axis? - Signal Processing Stack Exchange

I was missing the 2Pi*f…
Thanks for the help!