33
votes

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?

4

4 Answers

48
votes

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.

enter image description here

11
votes

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.

6
votes

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.

-2
votes

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.