增加采样率不会给你更高的光谱分辨率,它只会给你更多你不感兴趣的高频信息。提高光谱分辨率的唯一方法是增加窗口长度。有一种方法可以通过零填充人为地增加窗口的长度,但这只会给你“假分辨率”,它只会在正常点之间产生平滑的曲线。所以唯一的方法是测量更长时期的数据,没有免费的午餐。
对于您描述的问题,减少 FFT 计算时间的标准方法是使用解调(或外差,不确定正式名称是什么)。将您的数据与频率接近您感兴趣的频率的正弦相乘(可能是确切的频率,但这不是必需的),然后抽取您的日期(低通滤波,拐角频率刚好低于您的奈奎斯特频率采样率,然后是下采样)。这样,您的积分会少得多,因此您的 FFT 会更快。生成的频谱将与您的原始频谱相似,但只是通过解调频率移动。因此,在制作绘图时,只需将 f_demod 添加到您的 x 轴即可。
需要注意的一点是,如果与实正弦相乘,您的下采样频谱实际上将是两个镜像频谱的总和,因为实正弦由正负频率组成。有两种解决方案
用相同频率的正弦和余弦解调,得到 2 个频谱,然后求和或取差即可得到你的频谱。
通过乘以exp(2*pi*i*f_demod*t) 形式的复数正弦进行解调。您的 FFT 的输入现在将是复杂的,因此您必须计算一个两侧的频谱。但这正是你想要的,你会得到低于和高于f_demod的频率。
我更喜欢第二种解决方案。快速示例:
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.mlab import psd
from scipy.signal import decimate
f_line = 123.456
f_demod = 122
f_sample = 1000
t_total = 100
t_win = 10
ratio = 10
t = np.arange(0, t_total, 1 / f_sample)
x = np.sin(2*np.pi*f_line * t) + np.random.randn(len(t)) # sine plus white noise
lo = 2**.5 * np.exp(-2j*np.pi*f_demod * t) # local oscillator
y = decimate(x * lo, ratio) # demodulate and decimate to 100 Hz
z = decimate(y, ratio) # decimate further to 10 Hz
nfft = int(round(f_sample * t_win))
X, fx = psd(x, NFFT = nfft, noverlap = nfft/2, Fs = f_sample)
nfft = int(round(f_sample * t_win / ratio))
Y, fy = psd(y, NFFT = nfft, noverlap = nfft/2, Fs = f_sample / ratio)
nfft = int(round(f_sample * t_win / ratio**2))
Z, fz = psd(z, NFFT = nfft, noverlap = nfft/2, Fs = f_sample / ratio**2)
plt.semilogy(fx, X, fy + f_demod, Y, fz + f_demod, Z)
plt.xlabel('Frequency (Hz)')
plt.ylabel('PSD (V^2/Hz)')
plt.legend(('Full bandwidth FFT', '100 Hz FFT', '10 Hz FFT'))
plt.show()
结果:
如果放大,您会注意到在抽取滤波器的通带内,结果几乎相同。需要注意的一件事是,如果您使用远大于 10 的抽取率,decimate 中使用的低通滤波器将在数值上变得不稳定。解决此问题的方法是对大比率进行多次抽取,即抽取1000 倍,您将 3 倍抽取 10 倍。