从FFT恢复正弦信号的相位

时间:2015-04-15 06:49:29

标签: fft angle sine phase

我有一个简单的正弦函数作为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。

3 个答案:

答案 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]),因此您可以直接使用它。对于实际信号,可能会有一些偏差,因此插值会增加一些鲁棒性,这通常是一个好主意,尽管使用wwp且规范化可能会过大,并且angle(sgn*(X[i0]-X[i0+1))通常就足够了

欢迎对所有这些发表任何评论。我不是DSP专家,所以在某些细节上我可能是错的,因为这确实可行,所以希望其他人也会发现它有用。