Please help understand why the PSD calculation from FFT does not match with the value from Scipy signal.welch function.
According to Understanding Power Spectral Density and the Power Spectrum, $PSD = PS * \frac {N}{Fs}$ where PS is Power Spectrum.
However, as in the code below, $PSD = PS * \frac {N}{Fs}$ does not match the value from scipy.signal.welch.
# -------------------------------------------------------------------------------- # PSD = P * (N/FS) # -------------------------------------------------------------------------------- psd = power * (N/FS) plot(freqs[idx], psd[idx], xlabel='Frequency [Hz]', ylabel='PSD [P/Hz]', title="PSD from FFT") # -------------------------------------------------------------------------------- # PDF from Welch # -------------------------------------------------------------------------------- f, welch_psd = signal.welch(x, FS, 'flattop', 1024, scaling='density') plot(f, welch_psd, xlabel='Frequency [Hz]', ylabel='PSD', title="PSD from Welch") Code
from scipy import signal import numpy as np import matplotlib.pyplot as plt def plot(x, y, xlabel, ylabel, title): plt.figure(figsize=(5,2)) plt.plot(x, y) plt.xlabel(xlabel) plt.ylabel(ylabel) plt.title(title) plt.grid() plt.show() # -------------------------------------------------------------------------------- # Sampling (duration 10 secs) # -------------------------------------------------------------------------------- FS = 1e4 # Sampling Rate N = 1e5 # Total sample points time = np.arange(N) / FS # -------------------------------------------------------------------------------- # Target wave A * sin(2pi * F) # -------------------------------------------------------------------------------- A = 2*np.sqrt(2) # Amplitude F = 1234.0 # Frequency x = A * np.sin(2*np.pi * F * time) # Noise noise_power = 0.001 * FS / 2 x += np.random.normal(scale=np.sqrt(noise_power), size=time.shape) # -------------------------------------------------------------------------------- # FFT # -------------------------------------------------------------------------------- freqs = np.fft.rfftfreq(time.size, 1/FS) idx = np.argsort(freqs) yf = np.abs(np.fft.rfft(x)) amplitude = 2 * yf / N print(f"exepected amplitude {A}, actual amplitude {amplitude[np.argmax(amplitude)]}") plot(freqs[idx], amplitude[idx], xlabel='frequency [Hz]', ylabel='Amplitude', title="Amplitude from FFT") # -------------------------------------------------------------------------------- # Power Spectrum P = A**2 / 2 # -------------------------------------------------------------------------------- power = amplitude ** 2 / 2 print(f"power is {power[np.argmax(power)]}") plot(freqs[idx], power[idx], xlabel='Frequency [Hz]', ylabel='Power [A**2 / 2]', title="Power from FFT") # -------------------------------------------------------------------------------- # Power Spectram from Welch # -------------------------------------------------------------------------------- f, welch_P = signal.welch(x, FS, 'flattop', 1024, scaling='spectrum') plot(f, welch_P, xlabel='Frequency [Hz]', ylabel='Power Spectrum', title="Power from Welch") # -------------------------------------------------------------------------------- # PSD = P * (N/FS) # -------------------------------------------------------------------------------- psd = power * (N/FS) plot(freqs[idx], psd[idx], xlabel='Frequency [Hz]', ylabel='PSD [P/Hz]', title="PSD from FFT") # -------------------------------------------------------------------------------- # PDF from Welch # -------------------------------------------------------------------------------- f, welch_psd = signal.welch(x, FS, 'flattop', 1024, scaling='density') plot(f, welch_psd, xlabel='Frequency [Hz]', ylabel='PSD', title="PSD from Welch") 


signal.welch(x,FS,...)) $\endgroup$