在巴特沃斯滤波器之后执行 STFT 似乎分辨率较低

信息处理 过滤器 Python 频谱图 stft 巴特沃思
2022-02-11 15:28:52

我有一个以 2MHz 采样率记录的信号。在可能需要任何抽取之前,我首先使用 STFT/频谱图在我的周期性记录信号中寻找峰值。从这里,我可以清楚地看到一些山峰。使用 PSD,我发现在 ~500kHz 之后频率并不突出,因此我应用巴特沃斯滤波器滤除高于此的频率。此外,我的天线从 ~20kHz 开始记录,因此低于此的频率也会被过滤掉。

sample.wav文件可以在这里下载:https ://www.dropbox.com/s/hcwrtuzqbipatri/sample.wav?dl=0

滤波器幅度响应如下所示:

在此处输入图像描述

然而,比较原始信号和带通滤波信号的频谱图看起来完全不同,我似乎无法理解为什么。原始信号在左边,巴特沃斯滤波信号在右边。y 轴刻度是频率 (kHz)。

在此处输入图像描述

过滤器使用的代码如下:

import numpy as np
import os
import scipy.io.wavfile
from scipy import signal
# import librosa
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA

fs, x = scipy.io.wavfile.read('sample.wav') # fs is 2MHz (2000000)
# Convert to mono by avg I (left) and Q (right) channels
x = np.mean(x, axis=1)
x = np.asarray(x, dtype='float')
# Once the recording is in memory, we normalise it to +1/-1
# x /= np.max(np.abs(x)) ## This is sensitive to outliers and rescaling is not consistent
x /= x.std()

t_tot = 1 / fs*x.shape[0]
t = np.linspace(0, t_tot, x.shape[0])
tmin = np.min(t)
tmax = np.max(t)
N = len(x)

f_l, f_u = 20000, 500000  # Band from 20kHz to 500kHz
wp = np.array([f_l, f_u])*2/fs  # normalized pass band frequnecies
ws = np.array([0.8*f_l, 1.2*f_u])*2/fs  # normalized stop band frequencies
b, a = signal.iirdesign(wp, ws, gpass=60, gstop=80, ftype="butter", analog=False)

# plot filter response:
w, h = signal.freqz(b, a, whole=False)
ff_w = w*fs/(2*np.pi)
fg, ax = plt.subplots()
ax.set_title('Butterworth filter amplitude response')
ax.plot(ff_w/1000, np.abs(h))
ax.set_ylabel('Relative Amplitude')
ax.grid(True)
ax.set_xlabel('Frequency (kHz)')
fg.canvas.draw()
# do the filtering:
zi = signal.lfilter_zi(b, a)*x[0]
x1, _ = signal.lfilter(b, a, x, zi=zi)
# calculate the avarage:
avg = np.mean(x1**2)
print("RMS values is %g" % avg)
plt.show()

fig, ax = plt.subplots(1, 2, sharex=True, sharey=True)
f, t, Sxx = signal.spectrogram(x, fs=fs, window='hann', nperseg=8192, scaling='spectrum')
ax[0].pcolormesh(t, f / 1000, 10 * np.log10(Sxx), shading='gouraud')

f_1, t_1, Sxx_1 = signal.spectrogram(x1, fs=fs, window='hann', nperseg=8192, scaling='spectrum')
ax[1].pcolormesh(t_1, f_1 / 1000, 10 * np.log10(Sxx_1), shading='gouraud')
# plt.set_title(f'nperseg={str(i)}')
plt.xlabel('Time (s)')
plt.ylabel('')
plt.show()
1个回答

颜色规范是问题所在。默认情况下,颜色从 映射im.min()im.max(),因此单个高值像素将使整个光谱图显得更暗。即它对异常值很敏感(以及我为什么不鼓励x /= x.max())。

在绘制图像进行比较时,我们应该始终指定一个共享的颜色范围。为了确保没有丢失任何东西,我们可以拍摄所有图像的最大数量——但这可能会丢失细节,所以我们可能会喜欢0.5 * im.max()直到满意为止。我在评论中提供的代码实现了这一点并产生

在您的情况下,您采用 log10 因此异常值实际上是im.min(),并且在左图上很明显,靠近顶部的值小于靠近底部的值,因此在抽取后颜色范围不需要考虑这些并且更丰富地映射出剩余的变化(对于较小的输入动态范围,更大的色彩动态范围)。

正确的抽取不会改变时频行为,除非频域中的“过渡点”附近(图中约为 300 Hz),因此频谱图在该区域之外应该保持相同。(您实际上并没有进行抽取,但由于“平顶”,过滤器非常类似于抽取之一,即箱没有像高斯那样相对于彼此缩放)