Notes on DFT Result
I always forget about the rules here - writing them down for future reference.
DFT Magnitude
The N-point DFT returns a set of N complex values. The first N/2 values (0 to N/2-1) represent the positive frequencies (upward from left to right) and the N/2 to N-1 values represent the negative frequencies (downward from left to right).
The first value (0) is the DC component.
The N/2 value is the Nyquist (fs/2) frequency. By convention it is considered to be a negative frequency. It’s the largest negative frequency.
If the magnitude of the complex value in a bin is scaled by 2/N we get the Vp of the original signal. Except for the DC bin (bin 0) and the Nyquist bin (bin N/2), which should only be scaled by 1/N since they contains “both sides” of the energy.
When the input signal is a cosine, possibly with a DC component:
- Bin 0 is all of the DC energy.
- Bin N/2 is all of the energy at the Nyquist frequency (fs / 2).
- Bin 1 and Bin N-1 each contain half of the smallest non-zero frequency (fs / N).
- Bins N/2-1 and N/2+1 each contain half of the energy which is “one bin less” than the Nyquist rate. (fs / 2) - (fs / N).
Note that if the input signal is a sin at the Nyquist frequency the N/2 bin is zero.
The sine component (imaginary part): At k=N/2, the symmetry relation dictates that Im(X[N/2])=-Im(X[N-N/2])=-Im(X[N/2]). The only number that is equal to its own negative is zero. Therefore, the imaginary part of the Nyquist bin is always zero, regardless of the input signal. This means the signal component at the Nyquist frequency is purely a cosine function, with no phase shift.
Goertzel Algorithm
Here’s a simple implementation that returns the magnitude and angle of the selected frequency. ft is the desired frequency and fs is the sample rate.
def goertzel_polar(s, ft, fs):
g_w = 2.0 * np.pi * ft / fs
g_cw = math.cos(g_w)
g_sw = math.sin(g_w)
g_c = 2.0 * g_cw
g_z1 = 0
g_z2 = 0
for v in s:
g_z0 = v + g_c * g_z1 - g_z2
g_z2 = g_z1
g_z1 = g_z0
g_r = g_cw * g_z1 - g_z2
g_i = g_sw * g_z1
# NOTE: The expensive square root and arctan are here:
return np.abs(complex(g_r, g_i)), np.angle(complex(g_r, g_i))
The scaling rules are exactly like the full DFT: you need to scale the magnitude by (2.0 / N) to get the Vp of the original signal.
RMS Voltage
Vrms = Vp / √2 = 0.707 * Vp
So a pure sinusoid (Vp=1, Vpp=2) has a Vrms of 0.707.