增加采样率不会提高光谱分辨率,只会提供更多高频信息,而您并不感兴趣。唯一提高光谱分辨率的方法是增加窗口长度。有一种通过零填充人为增加窗口长度的方法,但这只能给您带来“虚假分辨率”,它只会在常规点之间产生平滑曲线。因此,唯一的方法是在更长的时间内测量数据,没有免费的午餐。
对于您所描述的问题,减少FFT计算时间的标准方法是使用解调(或混频,不确定官方名称是什么)。将数据与接近您感兴趣的频率的正弦波相乘(可以是确切的频率,但这并非必要),然后抽取数据(低通滤波器的截止频率略低于下采样采样率的奈奎斯特频率,然后进行下采样)。这样,您会得到较少的点,因此您的FFT速度会更快。得到的光谱将类似于原始光谱,但仅仅是由解调频率移位。因此,在制作图表时,只需将f_demod添加到x轴即可。
需要注意的一件事是,如果您用实数正弦相乘,则下采样的光谱实际上将是两个镜像光谱的总和,因为实数正弦由正负频率组成。有两种解决方法:
1. 同时通过正弦和余弦乘以相同频率进行解调,这样您就可以得到2个光谱,然后取和或差即可获得您的光谱。
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))
lo = 2**.5 * np.exp(-2j*np.pi*f_demod * t)
y = decimate(x * lo, ratio)
z = decimate(y, ratio)
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()
结果:
![enter image description here](https://istack.dev59.com/yZpi6.webp)
如果你放大看,你会注意到在抽取滤波器的通带内,结果几乎相同。需要注意的一件事是,在decimate
中使用的低通滤波器如果使用远大于10的抽取比率将变得数值不稳定。解决这个问题的方法是对于大比率进行多次抽取,例如对于1000的抽取比率,你可以通过3次10倍抽取来实现。