I'm looking for how to turn the frequency axis in a fft (taken via scipy.fftpack.fftfreq) into a frequency in Hertz, rather than bins or fractional bins.
I tried to code below to test out the FFT:
t = scipy.linspace(0,120,4000)
acc = lambda t: 10*scipy.sin(2*pi*2.0*t) + 5*scipy.sin(2*pi*8.0*t) + 2*scipy.random.random(len(t))
signal = acc(t)
FFT = abs(scipy.fft(signal))
FFT = scipy.fftpack.fftshift(FFT)
freqs = scipy.fftpack.fftfreq(signal.size)
pylab.plot(freqs,FFT,'x')
pylab.show()
The sampling rate should be 4000 samples / 120 seconds = 33.34 samples/sec.
The signal has a 2.0 Hz signal, a 8.0 Hz signal, and some random noise.
I take the FFT, grab the frequencies, and plot it. The numbers are pretty nonsensical. If I multiply the frequencies by 33.34 (the sampling frequency), then I get peaks at about 8 Hz and 15 Hz, which seems wrong (also, the frequencies should be a factor of 4 apart, not 2!).
Any thoughts on what I'm doing wrong here?
I think you don't need to do fftshift(), and you can pass sampling period to fftfreq():
import scipy
import scipy.fftpack
import pylab
from scipy import pi
t = scipy.linspace(0,120,4000)
acc = lambda t: 10*scipy.sin(2*pi*2.0*t) + 5*scipy.sin(2*pi*8.0*t) + 2*scipy.random.random(len(t))
signal = acc(t)
FFT = abs(scipy.fft(signal))
freqs = scipy.fftpack.fftfreq(signal.size, t[1]-t[0])
pylab.subplot(211)
pylab.plot(t, signal)
pylab.subplot(212)
pylab.plot(freqs,20*scipy.log10(FFT),'x')
pylab.show()
from the graph you can see there are two peak at 2Hz and 8Hz.
scipy.fftpack.fftfreq(n, d) gives you the frequencies directly. If you set d=1/33.34
, this will tell you the frequency in Hz for each point of the fft.
The frequency width of each bin is (sampling_freq / num_bins).
A more fundamental problem is that your sample rate is not sufficient for your signals of interest. Your sample rate is 8.3 Hz; you need at least 16Hz in order to capture an 8Hz input tone.1
1. To all the DSP experts; I'm aware that it's actually BW that's relevant, not max frequency. But I'm assuming the OP doesn't want to do undersampled data acquisition.
Your equation is messed up.
fs = 33.33
df1 = 2*pi * (2.0/fs)
df2 = 2*pi * (5.0/fs)
x = [10*sin(n*df1) + 5*sin(n*df2) + 2*random.random() for n in range(4000)]
This gives you 4000 samples of your function, sampled at 33.33 Hz, representing 120 seconds of data.
Now take your FFT. Bin 0 will hold the DC result. Bin 1 will be 33.33, bin 2 will be 66.66, etc..
Edit: I forget to mention that, since your sampling rate is 33.33 Hz, the maximum frequency that can be represented will be fs/2, or 16.665 Hz.