updated for compatibility with new hackrf_sweep -B option
This commit is contained in:
parent
713434d029
commit
4e9ecdb64d
@ -223,7 +223,7 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin
|
|||||||
def load_settings(self):
|
def load_settings(self):
|
||||||
"""Restore spectrum analyzer settings and window geometry"""
|
"""Restore spectrum analyzer settings and window geometry"""
|
||||||
settings = QtCore.QSettings()
|
settings = QtCore.QSettings()
|
||||||
self.startFreqSpinBox.setValue(settings.value("start_freq", 87.0, float))
|
self.startFreqSpinBox.setValue(settings.value("start_freq", 88.0, float))
|
||||||
self.stopFreqSpinBox.setValue(settings.value("stop_freq", 108.0, float))
|
self.stopFreqSpinBox.setValue(settings.value("stop_freq", 108.0, float))
|
||||||
self.binSizeSpinBox.setValue(settings.value("bin_size", 10.0, float))
|
self.binSizeSpinBox.setValue(settings.value("bin_size", 10.0, float))
|
||||||
self.intervalSpinBox.setValue(settings.value("interval", 10.0, float))
|
self.intervalSpinBox.setValue(settings.value("interval", 10.0, float))
|
||||||
|
@ -272,95 +272,61 @@ class HackRFSweepThread(RtlPowerBaseThread):
|
|||||||
def setup(self, start_freq, stop_freq, bin_size, interval=10.0, gain=-1,
|
def setup(self, start_freq, stop_freq, bin_size, interval=10.0, gain=-1,
|
||||||
ppm=0, crop=0, single_shot=False, device_index=0, sample_rate=2560000):
|
ppm=0, crop=0, single_shot=False, device_index=0, sample_rate=2560000):
|
||||||
"""Setup hackrf_sweep params"""
|
"""Setup hackrf_sweep params"""
|
||||||
crop = crop * 100
|
|
||||||
overlap = crop * 2
|
|
||||||
freq_range = stop_freq * 1e6 - start_freq * 1e6
|
|
||||||
min_overhang = sample_rate * overlap * 0.01
|
|
||||||
hops = math.ceil((freq_range - min_overhang) / (sample_rate - min_overhang))
|
|
||||||
overhang = (hops * sample_rate - freq_range) / (hops - 1) if hops > 1 else 0
|
|
||||||
bins = math.ceil(sample_rate / (bin_size * 1e3))
|
|
||||||
crop_freq = sample_rate * crop * 0.01
|
|
||||||
|
|
||||||
self.params = {
|
self.params = {
|
||||||
"start_freq": start_freq,
|
"start_freq": start_freq,
|
||||||
"stop_freq": stop_freq,
|
"stop_freq": stop_freq,
|
||||||
"freq_range": freq_range,
|
"hops": 0,
|
||||||
"device_index": device_index,
|
"device_index": 0,
|
||||||
"sample_rate": sample_rate,
|
"sample_rate": 20e6,
|
||||||
"bin_size": bin_size,
|
"bin_size": 1e6,
|
||||||
"bins": bins,
|
"interval": 0,
|
||||||
"interval": interval,
|
"gain": 0,
|
||||||
"hops": hops,
|
"ppm": 0,
|
||||||
"time": interval / hops,
|
"crop": 0,
|
||||||
"gain": gain * 10,
|
|
||||||
"ppm": ppm,
|
|
||||||
"crop": crop,
|
|
||||||
"overlap": overlap,
|
|
||||||
"min_overhang": min_overhang,
|
|
||||||
"overhang": overhang,
|
|
||||||
"single_shot": single_shot
|
"single_shot": single_shot
|
||||||
}
|
}
|
||||||
self.fft_size = 64
|
self.fft_size = 20
|
||||||
self.freqs = [self.get_hop_freq(hop) for hop in range(hops)]
|
|
||||||
self.freqs_crop = [(f[0] + crop_freq, f[1] - crop_freq) for f in self.freqs]
|
|
||||||
self.databuffer = {"timestamp": [], "x": [], "y": []}
|
self.databuffer = {"timestamp": [], "x": [], "y": []}
|
||||||
self.databuffer_hop = {"timestamp": [], "x": [], "y": []}
|
|
||||||
self.hop = 0
|
|
||||||
self.prev_line = ""
|
|
||||||
self.prev_freq = 0
|
|
||||||
self.skip = True
|
|
||||||
|
|
||||||
print("hackrf_sweep params:")
|
print("hackrf_sweep params:")
|
||||||
pprint.pprint(self.params)
|
pprint.pprint(self.params)
|
||||||
print()
|
print()
|
||||||
|
|
||||||
def get_hop_freq(self, hop):
|
|
||||||
"""Get start and stop frequency for particular hop"""
|
|
||||||
start_freq = self.params["start_freq"] * 1e6 + (self.params["sample_rate"] - self.params["overhang"]) * hop
|
|
||||||
stop_freq = start_freq + self.params["sample_rate"] - (self.params["sample_rate"] / self.params["bins"])
|
|
||||||
return (start_freq, stop_freq)
|
|
||||||
|
|
||||||
def process_start(self):
|
def process_start(self):
|
||||||
"""Start hackrf_sweep process"""
|
"""Start hackrf_sweep process"""
|
||||||
if not self.process and self.params:
|
if not self.process and self.params:
|
||||||
settings = QtCore.QSettings()
|
settings = QtCore.QSettings()
|
||||||
cmdline = [
|
cmdline = [
|
||||||
settings.value("rtl_power_executable", "hackrf_sweep"),
|
settings.value("rtl_power_executable", "hackrf_sweep"),
|
||||||
"-f", "{}:{}".format(int(self.params["start_freq"]),
|
"-f", "{}:{}".format(int(self.params["start_freq"]),
|
||||||
int(self.params["stop_freq"])),
|
int(self.params["stop_freq"])),
|
||||||
|
"-B", "-w", "1000000",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if self.params["single_shot"]:
|
||||||
|
cmdline.append("-1")
|
||||||
|
|
||||||
self.process = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
|
self.process = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
|
||||||
universal_newlines=False)
|
universal_newlines=False)
|
||||||
|
|
||||||
def filter_nan(self, num):
|
|
||||||
if np.isnan(num) or np.isinf(num) or np.isneginf(num):
|
|
||||||
return -80
|
|
||||||
return num
|
|
||||||
|
|
||||||
def parse_output(self, buf):
|
def parse_output(self, buf):
|
||||||
"""Parse one buf of output from hackrf_sweep"""
|
"""Parse one buf of output from hackrf_sweep"""
|
||||||
data = np.fromstring(buf, dtype='<f4')
|
data = np.fromstring(buf, dtype='<f4')
|
||||||
centre_freq = data[0] * 1e6
|
low_edge = data[0] * 1e6
|
||||||
|
high_edge = low_edge + self.params["sample_rate"] // 4
|
||||||
|
bin_width = self.params["sample_rate"] / self.fft_size
|
||||||
|
|
||||||
if centre_freq != self.prev_freq:
|
x_axis = list(np.arange(low_edge, high_edge, bin_width))
|
||||||
if centre_freq < self.prev_freq:
|
self.databuffer["x"].extend(x_axis)
|
||||||
# Skip first run through in case it was incomplete
|
for i in range(self.fft_size//4):
|
||||||
# otherwise the data_storage array sizes are setup incorrectly and mismatch later
|
self.databuffer["y"].append(data[1+i])
|
||||||
if not self.skip:
|
# The -1 below works around floating point errors
|
||||||
sorted_data = sorted(zip(self.databuffer["x"], self.databuffer["y"]))
|
if (high_edge / 1e6) >= (self.params["stop_freq"] - 1):
|
||||||
self.databuffer["x"], self.databuffer["y"] = [list(x) for x in zip(*sorted_data)]
|
sorted_data = sorted(zip(self.databuffer["x"], self.databuffer["y"]))
|
||||||
self.data_storage.update(self.databuffer)
|
self.databuffer["x"], self.databuffer["y"] = [list(x) for x in zip(*sorted_data)]
|
||||||
self.skip = False
|
self.data_storage.update(self.databuffer)
|
||||||
self.databuffer = {"timestamp": [], "x": [], "y": []}
|
self.databuffer = {"timestamp": [], "x": [], "y": []}
|
||||||
|
|
||||||
fft_size_eighth = int(self.fft_size / 8)
|
|
||||||
valid_bins = list(range(fft_size_eighth, fft_size_eighth * 3)) + list(range(fft_size_eighth * 5, fft_size_eighth * 7))
|
|
||||||
|
|
||||||
for i in valid_bins:
|
|
||||||
self.databuffer["x"].append(centre_freq + (i - self.fft_size/2) * 20e6 / self.fft_size)
|
|
||||||
self.databuffer["y"].append(self.filter_nan(data[1+i]))
|
|
||||||
self.prev_freq = centre_freq
|
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@ -370,7 +336,7 @@ class HackRFSweepThread(RtlPowerBaseThread):
|
|||||||
self.rtlPowerStarted.emit()
|
self.rtlPowerStarted.emit()
|
||||||
|
|
||||||
while self.alive:
|
while self.alive:
|
||||||
buf = self.process.stdout.read(4*(1+self.fft_size))
|
buf = self.process.stdout.read(4*(1+self.fft_size//4))
|
||||||
if buf:
|
if buf:
|
||||||
self.parse_output(buf)
|
self.parse_output(buf)
|
||||||
|
|
||||||
|
@ -189,13 +189,13 @@
|
|||||||
<number>3</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<double>1.000000000000000</double>
|
<double>0.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<double>6000.000000000000000</double>
|
<double>6000.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<double>87.000000000000000</double>
|
<double>88.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -227,7 +227,7 @@
|
|||||||
<number>3</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<double>1.000000000000000</double>
|
<double>0.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<double>6000.000000000000000</double>
|
<double>6000.000000000000000</double>
|
||||||
|
@ -116,9 +116,9 @@ class Ui_QSpectrumAnalyzerMainWindow(object):
|
|||||||
self.startFreqSpinBox.setSizePolicy(sizePolicy)
|
self.startFreqSpinBox.setSizePolicy(sizePolicy)
|
||||||
self.startFreqSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
self.startFreqSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
||||||
self.startFreqSpinBox.setDecimals(3)
|
self.startFreqSpinBox.setDecimals(3)
|
||||||
self.startFreqSpinBox.setMinimum(1.0)
|
self.startFreqSpinBox.setMinimum(0.0)
|
||||||
self.startFreqSpinBox.setMaximum(6000.0)
|
self.startFreqSpinBox.setMaximum(6000.0)
|
||||||
self.startFreqSpinBox.setProperty("value", 2390.0)
|
self.startFreqSpinBox.setProperty("value", 88.0)
|
||||||
self.startFreqSpinBox.setObjectName(_fromUtf8("startFreqSpinBox"))
|
self.startFreqSpinBox.setObjectName(_fromUtf8("startFreqSpinBox"))
|
||||||
self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.startFreqSpinBox)
|
self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.startFreqSpinBox)
|
||||||
self.label_3 = QtGui.QLabel(self.frequencyDockWidgetContents)
|
self.label_3 = QtGui.QLabel(self.frequencyDockWidgetContents)
|
||||||
@ -132,9 +132,9 @@ class Ui_QSpectrumAnalyzerMainWindow(object):
|
|||||||
self.stopFreqSpinBox.setSizePolicy(sizePolicy)
|
self.stopFreqSpinBox.setSizePolicy(sizePolicy)
|
||||||
self.stopFreqSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
self.stopFreqSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
||||||
self.stopFreqSpinBox.setDecimals(3)
|
self.stopFreqSpinBox.setDecimals(3)
|
||||||
self.stopFreqSpinBox.setMinimum(1.0)
|
self.stopFreqSpinBox.setMinimum(0.0)
|
||||||
self.stopFreqSpinBox.setMaximum(6000.0)
|
self.stopFreqSpinBox.setMaximum(6000.0)
|
||||||
self.stopFreqSpinBox.setProperty("value", 2510.0)
|
self.stopFreqSpinBox.setProperty("value", 108.0)
|
||||||
self.stopFreqSpinBox.setObjectName(_fromUtf8("stopFreqSpinBox"))
|
self.stopFreqSpinBox.setObjectName(_fromUtf8("stopFreqSpinBox"))
|
||||||
self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.stopFreqSpinBox)
|
self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.stopFreqSpinBox)
|
||||||
self.label = QtGui.QLabel(self.frequencyDockWidgetContents)
|
self.label = QtGui.QLabel(self.frequencyDockWidgetContents)
|
||||||
|
Loading…
Reference in New Issue
Block a user