Here is my attempt to perform an FFT on a random signal with 2 tones in which I applied zero padding AFTER windowing.
(I did not apply zero padding before windowing because that would suggest that the zeros are part of the original signal which is not correct.)
The signal amplitude is around 3.5 but the FFT amplitudes are off significantly. I cannot seem to get the correct FFT amplitudes.
What am I missing?
import numpy as np
import matplotlib.pyplot as plt
# Set the sampling frequency
fs = 20000 # Hz
# Generate a random time signal with 2 pure sine tones
t = np.linspace(0, 1, int(fs))
x = np.sin(2*np.pi*500*t) + np.sin(2*np.pi*900*t) + np.random.randn(len(t))*0.5
# Compute the FFT of the signal
X = np.fft.fft(x)
# Compute the frequency vector
freqs = np.fft.fftfreq(len(x), 1/fs)
# Apply a window function to reduce spectral leakage
window = np.hanning(len(x))
x_windowed = x * window
# make signal periodic by finding the next power of 2
n_fft = 2 ** int(np.ceil(np.log2(len(t))))
# pad the signal with zeros
x_windowed_padded = np.pad(x_windowed, (0, n_fft - len(t)), mode='constant')
X_windowed_padded = np.fft.fft(x_windowed_padded)
freqs_padded = np.fft.fftfreq(len(x_windowed_padded), 1/fs)
# Plot the results
fig, ax = plt.subplots(2, 2, figsize=(10, 6))
ax[0, 0].plot(t, x)
ax[0, 0].set_xlabel('Time (s)')
ax[0, 0].set_ylabel('Amplitude')
ax[0, 0].set_title('Original Signal')
ax[0, 1].plot(freqs[:len(freqs)//2], np.abs(X)[:len(X)//2])
ax[0, 1].set_xlabel('Frequency (Hz)')
ax[0, 1].set_ylabel('Amplitude')
ax[0, 1].set_title('FFT of Original Signal')
ax[1, 0].plot(t, x_windowed)
ax[1, 0].set_xlabel('Time (s)')
ax[1, 0].set_ylabel('Amplitude')
ax[1, 0].set_title('Windowed Signal')
ax[1, 1].plot(freqs_padded[:len(freqs_padded)//2],
np.abs(X_windowed_padded)[:len(X_windowed_padded)//2])
ax[1, 1].set_xlabel('Frequency (Hz)')
ax[1, 1].set_ylabel('Amplitude')
ax[1, 1].set_title('FFT of Windowed and Padded Signal')
plt.tight_layout()
plt.show()