ThinkDSP
This notebook contains code examples from Chapter 7: Discrete Fourier Transform
Copyright 2015 Allen Downey
Here's the definition of ComplexSinusoid, with print statements to display intermediate results.
Here's an example:
The simplest way to synthesize a mixture of signals is to evaluate the signals and add them up.
Here's an example that's a mixture of 4 components.
Now we can plot the real and imaginary parts:
The real part is a mixture of cosines; the imaginary part is a mixture of sines. They contain the same frequency components with the same amplitudes, so they sound the same to us:
We can express the same process using matrix multiplication.
And it should sound the same.
To see the effect of a complex amplitude, we can rotate the amplitudes by 1.5 radian:
Rotating all components by the same phase offset changes the shape of the waveform because the components have different periods, so the same offset has a different effect on each component.
Analysis
The simplest way to analyze a signal---that is, find the amplitude for each component---is to create the same matrix we used for synthesis and then solve the system of linear equations.
Using the first 4 values from the wave array, we can recover the amplitudes.
If we define the freqs
from 0 to N-1 and ts
from 0 to (N-1)/N, we get a unitary matrix.
To check whether a matrix is unitary, we can compute , which should be the identity matrix:
The result is actually , so in general we have an extra factor of to deal with, but that's a minor problem.
We can use this result to write a faster version of analyze1
:
Now we can write our own version of DFT:
And compare it to analyze2:
The result is close to amps * 4
.
We can also compare it to np.fft.fft
. FFT stands for Fast Fourier Transform, which is an even faster implementation of DFT.
The inverse DFT is almost the same, except we don't have to transpose and we have to divide through by .
We can confirm that dft(idft(amps))
yields amps
:
Real signals
Let's see what happens when we apply DFT to a real-valued signal.
wave
is a 500 Hz sawtooth signal sampled at 10 kHz.
hs
is the DFT of this wave, and amps
contains the amplitudes.
The DFT assumes that the sampling rate is N per time unit, for an arbitrary time unit. We have to convert to actual units -- seconds -- like this:
Also, the DFT of a real signal is symmetric, so the right side is redundant. Normally, we only compute and plot the first half:
Let's get a better sense for why the DFT of a real signal is symmetric. I'll start by making the inverse DFT matrix for .
And the DFT matrix:
And a triangle wave with 8 elements:
Here's what the wave looks like.
Now let's look at rows 3 and 5 of the DFT matrix:
They are almost the same, but row5 is the complex conjugate of row3.
When we multiply the DFT matrix and the wave array, the element with index 3 is:
And the element with index 5 is:
And they are the same, within floating point error.
Let's try the same thing with a complex signal:
Now the elements with indices 3 and 5 are different:
Visually we can confirm that the FFT of the real signal is symmetric:
And the FFT of the complex signal is not.
Another way to think about all of this is to evaluate the DFT matrix for different frequencies. Instead of through , let's try .
So you can think of the second half of the DFT as positive frequencies that get aliased (which is how I explained them), or as negative frequencies (which is the more conventional way to explain them). But the DFT doesn't care either way.
The thinkdsp
library provides support for computing the "full" FFT instead of the real FFT.