简体   繁体   English

绘制和提取 fft 相位

[英]Plotting and extracting fft phase

Here is a code that compares fft phase plotting with 2 different methods :这是一个将 fft 相位绘图与 2 种不同方法进行比较的代码:

import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack

phase = np.pi / 4
f = 1
fs = f*20
dur=10
t = np.linspace(0, dur, num=fs*dur, endpoint=False)
y = np.cos(2 * np.pi * t + phase)
Y = scipy.fftpack.fftshift(scipy.fftpack.fft(y))
f = scipy.fftpack.fftshift(scipy.fftpack.fftfreq(len(t)))

p = np.angle(Y)
p[np.abs(Y) < 1] = 0

fig, ax = plt.subplots(2, 1)
ax[0].plot(t, y)
ax[1].plot(f*fs, p, label='from fft')
ax[1].phase_spectrum(y, fs, window=None, label='from phase_spectrum')
plt.legend()
plt.show()

here is the result :结果如下:

在此处输入图片说明

Here is result when signal number of period is not an integer :这是当周期的信号数不是整数时的结果:

在此处输入图片说明

I have several questions :我有几个问题:

  • why are phase plot with phase_spectrum or using fft and the angle so different?为什么相位图与 phase_spectrum 或使用 fft 和角度如此不同? Using fft and then np.angle produces good results, but how can we explain magnitude_spectrum result?使用 fft 然后 np.angle 会产生很好的结果,但是我们如何解释magnitude_spectrum结果呢?
  • Here we are in a very simple case where we have sine periodic signal with N periods For if I have a widepand signal and I want to extract phase at f how can I do?在这里,我们处于一个非常简单的情况,其中我们有 N 个周期的正弦周期信号如果我有一个宽带信号并且我想在 f 处提取相位,我该怎么办? For example here with both methods presented in example, I'm not sure I can extract a precise phase.例如,这里使用示例中介绍的两种方法,我不确定我是否可以提取精确的相位。 With phase_spectrum, at f = 1 I cannot find back pi/4.使用 phase_spectrum,在 f = 1 时我找不到 pi/4。 And with fft and then np.angle, in order to extract the good phase I need to be sure signal number of period is an integer.使用fft然后np.angle,为了提取好的相位,我需要确保周期的信号数是一个整数。

Before the answer, just a small note:在回答之前,只是一个小说明:
Remove the p[np.abs(Y) < 1] = 0 line.删除p[np.abs(Y) < 1] = 0行。 Most of your spectrum has magnitude below 1, which is why, with this line, your spectrum looks mostly like a flat line at zero.您的大部分频谱的幅度都低于 1,这就是为什么使用这条线,您的频谱看起来最像一条位于 0 处的平线。

Now to the answer:现在回答:
phase_spectrum does three things different than you: phase_spectrum做了三件与你不同的事情:

  • It applies phase unwrapping.它应用相位展开。
    • If you want to apply phase unwrapping in your code, just do np.unwrap(np.angle(Y)) .如果您想在代码中应用相位展开,只需执行np.unwrap(np.angle(Y))
    • If you want matplotlib to plot the spectrum without unwrapping, use angle_spectrum instead.如果您希望 matplotlib 在不展开的情况下绘制频谱,请改用angle_spectrum
  • It applies a windowing function to the data before computing the spectrum.它在计算频谱之前对数据应用加窗函数。
    • I know you passed a window=None , but, for some reason, matplotlib decided that window=None means "use a hanning window, please" (see the docs ).我知道您通过了window=None ,但是,出于某种原因,matplotlib 决定window=None表示“请使用汉宁窗口”(请参阅文档)。
    • If you do not want matplotlib to apply the window, one solution is to pass window=lambda x: x .如果您不希望 matplotlib 应用窗口,一种解决方案是传递window=lambda x: x
      • The docs actually suggest passing window=matplotlib.mlab.window_none , but the source for it is just a def window_none(x): return x . 文档实际上建议传递window=matplotlib.mlab.window_none ,但它的来源只是一个def window_none(x): return x
  • It computes the one-sided version of your spectrum.它计算频谱的单边版本。 The docs say this is the default whenever the input is real, not complex. 文档说只要输入是真实的而不是复杂的,这是默认值。
    • To get the normal two-sided version, pass sides='twosided' to the phase_spectrum call.要获得正常sides='twosided'版本, phase_spectrum sides='twosided' phase_spectrum sides='twosided'传递给phase_spectrum调用。

Now, about getting the phase at a frequency f :现在,关于以频率f获取相位:

To do this, you must use the phase without unwrapping .为此,您必须使用相位而不展开.

You are right that you can't directly extract the phase of the single tone signal if you do not have an integer number of cycles.如果您没有整数个周期,您就不能直接提取单音信号的相位,这是对的。 That is because the singal's frequency does not fall exactly on top of any frequency bin in the FFT.那是因为信号的频率并不完全落在 FFT 中任何频率仓的顶部。 You can get an approximation with the phase of the nearest bin, though.不过,您可以获得最近 bin 相位的近似值。 You could also do a sinc interpolation of the spectrum to get its value at the frequency you want.您还可以对频谱进行正弦插值,以在您想要的频率下获得其值。

If you only care about the phase of a single frequency f , then you shouldn't use FFT at all.如果您只关心单个频率f的相位,那么您根本不应该使用 FFT。 The FFT computes the phase and amplitue at all frequencies. FFT 计算所有频率的相位和幅度。 If you only care about a single frequency, just do Y_at_f = y @ np.exp(2j * np.pi * f * t) and get that phase by np.angle(Y_at_f) .如果您只关心单个频率,只需执行Y_at_f = y @ np.exp(2j * np.pi * f * t)并通过np.angle(Y_at_f)获得该相位。

You can extract the phase referenced to the center of your data window by doing an fftshift (circular rotate by N/2) before the FFT.您可以通过在 FFT 之前执行 fftshift(圆形旋转 N/2)来提取参考数据窗口中心的相位。 This is because, after an fftshift, atan2() always is related to the ratio of oddness to eveness of the data around its center (as decomposed to an odd function plus an even function).这是因为,在 fftshift 之后,atan2() 始终与围绕其中心的数据的奇数与偶数之比有关(分解为奇函数加偶函数)。

So calculate the phase of the signal in the middle of your window during its generation, and use that instead of the phase at the beginning.因此,在生成过程中计算窗口中间信号的相位,并使用它而不是开始时的相位。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM