如何获得复杂RTL-SDR信号峰的坐标?

时间:2018-03-19 08:21:12

标签: python numpy peakutils

我可以使用以下方法从RTL-SDR捕获信号:

from rtlsdr import *

i = 0
signals = []
sdr = RtlSdr()

sdr.sample_rate = 2.8e6     
sdr.center_freq = 434.42e6
sdr.gain = 25

while True:
    samples = sdr.read_samples(1024*1024)
    decibel = 10*log10(var(samples))
    if decibel >= -10:
        signals.append(samples)
        i += 1
    if i == 2:
        break

如果我使用Matplotlib和Seaborn绘制信号,它们看起来像这样: enter image description here

现在,我需要的是使所有峰值的坐标高于某个功率水平,例如-20。

我找到了一个相当有前途的各种Python options列表。但是,所有这些示例都使用了一个简单的Numpy数组,不同的算法可以很容易地使用它。

这是最好的尝试(因为我的猜测是我从RTL-SDR获得一个复杂的信号,并且必须将其“转换”为具有实际值的数组?):

import numpy as np
import peakutils.peak

real_sig = np.real(signals[0])
indexes = peakutils.peak.indexes(real_sig, thres=-20.0/max(real_sig), min_dist=1)
print("Peaks are: %s" % (indexes))

在上面的脚本中添加了这几行,我确实得到了一些输出,但是,首先,对于功率级-20以上的五个峰值,有太多的值。其次,在给定的上下文中,这些值没有多大意义。

enter image description here

那么,我需要改变什么来获得有意义的结果,例如“峰值1是433.22 MHz”?

理想情况下,我应该得到像Peak 1: X = 433.22, Y = -18.0这样的坐标,但我想我知道如何获得正确的X值后我就能弄明白。

4 个答案:

答案 0 :(得分:1)

您缺少几个步骤。

首先需要:从长度为N的RTL-SDR中选择一段复杂的IQ数据(您可能需要将原始IQ样本从无符号8位转换为有符号浮点数),窗口(von Hann或汉明等),通过长度为N的FFT将其转换为频域,将FFT结果转换为对数幅度,并按频率标记FFT对数幅度结果箱(例如阵列元素),大致为

frequency(i) = sdr.center_freq + i * sdr.sample_rate / N

对于0到N / 2的区间

frequency(i) = sdr.center_freq - (N - i) * sdr.sample_rate / N

用于箱子N / 2到N-1

然后,您可以沿着该对数幅度数组搜索峰值,并将频率标签应用于找到的峰值。

补充:您无法直接从RTL-SDR获取频域信息(如频率峰值)。你想要的峰值不在那里。 RTL-SDR输出原始复数/ IQ时域采样,而不是频域数据。所以首先你需要查阅,研究和理解两者之间的区别。那么您可能会理解为什么需要FFT(或DFT,或Goertzels,或小波等)来进行转换。

答案 1 :(得分:1)

类似于:

获取你的signals y值相对幂数组。

sort([x for x in signals > -20])
sort[:i]

为我的拳峰。

对于频率范围:

frequency_peaks = [ frequencyspectrum[a] for a in signals[:i]]

但实际上你应该使用Fourrier变换和分组(@ hotpaw2'答案): enter image description here

numpy.fft可以解决问题:

https://docs.scipy.org/doc/numpy-1.13.0/reference/routines.fft.html

另见:

https://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem

为背景理论。

答案 2 :(得分:0)

我现在尝试了以下代码(灵感来自hotpaw2和我在Google上发现的答案):

from numpy.fft import fft, fftshift
window = np.hamming(sdr.sample_rate/1e6+1)

plt.figure()
A = fft(window, 2048) / 25.5 # what do these values mean?
mag = np.abs(fftshift(A))
freq = np.linspace(sdr.center_freq/1e6-(sdr.sample_rate/1e6)/2, sdr.center_freq/1e6+(sdr.sample_rate/1e6)/2, len(A))
response = 20 * np.log10(mag)
#response = np.clip(response, -100, 100)
plt.plot(freq, response)
plt.title("Frequency response of Hamming window")
plt.ylabel("Magnitude [dB]")
plt.xlabel("Normalized frequency [cycles per sample]")
plt.axis("tight")
plt.show()

来源:https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.hamming.html

这导致以下(绝对无用的)信号图: enter image description here 我需要在原始帖子中使用类似PSD的情节,然后我可以在其中检测到峰值。

有人可以向我解释为什么我应该做所有这些汉明/ FFT的东西吗?

我想要的只是表示我的信号(由RTL-SDR接收),peakutils.peak.indexes()方法接受并返回正确的峰值。

答案 3 :(得分:0)

我相信我已经开始明白你们所有人想要告诉我的事了......

目标仍然是重现如下图表(使用Matplotlib的plt.psd()方法创建): enter image description here 现在,我已经能够提出三个不同的代码段,每个代码段都让我非常接近,但没有一个是完美的:

# Scipy version 1
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()

以上情况产生以下情节: enter image description here 虽然这个数字看起来并不太糟糕,但我完全不知道为什么中心峰的一部分缺失,以及连接图的两端的奇怪线来自何处。另外,我无法弄清楚如何显示功率电平和频率的正确值。

我的下一次尝试:

# Scipy version 2
from scipy.fftpack import fft, fftfreq
window = np.hamming(len(signal[0]))
sig_fft = fft(signal[0])
power = 20*np.log10(np.abs(sig_fft)*window)
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()

这让我得到以下结果: enter image description here 显然,我再次走上正轨,但我不知道如何在该版本的代码中应用汉明窗口(显然,我做错了)。就像之前的尝试一样,我无法弄清楚如何在x轴上显示正确的频率。

我的最后一次尝试使用的是Numpy而不是Scipy:

# Numpy version
window = np.hamming(len(signal[0]))
sig_fft = np.fft.fft(signal[0])
sample_freq = np.fft.fftfreq(signal[0].size, sdr.sample_rate/signal[0].size)
power = 20*np.log10(np.abs(sig_fft))
plt.figure(figsize=(9.84, 3.94))
plt.plot(sample_freq, power)
plt.xlabel("Frequency (MHz)")
plt.ylabel("Relative power (dB)")
plt.show()

结果是: enter image description here 我会说这个可能最接近我想要的(如果没有错误应用的汉明窗口,Scipy版本2会看起来相同),如果它不是所有的噪音。再一次,不知道如何应用汉明窗来摆脱噪音。

我的问题是:

  • 如何在第二个和第三个代码段中应用汉明窗口?
  • 如何在x轴上获得正确的频率(从434.42-1.4到434.42 + 1.4的值)?
  • 为什么信号在所有三个图中的显示功率都显着高于原始图(由plt.psd()创建)?