y
定义如下:
import numpy as np
freq = 12.3
x = np.arange(10000)
y = np.cos(x * 2 * np.pi * freq / 10000)
如果频率是整数,则可以使用np.argmax(np.abs(np.fft.fft(y)))
进行计算。但是,如果频率不是整数,如何更精确地计算频率?
编辑:为澄清起见,我们不应该知道如何生成时间序列y
。上面的代码片段只是一个非整数频率如何出现的人工示例。显然,如果我们已经知道生成时间序列的函数,则不需要FFT即可确定频率。
答案 0 :(得分:1)
您需要为信号提供更高的分辨率
import numpy as np
freq = 12.3
x = np.arange(100000) # 10 times more resolution
y = np.cos(x * 2 * np.pi * freq / 10000) # don't change this
print(np.argmax(np.abs(np.fft.fft(y))) / 10) # divide by 10
# 12.3
x
中数据点的数量必须是除以y
的数量的10倍。您可以获得类似的效果:
x = np.arange(10000)
y = np.cos(x * 2 * np.pi * freq / 1000)
print(np.argmax(np.abs(np.fft.fft(y))) / 10)
# 12.3
如果要用两位小数查找频率,则分辨率需要提高100倍。
freq = 12.34
x = np.arange(10000)
y = np.cos(x * 2 * np.pi * freq / 100) # 100 times more resolution
print(np.argmax(np.abs(np.fft.fft(y))) / 100) # divide by 100
# 12.34
答案 1 :(得分:1)
您可以在计算FFT之前用零填充数据。
例如,这是您的原始计算。找到频率为12.0时最大幅度的傅立叶系数:
In [84]: freq = 12.3
In [85]: x = np.arange(10000)
In [86]: y = np.cos(x * 2 * np.pi * freq / 10000)
In [87]: f = np.fft.fft(y)
In [88]: k = np.argmax(np.abs(f))
In [89]: np.fft.fftfreq(len(f), d=1/10000)[k]
Out[89]: 12.0
现在重新计算傅立叶变换,但将输入的长度填充为原始长度的六倍(您可以根据需要调整该因子)。对于填充信号,最大幅度的傅立叶系数与频率12.333相关:
In [90]: f = np.fft.fft(y, 6*len(y))
In [91]: k = np.argmax(np.abs(f))
In [92]: np.fft.fftfreq(len(f), d=1/10000)[k]
Out[92]: 12.333333333333332
以下是说明填充信号效果的图。信号与上述信号不同;我使用了更短的信号来使用不同的值,以使其更容易看到效果。瓣的形状没有改变,但是采样频率的点数增加了。
该图由以下脚本生成:
import numpy as np
import matplotlib.pyplot as plt
fs = 10
T = 1.4
t = np.arange(T*fs)/fs
freq = 2.6
y = np.cos(2*np.pi*freq*t)
fy = np.fft.fft(y)
magfy = np.abs(fy)
freqs = np.fft.fftfreq(len(fy), d=1/fs)
plt.plot(freqs, magfy, 'd', label='no padding')
for (factor, markersize) in [(2, 9), (16, 4)]:
fy_padded = np.fft.fft(y, factor*len(y))
magfy_padded = np.abs(fy_padded)
freqs_padded = np.fft.fftfreq(len(fy_padded), d=1/fs)
plt.plot(freqs_padded, magfy_padded, '.', label='padding factor %d' % factor,
alpha=0.5, markersize=markersize)
plt.xlabel('Frequency')
plt.ylabel('Magnitude of Fourier Coefficient')
plt.grid()
plt.legend(framealpha=1, shadow=True)
plt.show()
答案 2 :(得分:0)
如果S / N允许,您可以尝试使用插值或零填充(等效于整个矢量插值)来潜在地改善频率估计。 Sinc核插值比抛物线插值更准确。