我应该在 Python 中规范化 FFT 吗?

信息处理 fft Python 功率谱密度 IFFT 正常化
2022-02-05 01:10:36

很抱歉,如果经常提出这个问题,我似乎无法理解已经存在的答案。我正在使用几种形式的傅里叶变换,包括 FFT、PSD 和频谱图。我不确定在计算信号的傅立叶变换后,我是否应该通过某个因素对结果进行归一化。对于上下文,我对比较信号没有任何兴趣,我只需要我的输出在数学上是准确的。

我已经看到一些答案说 DFT 必须通过 1/N 因子进行归一化 - 我将其解释为 IFFT 中的 1/N 因子,对吗?还是有其他原因您必须按 1/N 标准化?

不过,真正让我困惑的是,我被告知要除以采样频率的答案。这个答案意味着它相当于除以 N-这是否与在 1 秒段上计算的变换或沿着这些线的什么有关?

我正在使用的一个教程以这种方式规范化,我不确定它来自哪里。他们构建了一个美白程序,如下所示:

def whiten(strain, interp_psd, dt):     
    Nt = len(strain)
    freqs = np.fft.rfftfreq(Nt, dt)

    # whitening: transform to freq domain, divide by asd, then transform back, 
    # taking care to get normalization right.
    hf = np.fft.rfft(strain)
    norm = 1./np.sqrt(1./(dt*2))
    white_hf = hf / np.sqrt(interp_psd(freqs)) * norm
    white_ht = np.fft.irfft(white_hf, n=Nt)
    return white_ht

其中interp_psd是噪声的功率谱密度插值,并按以下方式计算:

NFFT = 4*fs
Pxx_H1, freqs = mlab.psd(strain_H1, Fs = fs, NFFT = NFFT)
Pxx_L1, freqs = mlab.psd(strain_L1, Fs = fs, NFFT = NFFT)

# We will use interpolations of the ASDs computed above for whitening:
psd_H1 = interp1d(freqs, Pxx_H1)
psd_L1 = interp1d(freqs, Pxx_L1)

为什么他们将频域中的白化应变乘以dt*2我知道 2 可能来自丢弃负频率,但 RFFT 不会考虑到这一点吗?

稍后的教程将计算过滤数据的频谱图:

# Plot the H1 spectrogram:
plt.figure(figsize=(10,6))
spec_H1, freqs, bins, im = plt.specgram(strain_H1[indxt], NFFT=NFFT, Fs=fs, window=window, 
                                            noverlap=NOVL, cmap=spec_cmap, xextent=[-deltat,deltat])
plt.xlabel('time (s) since '+str(tevent))
plt.ylabel('Frequency (Hz)')
plt.colorbar()
plt.axis([-deltat, deltat, 0, 2000])
plt.title('aLIGO H1 strain data near '+eventname)
plt.savefig(eventname+'_H1_spectrogram.'+plottype)

在这里,他们没有标准化,但我认识到他们可能不需要,因为他们只对频谱图的视觉感兴趣。

后来,他们在执行匹配滤波器练习时通过采样频率进行归一化,然后将其反转。

# Take the Fourier Transform (FFT) of the data and the template (with dwindow)
    data_fft = np.fft.fft(data*dwindow) / fs

    # -- Interpolate to get the PSD values at the needed frequencies
    power_vec = np.interp(np.abs(datafreq), freqs, data_psd)

    # -- Calculate the matched filter output in the time domain:
    # Multiply the Fourier Space template and data, and divide by the noise power in each frequency bin.
    # Taking the Inverse Fourier Transform (IFFT) of the filter output puts it back in the time domain,
    # so the result will be plotted as a function of time off-set between the template and the data:
    optimal = data_fft * template_fft.conjugate() / power_vec
    optimal_time = 2*np.fft.ifft(optimal)*fs

如果信息过多,我深表歉意。总而言之,我的问题是:我是否必须在 python(numpy、scipy、matplotlib)中规范化 FFT 的输出才能在数学上准确,以及通过什么因素?对于 PSD 或频谱图等变换,这种归一化是否有所不同?

2个回答

您是否标准化的决定不会改变答案的准确性,因为它只是一个比例因子。如果您使用常见的缩放比例1/N,那么每个 DFT bin 的输出将表示输入信号中处于该 bin 定义的频率的部分的平均值,并缩放到与输入相同的单位。所以这很方便,并且对输出的大小表示了一定的含义,但是如果你没有除以 N 它不会使答案不正确,只要你的方法是一致的。

例如,它也很常见使用1/N因为这将使 DFT 和 IDFT 完全对称,因为两者都可以使用相同的缩放比例。

要了解发生了什么,请考虑计算 DFT 的第一个 bin 的最简单情况,它表示 DC,并考虑一个 DC 信号,对于所有N给的样品。如果这个直流信号代表一个电压幅度,那么这是f(t)=1伏特,给出为f[n]=1, 对全部N样品,n=0N1.

没有任何归一化的 DFT 的第一个 bin 只是所有 N 个样本的总和:

F[k=0]=n=0N1v[n]=N

所以如果我们要按比例缩放1/N,那么结果将代表我们的信号在该 bin 内的频率分量的平均值,其单位与输入相同。在这种情况下,所有值都位于 bin 0“DC”,因此平均值为 1V,与我们按比例缩放时得到的结果一致1/N. (从上面的公式可以清楚地看出,当按比例缩放时1/N这只是平均值f[n])。

类似地,所有其他 bin 是在乘以时域信号后的平均值ejkωon,这可以被视为将该 bin 处的任何信号频率转换为 DC,然后取该结果的平均值。

是否要归一化取决于您是否想知道 DFT 输入的电平或能量。

IIRC,SciPy FFT 返回能量(符合 Parseval 的关系)。一个信号在同一电平上 N 倍的长度具有 N 倍的能量。所以你可以除以 N 来估计一个水平而不是能量。