我有一个简单的正弦函数作为sin(2 * pi f t + phi)。我想获得相位信号phi。 我试图用FFT来计算phi。在matlab中我做了以下
f=200; %frequency of sine wave
overSampRate=30; %oversampling rate
fs=overSampRate*f; %sampling frequency
phase = 3/5*pi; %desired phase shift in radians
nCyl = 5; %to generate five cycles of sine wave
t=0:1/fs:nCyl*1/f; %time base
x=sin(2*pi*f*t+phase); %replace with cos if a cosine wave is desired
NFFT=1024; %NFFT-point DFT
X=fft(x,NFFT); %compute DFT using FFT
XX=2*abs(X(1:NFFT/2+1));
[tt ind]=max(XX);
phase_Estimate=angle(X(ind);
这个结果对我来说几乎没有任何意义。例如,当phi = 0.523时,获得phase_Estimate -0.98。
答案 0 :(得分:2)
使用非插值FFT结果相位仅在正弦波周期恰好是FFT长度的整数倍时才有效。在您的示例中,正弦波在光圈中不是整数周期。
如果没有,您需要插入相位以获得更好的估计。这是获得更好插值阶段的一种方法:
首先进行ffthift(旋转N / 2)数据,以便在进行FFT之前将零相位参考点移动到窗口的中心。 (这是为了防止相位在相邻的FFT结果区间之间翻转/交替。*)
然后进行FFT并通过抛物线或更好的Sinc插值来估计正弦波的频率。
然后使用估计的频率线性插值最近的两个FFT结果仓阶段之间的相位。更新:或者更好的是,分别使用FFT结果的实部和虚部的Sinc插值,然后在插值的IQ分量上使用atan2来获得插值相位。
然后使用窗口中心的估计频率和相位来计算某个其他点的相位,例如FFT窗口的开始。
另请注意,正弦的相位与余弦波的相位相差pi / 2。 atan(im,re)返回余弦阶段。
(*作为预先设置数据的替代方法,也可以翻转奇数FFT结果区的相位。)
答案 1 :(得分:1)
当你应该从FFT(XX
)获取相位时,你试图从功率谱(X
)获得相位。变化:
phase_Estimate=angle(XX(ind));
为:
phase_Estimate=angle(X(ind));
答案 2 :(得分:1)
实际上,这个问题比最初看起来要难得多。 @ hotpaw2给出的答案是完全正确的,并且比我发现的任何其他资源都更好地进行了拼写,但是它仍然只是一个轮廓,花了我几个小时才把所有的肉都放到骨头上。
希望其他人也能找到相关的问题(并为将来自己参考),这里有一个更详尽的解释:
假设您在索引ind处有一个(局部)最大值(如出现问题的情况)。
步骤1:尝试使用两个周围的值对最大值的更精确位置进行插值。在很多地方,例如https://www.dsprelated.com/freebooks/sasp/Quadratic_Interpolation_Spectral_Peaks.html对此都有很好的解释,但是对于TL:DR版本是:
delta = 0.5*(X[ind-1]-X[ind+1])/(X[ind-1]-2*X[ind]+X[ind+1])
p0 = ind+delta
,估计峰值为p0
(如果您想要更精确的估计,请改用log(X[ind-1])
,或全力以赴并使用sinc
函数,但对于大多数目的,上面的增量就足够了)
步骤2:棘手的部分:使用该位置插值相位。 第一个本能是使用我们刚刚发现的增量进行简单的线性插值:
i0 = floor(p0); w = p0-i0; wp = 1-w
ang = wp*angle(X[i0]) + w*angle(X[i0+1])
此无法正常工作的原因有很多,其中大部分由@ hotpaw2概述。他们中的第一个是这不是您平均角度的方式,因为它们以2pi为模进行包装,因此0和2pi应该相似。更正确的方法是取归一化的复数平均值:
ang = angle(wp*X[i0]/abs(X[i0]) + w*X[i0+1]/abs(X[i0+1]))
但是,这仍然是不正确的,因为如果峰值在i0和i0 + 1之间,则相位会在此处翻转180度(π弧度),从而使平均值非常产生误导。要解决此“相位翻转”问题,您必须(a)在fft之前执行fftshift
(是,在时域中),或者(b)翻转每个X的奇数索引值的相位(通过乘以获得) (-1)或(如果您不愿意像以前那样触摸FFT),也可以(c)使用以下代码模拟方法(b):
i0 = floor(p0); w = p0-i0; wp = 1-w
if (i0 % 2 == 1) { w*=-1; wp*=-1 } # Flip both if i0 odd
ang = angle(wp*X[i0]/abs(X[i0]) - w*X[i0+1]/abs(X[i0+1])) # Note the "-" here!
这将为您提供一个(大部分)正确的相位,但对于余弦而言且位于fft窗口的中心。
第3步(可选)::如果您需要正弦相位,并且从窗口的开头开始,则需要添加校正因子:
ang_beg = ang - (2*pi*p0/N)*N/2 + pi*0.5 = ang - pi*(p0 - 0.5)
({0.5*pi
将cos转换为sin,而-p0*pi
则转换为窗口的开始)。
这似乎行得通,至少在我需要的相位声码器中有效。希望其他人也会发现它有用。
顺便说一句,纯正弦波不需要相位插值,例如angle(X[i0]) = angle(-X[i0+1])
,因此您可以直接使用它。对于实际信号,可能会有一些偏差,因此插值会增加一些鲁棒性,这通常是一个好主意,尽管使用w
和wp
且规范化可能会过大,并且angle(sgn*(X[i0]-X[i0+1))
通常就足够了
欢迎对所有这些发表任何评论。我不是DSP专家,所以在某些细节上我可能是错的,因为这确实可行,所以希望其他人也会发现它有用。