python中一个实际问题离散功率谱密度的正确归一化

python中一个实际问题离散功率谱密度的正确归一化,python,fft,normalization,dft,spectral-density,Python,Fft,Normalization,Dft,Spectral Density,我正在努力实现功率谱密度(及其逆功率谱密度)的正确标准化 我遇到了一个真正的问题,比如加速度计的读数,以功率谱密度(psd)的形式表示,单位为振幅^2/Hz。我想把它转换成一个随机时间序列。然而,首先我想了解“前进”方向,时间序列到PSD 根据[1],时间序列x(t)的PSD可通过以下公式计算: PSD(w) = 1/T * abs(F(w))^2 = df * abs(F(w))^2 其中T是x(T)的采样时间,F(w)是x(T)的傅里叶变换,df=1/T是傅里叶空间中的频率分辨率。然而,我

我正在努力实现功率谱密度(及其逆功率谱密度)的正确标准化

我遇到了一个真正的问题,比如加速度计的读数,以功率谱密度(psd)的形式表示,单位为振幅^2/Hz。我想把它转换成一个随机时间序列。然而,首先我想了解“前进”方向,时间序列到PSD

根据[1],时间序列x(t)的PSD可通过以下公式计算:

PSD(w) = 1/T * abs(F(w))^2 = df * abs(F(w))^2
其中T是x(T)的采样时间,F(w)是x(T)的傅里叶变换,df=1/T是傅里叶空间中的频率分辨率。然而,我得到的结果并不等于我使用scipy-Welch方法得到的结果,请参见下面的代码

第一段代码取自scipy.welch纪录片:

from scipy import signal
import matplotlib.pyplot as plt

fs = 10e3
N = 1e5
amp = 2*np.sqrt(2)
freq = 1234.0
noise_power = 0.001 * fs / 2
time = np.arange(N) / fs
x = amp*np.sin(2*np.pi*freq*time)
x += np.random.normal(scale=np.sqrt(noise_power), size=time.shape)

f, Pxx_den = signal.welch(x, fs, nperseg=1024)
plt.semilogy(f, Pxx_den)
plt.ylim(\[0.5e-3, 1\])
plt.xlabel('frequency \[Hz\]')
plt.ylabel('PSD \[V**2/Hz\]')
plt.show()
我注意到的第一件事是,绘制的psd随变量fs而变化,这对我来说似乎很奇怪。(也许我需要相应地调整nperseg参数?为什么nperseg不能自动设置为fs?)

我的代码如下:(注意,我定义了自己的fft_全函数,它已经处理了正确的傅里叶变换规范化,我通过检查Parsevals定理来验证)

不幸的是,我还不允许发布图片,但两个情节看起来不一样

如果有人能向我解释我错在哪里,并一劳永逸地解决这个问题,我将不胜感激:)

[1] :等式2.82。航天器结构设计中的随机振动 理论与应用,作者:Wijker,J.Jaap,2009


scipy库使用Welch方法估计PSD。这种方法比只取离散傅里叶变换的平方模更复杂。简言之,其进展如下:

  • 设x为包含N个样本的输入离散信号

  • 将x分割为M个重叠段,这样每个段sm包含nperseg样本,并且每个连续段在noverlap样本中重叠,这样nperseg=K*(nperseg-noverlap),其中K是一个整数(通常K=2)。还请注意: N=nperseg+(M-1)*(nperseg-noverlap)=(M+K-1)*nperseg/K

  • 从每个分段sm中减去其平均值(这将删除直流分量): tm=sm-总和(sm)/nperseg

  • 将获得的零平均段tm的元素乘以适当(非对称)窗口函数h(如Hann窗口)的元素: um=tm*h

  • 计算所有向量um的快速傅里叶变换。在执行这些变换之前,我们通常首先向每个向量um附加太多的零,使其新维度成为2的幂(函数welch的nfft参数用于此目的)。让我们假设len(um)=2p。在大多数情况下,我们的输入向量是实值的,因此最好对实际数据应用FFT。它的结果是复数向量vm=rfft(um),这样len(vm)=2p-1+1

  • 计算所有变换向量的平方模: am=abs(vm)**2, 或者更有效地: am=vm.real**2+vm.imag**2

  • 按如下方式规范化向量am: bm=am/总和(h*h) bm[1:-1]*=2(这考虑了负频率), 其中h是包含窗口系数的维数nperseg的实向量。在汉恩窗口的情况下,我们可以证明这一点 和(h*h)=3/8*len(h)=3/8*nperseg

  • 将PSD估计为所有向量bm的平均值: psd=总和(bm)/M 结果是尺寸len(psd)=2p-1+1的向量。如果我们希望所有psd系数之和匹配加窗输入数据的均方振幅(而不是振幅平方和),那么矢量psd也必须除以nperseg。但是,scipy例程忽略了这个步骤。在任何情况下,我们通常在分贝级上显示psd,因此最终结果为: psd_dB=10*log10(psd)

  • 有关更详细的说明,请阅读。另请参见和第13.4章

    import scipy.fftpack as fftpack
    
    def fft_full(xt,yt):
        dt = xt[1] - xt[0]
        x_fft=fftpack.fftfreq(xt.size,dt)
        y_fft=fftpack.fft(yt)*dt
        return (x_fft,y_fft)
    
    xf,yf=fft_full(time,x)
    df=xf[1] - xf[0]
    psd=np.abs(yf)**2 *df
    plt.figure()
    plt.semilogy(xf, psd)
    #plt.ylim([0.5e-3, 1])
    plt.xlim(0,)
    plt.xlabel('frequency [Hz]')
    plt.ylabel('PSD [V**2/Hz]')
    plt.show()