Reputation: 1291
Here is a code that compares fft phase plotting with 2 different methods :
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 :
Upvotes: 3
Views: 5939
Reputation: 189
Before the answer, just a small note:
Remove the p[np.abs(Y) < 1] = 0
line. Most of your spectrum has magnitude below 1, which is why, with this line, your spectrum looks mostly like a flat line at zero.
Now to the answer:
phase_spectrum
does three things different than you:
np.unwrap(np.angle(Y))
.angle_spectrum
instead.window=None
, but, for some reason, matplotlib decided that window=None
means "use a hanning window, please" (see the docs).window=lambda x: x
.
sides='twosided'
to the phase_spectrum
call.Now, about getting the phase at a frequency 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. You can get an approximation with the phase of the nearest bin, though. 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. The FFT computes the phase and amplitue at all frequencies. 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)
.
Upvotes: 8
Reputation: 70733
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. 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).
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.
Upvotes: 0