这是我之前根据@hotpaw2 给出答案后的聊天提出的一个后续问题,并从stackoverflow 交叉发布,因为有人建议它与DSP 更相关。我有一个信号,它将是一个带有相位偏移的单余弦波。我的任务是提取(需要非常高的精度)这个单一频率分量的幅度和相位。
在纸面上,假设正确归一化的傅立叶变换,以下关系成立:
不出所料,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 箱的中心。对于任意数量的样本,如何确保这一点?上面链接的问题提供了一个很好的例子,它适用于单个频率,但必须有一种通用的方法来为多个频率执行此操作。