A noob learns FFT
I have never done any time–frequency transformations. I understand the general<br>idea, but I have never worked with the details. Today I had a reason to use it,<br>but that means learning the basics first.
Pure sine wave
The following is three seconds of a single 5 Hz sine wave, recorded at a sample<br>rate of 100 Hz.11 Well, it’s synthetically generated, but that’s what it<br>represents.
If we run this through the fft function in R, we get a bunch of complex<br>numbers – as many as we have samples. In this case, that’s 300 of them. The<br>values of these numbers depend on the amplitude of the signal, so for this<br>signal, they should max out at 1. However, coming out of the R fft function,<br>these values are also scaled up by the number of samples. In the following plot,<br>I have divided by the number of samples to get the amplitudes back out.
Each point corresponds to the signal amplitude at a specific frequency. Of the<br>300 points in that plot, 298 of them sit at the origin, because there is no<br>signal with their frequency in the data.
The two points that do exist away from the origin makes it seem like our data<br>had two frequencies, but we only had one! I have to admit this is a quirk of<br>the Fourier transform I don’t fully understand, but apparently the<br>transformation looks at our 5 Hz signal with amplitude 1 and sees two signals:
One 5 Hz signal with amplitude 0.5, and
One −5 Hz signal with amplitude −0.5.
These add up to a single 5 Hz signal of amplitude 1. Or something.
Anyway, in this latest plot, the X axis (the real part of the numbers that come<br>out of fft) correspond to the cosine signal at that point, whereas the Y axis<br>(the imaginary part) correspond to the sine signal at that point. Since we only<br>had a sine wave, the real part is zero for all frequencies.
However, that is a bit of a misleading way to look at it. The reason each<br>frequency output is a complex number is because a sine plus a cosine lets us<br>represent a wave of any phase. So it’s more intuitive to look at these complex<br>numbers through their magnitude and angle. The angle is the least interesting.<br>We’ll start with that.
As we have said, the output of fft is a vector of the same length as the<br>original data. Each value in this vector corresponds to a frequency, with the<br>first value being “frequency zero”, or the mean of the data. The values after<br>that correspond to frequencies that are linearly spaced out so that the last<br>frequency is just below the sampling rate. In the following plot, I have<br>transformed the X axis from vector indices to frequencies accordingly. The<br>highest frequency in the plot is nearly the sampling rate of 100 per second.
The phase goes from \(-\pi\) to \(\pi\), and in our case it is all over the place.<br>But that’s not surprising! We don’t have any signal at most of these<br>frequencies, so we don’t care what the phase is there. What we care about is the<br>amplitude of the signal at each frequency, and that has the shape we expect.<br>Here we plot the magnitude of the complex numbers in the fft output, rather<br>than their angle.
These two bars correspond to 5 Hz and −5 Hz. We can simplify this plot by adding<br>up the mirrored amplitudes and only plotting the first half of the spectrum.<br>After all, Nyquist suggests we cannot recover signals with a frequency higher<br>than half the sample rate.
There we go! We recovered the full amplitude of the 5 Hz pure sine wave signal<br>we had. Already there were a lot more details to account for than I knew.
Truncated signal
In the above case, we presented an unnaturally clean signal. The data was even<br>constructed so the sample window captured a whole number of cycles. If we cut<br>the sample off prematurely, we get spectral leakage.
Here are the raw complex values from the fft of the same signal, except we<br>stopped recording at 2.9 seconds, instead of 3 seconds.
This looks nothing like the previous result. It’s a bit easier to see what<br>happened when we look at the amplitudes at various frequencies.
Truncation causes spectral leakage that smears out the clean 5 Hz sine wave into<br>a group of closely related signals. Practically speaking, it’s no big deal: we<br>can still perfectly reconstruct the original signal. The leakage only makes the<br>spectrum look confusing, it doesn’t actually destroy any data.
A common way to deal with truncation is to apply an envelope function to the<br>data that smoothly transitions it to zero near the endpoints. The below plots<br>show the exact same signal as before, except attenuated toward the ends.
This envelope adds some fuzziness to the spectrum, but it does effectively get<br>rid of the spectral leakage.
A noisy, composite signal
If we add a little noise to the signal, the fft still gets a representative<br>spectrum out of it.
We can see the peak around 5 Hz, but with a noise floor at all other<br>frequencies. In the next group of plots, I have added another sine wave to the<br>signal....