【发布时间】:2018-03-22 22:33:11
【问题描述】:
我可以使用 Matplotlib 的 plt.psd() 方法绘制从 RTL-SDR 接收到的信号,结果如下图: 我试图实现的最终目标是检索高于某个功率水平的所有峰值的坐标,例如,-20。当我从时域接收信号时,我必须先将它们转换为频域,这由以下代码完成:
signal = []
sdr = RtlSdr()
sdr.sample_rate = 2.8e
sdr.center_freq = 434.42e6
samples = sdr.read_samples(1024*1024)
signal.append(samples)
from scipy.fftpack import fft, fftfreq
window = np.hanning(len(signal[0]))
sig_fft = fft(signal[0]*window)
power = 20*np.log10(np.abs(sig_fft))
sample_freq = fftfreq(signal[0].size, sdr.sample_rate/signal[0].size)
plt.figure(figsize=(9.84, 3.94))
plt.plot(sample_freq, power)
plt.xlabel("Frequency (MHz)")
plt.ylabel("Relative power (dB)")
plt.show()
idx = np.argmax(np.abs(sig_fft))
freq = sample_freq[idx]
peak_freq = abs(freq)
print(peak_freq)
此代码生成以下图: 我未能实现的是,首先,摆脱所有的噪音,只画一条细线,就像在 psd() 图中一样。其次,要在 x 轴上显示正确的频率值。
所以,我的问题是:
- 我是否以错误的方式应用 Hanning 窗口,或者我怎样才能消除所有噪音?
- 如何在绘图的 x 轴上获得正确的频率值?
[编辑]
这是我对 welch() 方法的尝试:
from scipy.signal import welch
sample_freq, power = welch(signal[0], sdr.sample_rate, window="hamming")
plt.figure(figsize=(9.84, 3.94))
plt.semilogy(sample_freq, power)
plt.xlabel("Frequency (MHz)")
plt.ylabel("Relative power (dB)")
plt.show()
结果: 这样我就无法在任一轴上获得正确的值。此外,中心峰值的一部分丢失了,我完全不明白,而且情节中有一条连接信号两端的恼人线。
[编辑 2]
根据 Francois Gosselin 的回答:以下代码产生的结果与 mpl.psd() 方法产生的结果最相似:
from scipy.signal import welch
corr = 1.5
sample_freq, power = welch(signal[0], fs=sdr.sample_rate, window="hann", nperseg=2048, scaling="spectrum")
sample_freq = fftshift(sample_freq)
power = fftshift(power)/corr
print(sum(power))
plt.figure(figsize=(9.84, 3.94))
plt.semilogy(sample_freq, power)
plt.xlabel("Frequency (MHz)")
plt.ylabel("Relative power (dB)")
plt.show()
现在,唯一剩下的就是弄清楚如何在相应的轴上获得正确的频率(以 MHz 为单位)和功率(以 dB 为单位)值...
[编辑 3]
使用 EDIT 2 中的代码,但使用以下行代替 plt.semilogy(...),
plt.plot((sample_freq+sdr.center_freq)/1e6, np.log10(power))
我得到: 但是,没有必要在 plot() 方法中添加一些“额外的计算”来增强功率,不是吗? welch() 方法不应该已经返回正确的功率级别了吗?
[编辑 4]
在尝试了您编写的所有内容后,我发现获取 plt.psd() 方法返回的频率和功率数组是最容易理解和在我的代码中使用的解决方案:
Pxx, freqs = plt.psd(signals[0], NFFT=2048, Fs=sdr.sample_rate/1e6, Fc=sdr.center_freq/1e6, scale_by_freq=True, color="green")
power_lvls = 10*log10(Pxx/(sdr.sample_rate/1e6))
plt.plot(freqs, power_lvls)
plt.show()
结果图: 有趣的是 plt.psd() 方法似乎使用与我从返回的 Pxx 数组计算它们后得到的功率级别略有不同的功率级别。绿色和红色信号是使用 plt.psd() 绘制来自同一源的两个不同信号的结果,而蓝色信号是通过提供简单的 plot() 方法以及从 plt.psd() 返回的数组产生的( Pxx 除以 sample_rate 和应用于结果的 log10)。
[EDIT 4 的小补充]
我刚刚看到,将计算出的 power_lvls 数组中的值除以 1.1 大致使信号处于与 plt.psd() 绘制的相同的功率水平:
plt.plot(freqs, power_lvls/1.1)
现在,这可能是什么原因?默认情况下,plt.psd() 使用校正值为 1.5 的 Hanning 窗口,或者我认为。
.......
使用以下两行,我现在还可以检索多个峰的坐标:
indexes = peakutils.peak.indexes(np.array(power_lvls), thres=0.6/max(power_lvls), min_dist=120)
print("\nX: {}\n\nY: {}\n".format(freqs_1[indexes], np.array(power_lvls)[indexes]))
【问题讨论】:
-
这个问题很相关,但是……真是一团糟!
标签: python numpy matplotlib scipy