使用任意数量的样本从 FFT 中提取准确的相位和幅度信息

信息处理 fft 自由度 阶段 窗函数 振幅
2021-12-24 02:28:43

这是我之前根据@hotpaw2 给出答案后的聊天提出的一个后续问题,并从stackoverflow 交叉发布,因为有人建议它与DSP 更相关。我有一个信号,它将是一个带有相位偏移的单余弦波。我的任务是提取(需要非常高的精度)这个单一频率分量的幅度和相位。

在纸面上,假设正确归一化的傅立叶变换T,以下关系成立:

T(ω)=F{Acos(ω0t+ϕ)}
A=2|T(ω0)|=2Re(T(ω0))2+Im(T(ω0))2

ϕ=arg{Re(T(ω0))+jIm(T(ω0))}=atan2(Im(T(ω0)), Re(T(ω0))

不出所料,DFT 不仅仅是简单地进行转换并挑选相关组件。特别是,讨论向我表明,我并不完全清楚测量相位偏移的对象是什么,并且如果数据未正确窗口化,则会存在显着的边缘效应,可能会破坏结果的准确性。

我一直在谷歌上搜索,但大多数讨论都是相当技术性的,而且例子很简单,所以我希望有人能对事情有所了解。特别是,我遇到了一个示例,建议我应该先进行转换,而不是进行简单的转换。

我把这段代码放在一起来测试一些想法:

import numpy as np
import pylab as pl

def flattop(n):
    return [1.0 - 1.93*np.cos(2*np.pi*k/(float(n-1))) + 1.29*np.cos(4*np.pi*k/(float(n-1)))- 0.388*np.cos(6*np.pi*k/(float(n-1))) + 0.028*np.cos(8*np.pi*k/(float(n-1))) for k in range(int(n))]

f = 30.0
w = 2.0*np.pi*f
phase = np.pi/7
num_t = 10*f
window = flattop(num_t)

t, dt = np.linspace(0, 1, num_t, endpoint=False, retstep=True)

signal = np.cos(w*t+phase)+np.random.normal(0,0.05,len(t))

pl.plot(t,window*signal)
pl.show()

amp = np.fft.rfft(window*signal)
freqs = np.fft.rfftfreq(t.shape[-1],dt)

index = np.argmax(np.abs(amp))
print index
print(np.arctan2(amp.imag,amp.real))[index]
print (np.abs(amp))[index]*(2.0/len(t))

pl.subplot(211)
pl.plot(freqs,np.abs(amp))
pl.subplot(212)
pl.plot(freqs,(np.arctan2(amp.imag,amp.real)))
pl.show()

一些观察:

当我使用 时num=10*f,我在相位和幅度上都得到了完美的结果。但是,如果我使用num=10*f+1,我得到的阶段是完全错误的。我尝试过使用一个窗口(特别是一个平顶窗口,因为它保留了幅度)并且我得到了同样的结果:除非我将样本数控制为整数倍(实际上是 10.2、10.4、10.6 和 10.8* f 由于某种原因也给出了很好的结果)的信号频率,我得到了垃圾。

有人建议我可以通过测量窗口中心的相位来改进这一点,这可以使用 fftshift 来实现。谷歌搜索给了我几个我在代码中实现的例子,但结果是一样的。

所以:第一组问题:有人可以解释使用 fftshift 的意义,以及它对数据的确切作用吗?为什么使用逆移位、变换、然后移位需要一个仅移位一次且没有逆运算的频率分量集?这种方法是否正确(暂时忽略窗口问题)。

对于有限时域信号,似乎也需要使用窗口,我可以理解。我不清楚的是如何校正窗口效果的幅度和相位。我玩过平顶窗来保持幅度,它似乎有效,但我不明白为什么幅度增益不是频率的函数。

第二组问题:如果我对数据进行窗口化,大概这会影响结果的幅度和相位(?)。是否有一种分析方法可以校正给定窗口形状的幅度变化?我可以找到一些列出校正因子的表格,但我还没有真正看到一个好的解释。

在链接的问题中,有人说相位应该在窗口函数的驼峰中心附近测量。但是因为窗口函数是一个时域函数并且我想要特定频率的相位,所以我不太明白这意味着什么。

实际上,我的样本信号不会是生成的完美余弦波,而且我无法控制样本数量,因此我需要改进方法以提供准确的信息,而不管样本的确切数量如何。

编辑:似乎这里的第一个答案可能是一个解决方案,但如果有人可以提供见解,我仍然想更深入地了解问题中提出的问题。

编辑 2:似乎问题与这样一个事实有关,即为了获得准确的相位信息,我们要求感兴趣的频率准确地位于 DFT 箱的中心。对于任意数量的样本,如何确保这一点?上面链接的问题提供了一个很好的例子,它适用于单个频率,但必须有一种通用的方法来为多个频率执行此操作。

2个回答

关于您的编辑#2:相位估计要求频率位于 bin 中心是不正确的。

但是,如果频率估计需要在 FFT 结果箱之间进行插值,那么在该频率估计处的任何相位估计也需要。对复数 FFT 结果使用 Sinc 插值内核,加上幅度峰值的逐次逼近,也可以改进相位估计。对称零填充和使用更长的 FFT 是进行 Sinc 内核插值的一种方法。fftshift 将确保奇数/偶数比,即 atan2(),在插值后保持接近相同。但请注意,这会将相位参考移动到时域窗口的中间。

添加更新:

如果您交换相当平滑的波形(例如正弦曲线)的输入样本向量的一半(与偶数 N 的 fftshift 相同),则波形的平滑部分从向量的末端交叉回到起点向量,从而消除该环绕点附近的不连续性(任何非精确整数周期正弦曲线的)。因此,在估计 bin 之间的频率中的任何错误都不会影响复数 FFT 结果的奇/偶比(例如,相邻 bin 将具有几乎相同的相位),从而允许更容易的插值。如果在 FFT 之前不连续性靠近矢量的第一个和/或最后一个元素,则 FFT 相位结果可以在相邻 FFT 结果箱之间来回翻转(对于任何非精确整数周期正弦曲线,由于到它们在最后一个元素和第一个元素之间的不连续性),

相位翻转是因为 Sinc 函数的符号在交替的 Sinc 波瓣之间翻转,并且任何 DFT 区间正弦波上的矩形窗口(在任何有限长度 DFT 中固有)导致变换对波瓣进行采样,而不是对零交叉进行采样,这个 Sinc 函数。但是,fftshift 在复数空间中“扭曲”了这个 Sinc 函数,因此它总是以相同的符号进行采样(不是交替或“翻转”)。(数学学生的练习:表明时间偏移会导致 DFT 结果向量的相位扭曲,而 fftshift 会导致 Sinc 的每叶扭曲 pi。)

在实践中,我通常是 post-unflip 而不是 pre-fftshift,因为这会产生相同的相位结果。

fftshift 将相位参考(测量正弦波相位的时间点)从原始数据向量的元素 [0] 移动到元素 [N/2]。您可以使用估计的频率和相位如何在该频率下随时间变化的方程将相位角移回任何其他时间点(例如元素[0] 的时间点)。但是,当然,移动相位估计的准确性取决于频率估计的准确性。所以我通常只是设计我的实验,在我的数据捕获窗口中间需要一个相位测量/估计结果,而不是在一端或另一端。

这是我们在正弦建模中遇到的问题。有很多问题,您的窗口函数可能就是其中之一。我的这篇论文更笼统一点,因为它不仅导出了每个正弦曲线的幅度、相位和频率,还导出了频率上的扫描率和幅度的“斜坡率”。

尽管如此,如果您担心正弦曲线存在于 FFT 区间之间的频率,我建议使用高斯窗口并从参考论文中的方程推导每个正弦曲线的参数。