From a3ce184f5e27dc548d678de90cc97b88df907eae Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sat, 30 Jul 2016 06:23:25 +0100 Subject: [PATCH 01/17] Initial hackrf_sweep skeleton --- qspectrumanalyzer/__main__.py | 5 +- qspectrumanalyzer/backend.py | 82 +++++++++++++++++++ .../languages/qspectrumanalyzer_cs.ts | 23 ++++-- .../qspectrumanalyzer_settings.ui | 5 ++ .../ui_qspectrumanalyzer_settings.py | 2 + 5 files changed, 107 insertions(+), 10 deletions(-) diff --git a/qspectrumanalyzer/__main__.py b/qspectrumanalyzer/__main__.py index a160e99..54a6b7c 100755 --- a/qspectrumanalyzer/__main__.py +++ b/qspectrumanalyzer/__main__.py @@ -5,7 +5,7 @@ import sys, signal, time from PyQt4 import QtCore, QtGui from qspectrumanalyzer.version import __version__ -from qspectrumanalyzer.backend import RtlPowerThread, RtlPowerFftwThread +from qspectrumanalyzer.backend import RtlPowerThread, RtlPowerFftwThread, HackRFSweepThread from qspectrumanalyzer.data import DataStorage from qspectrumanalyzer.plot import SpectrumPlotWidget, WaterfallPlotWidget from qspectrumanalyzer.utils import color_to_str, str_to_color @@ -186,6 +186,9 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin backend = settings.value("backend", "rtl_power") if backend == "rtl_power_fftw": self.rtl_power_thread = RtlPowerFftwThread(self.data_storage) + elif backend == "hackrf_sweep": + self.rtl_power_thread = HackRFSweepThread(self.data_storage) + print(self.rtl_power_thread) else: self.rtl_power_thread = RtlPowerThread(self.data_storage) diff --git a/qspectrumanalyzer/backend.py b/qspectrumanalyzer/backend.py index 206c910..a2b83a8 100644 --- a/qspectrumanalyzer/backend.py +++ b/qspectrumanalyzer/backend.py @@ -267,3 +267,85 @@ class RtlPowerFftwThread(RtlPowerBaseThread): pass self.prev_line = line + +class HackRFSweepThread(RtlPowerBaseThread): + """Thread which runs hackrf_sweep process""" + 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): + """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 = { + "start_freq": start_freq, + "stop_freq": stop_freq, + "freq_range": freq_range, + "device_index": device_index, + "sample_rate": sample_rate, + "bin_size": bin_size, + "bins": bins, + "interval": interval, + "hops": hops, + "time": interval / hops, + "gain": gain * 10, + "ppm": ppm, + "crop": crop, + "overlap": overlap, + "min_overhang": min_overhang, + "overhang": overhang, + "single_shot": single_shot + } + 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_hop = {"timestamp": [], "x": [], "y": []} + self.hop = 0 + self.prev_line = "" + + print("hackrf_sweep params:") + pprint.pprint(self.params) + 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): + """Start hackrf_sweep process""" + if not self.process and self.params: + settings = QtCore.QSettings() + cmdline = [ + settings.value("rtl_power_executable", "hackrf_sweep"), + ] + + self.process = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, + universal_newlines=False) + + def parse_output(self, buf): + """Parse one buf of output from hackrf_sweep""" + data = np.fromstring(buf, dtype=' QSpectrumAnalyzerSettings - + rtl_power - + ... - + rtl_power_fftw - + &Backend: - + E&xecutable: - + &Waterfall history size: - + Sa&mple rate: @@ -292,15 +292,20 @@ - + Settings - QSpectrumAnalyzer - + &Device index: + + + hackrf_sweep + + QSpectrumAnalyzerSmooth diff --git a/qspectrumanalyzer/qspectrumanalyzer_settings.ui b/qspectrumanalyzer/qspectrumanalyzer_settings.ui index 2f00976..a35568b 100644 --- a/qspectrumanalyzer/qspectrumanalyzer_settings.ui +++ b/qspectrumanalyzer/qspectrumanalyzer_settings.ui @@ -38,6 +38,11 @@ rtl_power_fftw + + + hackrf_sweep + + diff --git a/qspectrumanalyzer/ui_qspectrumanalyzer_settings.py b/qspectrumanalyzer/ui_qspectrumanalyzer_settings.py index 2c34180..2674234 100644 --- a/qspectrumanalyzer/ui_qspectrumanalyzer_settings.py +++ b/qspectrumanalyzer/ui_qspectrumanalyzer_settings.py @@ -37,6 +37,7 @@ class Ui_QSpectrumAnalyzerSettings(object): self.backendComboBox.setObjectName(_fromUtf8("backendComboBox")) self.backendComboBox.addItem(_fromUtf8("")) self.backendComboBox.addItem(_fromUtf8("")) + self.backendComboBox.addItem(_fromUtf8("")) self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.backendComboBox) self.label = QtGui.QLabel(QSpectrumAnalyzerSettings) self.label.setObjectName(_fromUtf8("label")) @@ -108,6 +109,7 @@ class Ui_QSpectrumAnalyzerSettings(object): self.label_3.setText(_translate("QSpectrumAnalyzerSettings", "&Backend:", None)) self.backendComboBox.setItemText(0, _translate("QSpectrumAnalyzerSettings", "rtl_power", None)) self.backendComboBox.setItemText(1, _translate("QSpectrumAnalyzerSettings", "rtl_power_fftw", None)) + self.backendComboBox.setItemText(2, _translate("QSpectrumAnalyzerSettings", "hackrf_sweep", None)) self.label.setText(_translate("QSpectrumAnalyzerSettings", "E&xecutable:", None)) self.executableEdit.setText(_translate("QSpectrumAnalyzerSettings", "rtl_power", None)) self.executableButton.setText(_translate("QSpectrumAnalyzerSettings", "...", None)) From 1538f37132b2909e456bbc4abccafc1ceae7706f Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sat, 30 Jul 2016 10:51:29 +0100 Subject: [PATCH 02/17] Make it work --- qspectrumanalyzer/backend.py | 25 +++++++++++++++++-- .../languages/qspectrumanalyzer_cs.ts | 8 +++--- qspectrumanalyzer/qspectrumanalyzer.ui | 8 +++--- qspectrumanalyzer/ui_qspectrumanalyzer.py | 12 ++++----- 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/qspectrumanalyzer/backend.py b/qspectrumanalyzer/backend.py index a2b83a8..02a2cf0 100644 --- a/qspectrumanalyzer/backend.py +++ b/qspectrumanalyzer/backend.py @@ -301,12 +301,15 @@ class HackRFSweepThread(RtlPowerBaseThread): "overhang": overhang, "single_shot": single_shot } + self.fft_size = 64 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_hop = {"timestamp": [], "x": [], "y": []} self.hop = 0 self.prev_line = "" + self.prev_freq = 0 + self.skip = True print("hackrf_sweep params:") pprint.pprint(self.params) @@ -329,10 +332,28 @@ class HackRFSweepThread(RtlPowerBaseThread): self.process = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, 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): """Parse one buf of output from hackrf_sweep""" data = np.fromstring(buf, dtype=' - + QSpectrumAnalyzer {} @@ -176,12 +176,12 @@ - + N/A - + About - QSpectrumAnalyzer @@ -206,7 +206,7 @@ - + Frequency hops: {} | Sweep time: {:.2f} s | FPS: {:.2f} diff --git a/qspectrumanalyzer/qspectrumanalyzer.ui b/qspectrumanalyzer/qspectrumanalyzer.ui index 63467ba..5bb6095 100644 --- a/qspectrumanalyzer/qspectrumanalyzer.ui +++ b/qspectrumanalyzer/qspectrumanalyzer.ui @@ -189,10 +189,10 @@ 3 - 24.000000000000000 + 1.000000000000000 - 1766.000000000000000 + 6000.000000000000000 87.000000000000000 @@ -227,10 +227,10 @@ 3 - 24.000000000000000 + 1.000000000000000 - 1766.000000000000000 + 6000.000000000000000 108.000000000000000 diff --git a/qspectrumanalyzer/ui_qspectrumanalyzer.py b/qspectrumanalyzer/ui_qspectrumanalyzer.py index db0ed27..d96153f 100644 --- a/qspectrumanalyzer/ui_qspectrumanalyzer.py +++ b/qspectrumanalyzer/ui_qspectrumanalyzer.py @@ -116,9 +116,9 @@ class Ui_QSpectrumAnalyzerMainWindow(object): self.startFreqSpinBox.setSizePolicy(sizePolicy) self.startFreqSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.startFreqSpinBox.setDecimals(3) - self.startFreqSpinBox.setMinimum(24.0) - self.startFreqSpinBox.setMaximum(1766.0) - self.startFreqSpinBox.setProperty("value", 87.0) + self.startFreqSpinBox.setMinimum(1.0) + self.startFreqSpinBox.setMaximum(6000.0) + self.startFreqSpinBox.setProperty("value", 2390.0) self.startFreqSpinBox.setObjectName(_fromUtf8("startFreqSpinBox")) self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.startFreqSpinBox) self.label_3 = QtGui.QLabel(self.frequencyDockWidgetContents) @@ -132,9 +132,9 @@ class Ui_QSpectrumAnalyzerMainWindow(object): self.stopFreqSpinBox.setSizePolicy(sizePolicy) self.stopFreqSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.stopFreqSpinBox.setDecimals(3) - self.stopFreqSpinBox.setMinimum(24.0) - self.stopFreqSpinBox.setMaximum(1766.0) - self.stopFreqSpinBox.setProperty("value", 108.0) + self.stopFreqSpinBox.setMinimum(1.0) + self.stopFreqSpinBox.setMaximum(6000.0) + self.stopFreqSpinBox.setProperty("value", 2510.0) self.stopFreqSpinBox.setObjectName(_fromUtf8("stopFreqSpinBox")) self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.stopFreqSpinBox) self.label = QtGui.QLabel(self.frequencyDockWidgetContents) From 2c76c5cbe323ca70cc74bfe20c117eada36acea8 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sat, 30 Jul 2016 14:51:34 +0100 Subject: [PATCH 03/17] Pass sweep freq argument --- qspectrumanalyzer/backend.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qspectrumanalyzer/backend.py b/qspectrumanalyzer/backend.py index 02a2cf0..2301ffd 100644 --- a/qspectrumanalyzer/backend.py +++ b/qspectrumanalyzer/backend.py @@ -327,6 +327,8 @@ class HackRFSweepThread(RtlPowerBaseThread): settings = QtCore.QSettings() cmdline = [ settings.value("rtl_power_executable", "hackrf_sweep"), + "-f", "{}:{}".format(int(self.params["start_freq"]), + int(self.params["stop_freq"])), ] self.process = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, From 461e674f8259f16340cbcaff1a700c43414a9cb6 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sat, 30 Jul 2016 14:55:28 +0100 Subject: [PATCH 04/17] Ignore band-edge and DC bins --- qspectrumanalyzer/backend.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/qspectrumanalyzer/backend.py b/qspectrumanalyzer/backend.py index 2301ffd..541d8c7 100644 --- a/qspectrumanalyzer/backend.py +++ b/qspectrumanalyzer/backend.py @@ -349,12 +349,18 @@ class HackRFSweepThread(RtlPowerBaseThread): # Skip first run through in case it was incomplete # otherwise the data_storage array sizes are setup incorrectly and mismatch later if not self.skip: + sorted_data = sorted(zip(self.databuffer["x"], self.databuffer["y"])) + self.databuffer["x"], self.databuffer["y"] = [list(x) for x in zip(*sorted_data)] self.data_storage.update(self.databuffer) self.skip = False self.databuffer = {"timestamp": [], "x": [], "y": []} - self.databuffer["x"] += [centre_freq + i * 20e6 / self.fft_size for i in range(int(-self.fft_size/2), int(self.fft_size/2))] - self.databuffer["y"] += [self.filter_nan(x) for x in data[1:]] + 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 From 713434d0298487c019a4717b9920dae7a043e74d Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 31 Jul 2016 16:05:56 +0100 Subject: [PATCH 05/17] Let the processing thread stop the process Fixes exception where the processing thread would try to read stdout after the process had stopped, but before the processing thread stopped --- qspectrumanalyzer/backend.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qspectrumanalyzer/backend.py b/qspectrumanalyzer/backend.py index 541d8c7..abb3b2f 100644 --- a/qspectrumanalyzer/backend.py +++ b/qspectrumanalyzer/backend.py @@ -17,7 +17,6 @@ class RtlPowerBaseThread(QtCore.QThread): def stop(self): """Stop rtl_power thread""" - self.process_stop() self.alive = False self.wait() From 4e9ecdb64dbbd14f50636187f4937a46fd825184 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Thu, 9 Feb 2017 08:27:53 -0700 Subject: [PATCH 06/17] updated for compatibility with new hackrf_sweep -B option --- qspectrumanalyzer/__main__.py | 2 +- qspectrumanalyzer/backend.py | 92 +++++++---------------- qspectrumanalyzer/qspectrumanalyzer.ui | 6 +- qspectrumanalyzer/ui_qspectrumanalyzer.py | 8 +- 4 files changed, 37 insertions(+), 71 deletions(-) diff --git a/qspectrumanalyzer/__main__.py b/qspectrumanalyzer/__main__.py index 54a6b7c..e96cc24 100755 --- a/qspectrumanalyzer/__main__.py +++ b/qspectrumanalyzer/__main__.py @@ -223,7 +223,7 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin def load_settings(self): """Restore spectrum analyzer settings and window geometry""" 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.binSizeSpinBox.setValue(settings.value("bin_size", 10.0, float)) self.intervalSpinBox.setValue(settings.value("interval", 10.0, float)) diff --git a/qspectrumanalyzer/backend.py b/qspectrumanalyzer/backend.py index abb3b2f..6629bbd 100644 --- a/qspectrumanalyzer/backend.py +++ b/qspectrumanalyzer/backend.py @@ -272,95 +272,61 @@ class HackRFSweepThread(RtlPowerBaseThread): 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): """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 = { "start_freq": start_freq, "stop_freq": stop_freq, - "freq_range": freq_range, - "device_index": device_index, - "sample_rate": sample_rate, - "bin_size": bin_size, - "bins": bins, - "interval": interval, - "hops": hops, - "time": interval / hops, - "gain": gain * 10, - "ppm": ppm, - "crop": crop, - "overlap": overlap, - "min_overhang": min_overhang, - "overhang": overhang, + "hops": 0, + "device_index": 0, + "sample_rate": 20e6, + "bin_size": 1e6, + "interval": 0, + "gain": 0, + "ppm": 0, + "crop": 0, "single_shot": single_shot } - self.fft_size = 64 - 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.fft_size = 20 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:") pprint.pprint(self.params) 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): """Start hackrf_sweep process""" if not self.process and self.params: settings = QtCore.QSettings() cmdline = [ settings.value("rtl_power_executable", "hackrf_sweep"), - "-f", "{}:{}".format(int(self.params["start_freq"]), - int(self.params["stop_freq"])), + "-f", "{}:{}".format(int(self.params["start_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, 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): """Parse one buf of output from hackrf_sweep""" data = np.fromstring(buf, dtype='= (self.params["stop_freq"] - 1): + sorted_data = sorted(zip(self.databuffer["x"], self.databuffer["y"])) + self.databuffer["x"], self.databuffer["y"] = [list(x) for x in zip(*sorted_data)] + self.data_storage.update(self.databuffer) + self.databuffer = {"timestamp": [], "x": [], "y": []} def run(self): @@ -370,7 +336,7 @@ class HackRFSweepThread(RtlPowerBaseThread): self.rtlPowerStarted.emit() 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: self.parse_output(buf) diff --git a/qspectrumanalyzer/qspectrumanalyzer.ui b/qspectrumanalyzer/qspectrumanalyzer.ui index 5bb6095..238c9ab 100644 --- a/qspectrumanalyzer/qspectrumanalyzer.ui +++ b/qspectrumanalyzer/qspectrumanalyzer.ui @@ -189,13 +189,13 @@ 3 - 1.000000000000000 + 0.000000000000000 6000.000000000000000 - 87.000000000000000 + 88.000000000000000 @@ -227,7 +227,7 @@ 3 - 1.000000000000000 + 0.000000000000000 6000.000000000000000 diff --git a/qspectrumanalyzer/ui_qspectrumanalyzer.py b/qspectrumanalyzer/ui_qspectrumanalyzer.py index d96153f..db72b62 100644 --- a/qspectrumanalyzer/ui_qspectrumanalyzer.py +++ b/qspectrumanalyzer/ui_qspectrumanalyzer.py @@ -116,9 +116,9 @@ class Ui_QSpectrumAnalyzerMainWindow(object): self.startFreqSpinBox.setSizePolicy(sizePolicy) self.startFreqSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.startFreqSpinBox.setDecimals(3) - self.startFreqSpinBox.setMinimum(1.0) + self.startFreqSpinBox.setMinimum(0.0) self.startFreqSpinBox.setMaximum(6000.0) - self.startFreqSpinBox.setProperty("value", 2390.0) + self.startFreqSpinBox.setProperty("value", 88.0) self.startFreqSpinBox.setObjectName(_fromUtf8("startFreqSpinBox")) self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.startFreqSpinBox) self.label_3 = QtGui.QLabel(self.frequencyDockWidgetContents) @@ -132,9 +132,9 @@ class Ui_QSpectrumAnalyzerMainWindow(object): self.stopFreqSpinBox.setSizePolicy(sizePolicy) self.stopFreqSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.stopFreqSpinBox.setDecimals(3) - self.stopFreqSpinBox.setMinimum(1.0) + self.stopFreqSpinBox.setMinimum(0.0) self.stopFreqSpinBox.setMaximum(6000.0) - self.stopFreqSpinBox.setProperty("value", 2510.0) + self.stopFreqSpinBox.setProperty("value", 108.0) self.stopFreqSpinBox.setObjectName(_fromUtf8("stopFreqSpinBox")) self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.stopFreqSpinBox) self.label = QtGui.QLabel(self.frequencyDockWidgetContents) From adb3b53058ef4ab236446c4ce406012100522989 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Thu, 9 Feb 2017 19:05:46 -0700 Subject: [PATCH 07/17] updated for recent changes in hackrf_sweep -B output format --- qspectrumanalyzer/backend.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/qspectrumanalyzer/backend.py b/qspectrumanalyzer/backend.py index 6629bbd..25c08ca 100644 --- a/qspectrumanalyzer/backend.py +++ b/qspectrumanalyzer/backend.py @@ -2,6 +2,8 @@ import subprocess, math, pprint import numpy as np from PyQt4 import QtCore +import struct +import time class RtlPowerBaseThread(QtCore.QThread): @@ -279,7 +281,7 @@ class HackRFSweepThread(RtlPowerBaseThread): "hops": 0, "device_index": 0, "sample_rate": 20e6, - "bin_size": 1e6, + "bin_size": int(bin_size*1000), "interval": 0, "gain": 0, "ppm": 0, @@ -301,7 +303,7 @@ class HackRFSweepThread(RtlPowerBaseThread): settings.value("rtl_power_executable", "hackrf_sweep"), "-f", "{}:{}".format(int(self.params["start_freq"]), int(self.params["stop_freq"])), - "-B", "-w", "1000000", + "-B", "-w", "{}".format(self.params["bin_size"]), ] if self.params["single_shot"]: @@ -312,23 +314,19 @@ class HackRFSweepThread(RtlPowerBaseThread): def parse_output(self, buf): """Parse one buf of output from hackrf_sweep""" - data = np.fromstring(buf, dtype='= (self.params["stop_freq"] - 1): + for i in range(len(data)): + self.databuffer["y"].append(data[i]) + if (high_edge / 1e6) >= (self.params["stop_freq"]): sorted_data = sorted(zip(self.databuffer["x"], self.databuffer["y"])) self.databuffer["x"], self.databuffer["y"] = [list(x) for x in zip(*sorted_data)] self.data_storage.update(self.databuffer) self.databuffer = {"timestamp": [], "x": [], "y": []} - def run(self): """hackrf_sweep thread main loop""" self.process_start() @@ -336,9 +334,12 @@ class HackRFSweepThread(RtlPowerBaseThread): self.rtlPowerStarted.emit() while self.alive: - buf = self.process.stdout.read(4*(1+self.fft_size//4)) + buf = self.process.stdout.read(4) if buf: - self.parse_output(buf) + (record_length,) = struct.unpack('I', buf) + buf = self.process.stdout.read(record_length) + if buf: + self.parse_output(buf) self.process_stop() self.alive = False From 71ff6cd950a6b7f1c835b594f50df82f7d6f3808 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Thu, 9 Feb 2017 19:17:24 -0700 Subject: [PATCH 08/17] cleanup --- qspectrumanalyzer/backend.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qspectrumanalyzer/backend.py b/qspectrumanalyzer/backend.py index 25c08ca..43c07da 100644 --- a/qspectrumanalyzer/backend.py +++ b/qspectrumanalyzer/backend.py @@ -3,7 +3,6 @@ import subprocess, math, pprint import numpy as np from PyQt4 import QtCore import struct -import time class RtlPowerBaseThread(QtCore.QThread): @@ -288,7 +287,6 @@ class HackRFSweepThread(RtlPowerBaseThread): "crop": 0, "single_shot": single_shot } - self.fft_size = 20 self.databuffer = {"timestamp": [], "x": [], "y": []} print("hackrf_sweep params:") From eef73719f0257bfea16a15482d3c2f00bbdbf795 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Fri, 10 Feb 2017 16:29:47 -0700 Subject: [PATCH 09/17] compute bin width from number of input bins --- qspectrumanalyzer/backend.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qspectrumanalyzer/backend.py b/qspectrumanalyzer/backend.py index 43c07da..16ec82c 100644 --- a/qspectrumanalyzer/backend.py +++ b/qspectrumanalyzer/backend.py @@ -314,8 +314,9 @@ class HackRFSweepThread(RtlPowerBaseThread): """Parse one buf of output from hackrf_sweep""" (low_edge, high_edge, bin_width) = struct.unpack('QQI', buf[:20]) data = np.fromstring(buf[20:], dtype=' Date: Fri, 10 Feb 2017 16:38:01 -0700 Subject: [PATCH 10/17] removed bin width from hackrf_sweep output format --- qspectrumanalyzer/backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qspectrumanalyzer/backend.py b/qspectrumanalyzer/backend.py index 16ec82c..120abd5 100644 --- a/qspectrumanalyzer/backend.py +++ b/qspectrumanalyzer/backend.py @@ -312,8 +312,8 @@ class HackRFSweepThread(RtlPowerBaseThread): def parse_output(self, buf): """Parse one buf of output from hackrf_sweep""" - (low_edge, high_edge, bin_width) = struct.unpack('QQI', buf[:20]) - data = np.fromstring(buf[20:], dtype=' Date: Fri, 10 Feb 2017 16:57:12 -0700 Subject: [PATCH 11/17] limit bin size configuration to acceptable bounds --- qspectrumanalyzer/backend.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/qspectrumanalyzer/backend.py b/qspectrumanalyzer/backend.py index 120abd5..259db3b 100644 --- a/qspectrumanalyzer/backend.py +++ b/qspectrumanalyzer/backend.py @@ -270,18 +270,25 @@ class RtlPowerFftwThread(RtlPowerBaseThread): class HackRFSweepThread(RtlPowerBaseThread): """Thread which runs hackrf_sweep process""" - 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): + def setup(self, start_freq=0, stop_freq=6000, bin_size=1000, + interval=0.0, gain=0, ppm=0, crop=0, single_shot=False, + device_index=0, sample_rate=20000000): """Setup hackrf_sweep params""" + # theoretically we can support bins smaller than 40 kHz, but it is + # unlikely to result in acceptable performance + if bin_size < 40: + bin_size = 40 + if bin_size > 5000: + bin_size = 5000 self.params = { - "start_freq": start_freq, - "stop_freq": stop_freq, + "start_freq": start_freq, # MHz + "stop_freq": stop_freq, # MHz "hops": 0, "device_index": 0, - "sample_rate": 20e6, - "bin_size": int(bin_size*1000), - "interval": 0, + "sample_rate": 20e6, # Msps + "bin_size": bin_size, # kHz + "interval": 0, # seconds "gain": 0, "ppm": 0, "crop": 0, @@ -301,7 +308,8 @@ class HackRFSweepThread(RtlPowerBaseThread): settings.value("rtl_power_executable", "hackrf_sweep"), "-f", "{}:{}".format(int(self.params["start_freq"]), int(self.params["stop_freq"])), - "-B", "-w", "{}".format(self.params["bin_size"]), + "-B", + "-w", "{}".format(int(self.params["bin_size"]*1000)), ] if self.params["single_shot"]: From 76c3ad2b55ee090494bbf9f54189ee07e6520432 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Fri, 10 Feb 2017 17:04:54 -0700 Subject: [PATCH 12/17] increased default and maximum bin size settings --- qspectrumanalyzer/__main__.py | 2 +- qspectrumanalyzer/backend.py | 4 ++++ qspectrumanalyzer/qspectrumanalyzer.ui | 4 ++-- qspectrumanalyzer/ui_qspectrumanalyzer.py | 4 ++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/qspectrumanalyzer/__main__.py b/qspectrumanalyzer/__main__.py index e96cc24..0a2d33a 100755 --- a/qspectrumanalyzer/__main__.py +++ b/qspectrumanalyzer/__main__.py @@ -225,7 +225,7 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin settings = QtCore.QSettings() self.startFreqSpinBox.setValue(settings.value("start_freq", 88.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", 100.0, float)) self.intervalSpinBox.setValue(settings.value("interval", 10.0, float)) self.gainSpinBox.setValue(settings.value("gain", 0, int)) self.ppmSpinBox.setValue(settings.value("ppm", 0, int)) diff --git a/qspectrumanalyzer/backend.py b/qspectrumanalyzer/backend.py index 259db3b..62f76a1 100644 --- a/qspectrumanalyzer/backend.py +++ b/qspectrumanalyzer/backend.py @@ -65,6 +65,8 @@ class RtlPowerThread(RtlPowerBaseThread): 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): """Setup rtl_power params""" + if bin_size > 2800: + bin_size = 2800 self.params = { "start_freq": start_freq, "stop_freq": stop_freq, @@ -153,6 +155,8 @@ class RtlPowerFftwThread(RtlPowerBaseThread): 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 + if bin_size > 2800: + bin_size = 2800 bins = math.ceil(sample_rate / (bin_size * 1e3)) crop_freq = sample_rate * crop * 0.01 diff --git a/qspectrumanalyzer/qspectrumanalyzer.ui b/qspectrumanalyzer/qspectrumanalyzer.ui index 238c9ab..2562645 100644 --- a/qspectrumanalyzer/qspectrumanalyzer.ui +++ b/qspectrumanalyzer/qspectrumanalyzer.ui @@ -265,10 +265,10 @@ 3 - 2800.000000000000000 + 5000.000000000000000 - 10.000000000000000 + 100.000000000000000 diff --git a/qspectrumanalyzer/ui_qspectrumanalyzer.py b/qspectrumanalyzer/ui_qspectrumanalyzer.py index db72b62..115af1d 100644 --- a/qspectrumanalyzer/ui_qspectrumanalyzer.py +++ b/qspectrumanalyzer/ui_qspectrumanalyzer.py @@ -148,8 +148,8 @@ class Ui_QSpectrumAnalyzerMainWindow(object): self.binSizeSpinBox.setSizePolicy(sizePolicy) self.binSizeSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.binSizeSpinBox.setDecimals(3) - self.binSizeSpinBox.setMaximum(2800.0) - self.binSizeSpinBox.setProperty("value", 10.0) + self.binSizeSpinBox.setMaximum(5000.0) + self.binSizeSpinBox.setProperty("value", 100.0) self.binSizeSpinBox.setObjectName(_fromUtf8("binSizeSpinBox")) self.formLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.binSizeSpinBox) spacerItem1 = QtGui.QSpacerItem(20, 0, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) From c060f82c251b39cb7ddb534199a447b008ca5299 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Fri, 10 Feb 2017 22:18:46 -0700 Subject: [PATCH 13/17] fixed bug that can happen when frequency ranges are interleaved around the stop frequency --- qspectrumanalyzer/backend.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/qspectrumanalyzer/backend.py b/qspectrumanalyzer/backend.py index 62f76a1..1fa6556 100644 --- a/qspectrumanalyzer/backend.py +++ b/qspectrumanalyzer/backend.py @@ -285,12 +285,20 @@ class HackRFSweepThread(RtlPowerBaseThread): if bin_size > 5000: bin_size = 5000 + # We only support whole numbers of steps with bandwidth equal to the + # sample rate. + step_bandwidth = sample_rate / 1000000 + total_bandwidth = stop_freq - start_freq + step_count = 1 + (total_bandwidth - 1) // step_bandwidth + total_bandwidth = step_count * step_bandwidth + stop_freq = start_freq + total_bandwidth + self.params = { "start_freq": start_freq, # MHz "stop_freq": stop_freq, # MHz "hops": 0, "device_index": 0, - "sample_rate": 20e6, # Msps + "sample_rate": 20e6, # sps "bin_size": bin_size, # kHz "interval": 0, # seconds "gain": 0, @@ -328,15 +336,19 @@ class HackRFSweepThread(RtlPowerBaseThread): data = np.fromstring(buf[16:], dtype='= (self.params["stop_freq"]): + # We've reached the end of a pass, so sort and display it. sorted_data = sorted(zip(self.databuffer["x"], self.databuffer["y"])) self.databuffer["x"], self.databuffer["y"] = [list(x) for x in zip(*sorted_data)] self.data_storage.update(self.databuffer) - self.databuffer = {"timestamp": [], "x": [], "y": []} def run(self): """hackrf_sweep thread main loop""" From dc39b44c674a7ed8f9ab19ad3f707687b881c197 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Sat, 11 Feb 2017 21:45:40 -0700 Subject: [PATCH 14/17] hackrf_sweep gain configuration support --- qspectrumanalyzer/__main__.py | 2 ++ qspectrumanalyzer/backend.py | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/qspectrumanalyzer/__main__.py b/qspectrumanalyzer/__main__.py index 0a2d33a..4b37010 100755 --- a/qspectrumanalyzer/__main__.py +++ b/qspectrumanalyzer/__main__.py @@ -187,6 +187,8 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin if backend == "rtl_power_fftw": self.rtl_power_thread = RtlPowerFftwThread(self.data_storage) elif backend == "hackrf_sweep": + self.gainSpinBox.setMaximum(102) + self.gainSpinBox.setValue(40) self.rtl_power_thread = HackRFSweepThread(self.data_storage) print(self.rtl_power_thread) else: diff --git a/qspectrumanalyzer/backend.py b/qspectrumanalyzer/backend.py index 1fa6556..5345629 100644 --- a/qspectrumanalyzer/backend.py +++ b/qspectrumanalyzer/backend.py @@ -275,7 +275,7 @@ class RtlPowerFftwThread(RtlPowerBaseThread): class HackRFSweepThread(RtlPowerBaseThread): """Thread which runs hackrf_sweep process""" def setup(self, start_freq=0, stop_freq=6000, bin_size=1000, - interval=0.0, gain=0, ppm=0, crop=0, single_shot=False, + interval=0.0, gain=40, ppm=0, crop=0, single_shot=False, device_index=0, sample_rate=20000000): """Setup hackrf_sweep params""" # theoretically we can support bins smaller than 40 kHz, but it is @@ -293,6 +293,14 @@ class HackRFSweepThread(RtlPowerBaseThread): total_bandwidth = step_count * step_bandwidth stop_freq = start_freq + total_bandwidth + # distribute gain between two analog gain stages + if gain < 0: + gain = 0 + if gain > 102: + gain = 102 + lna_gain = 8 * (gain // 18) + vga_gain = 2 * ((gain - lna_gain) // 2) + self.params = { "start_freq": start_freq, # MHz "stop_freq": stop_freq, # MHz @@ -301,7 +309,9 @@ class HackRFSweepThread(RtlPowerBaseThread): "sample_rate": 20e6, # sps "bin_size": bin_size, # kHz "interval": 0, # seconds - "gain": 0, + "gain": gain, + "lna_gain": lna_gain, + "vga_gain": vga_gain, "ppm": 0, "crop": 0, "single_shot": single_shot @@ -322,6 +332,8 @@ class HackRFSweepThread(RtlPowerBaseThread): int(self.params["stop_freq"])), "-B", "-w", "{}".format(int(self.params["bin_size"]*1000)), + "-l", "{}".format(int(self.params["lna_gain"])), + "-g", "{}".format(int(self.params["vga_gain"])), ] if self.params["single_shot"]: From 3cc202de89654a3e4e1e43f82e84c2542202aaa5 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Sat, 11 Feb 2017 22:24:44 -0700 Subject: [PATCH 15/17] set backend-specific min/max/default values when backend is selected --- qspectrumanalyzer/__main__.py | 65 ++++++++++++++++++++++- qspectrumanalyzer/qspectrumanalyzer.ui | 14 ++--- qspectrumanalyzer/ui_qspectrumanalyzer.py | 14 ++--- 3 files changed, 77 insertions(+), 16 deletions(-) diff --git a/qspectrumanalyzer/__main__.py b/qspectrumanalyzer/__main__.py index 4b37010..9945745 100755 --- a/qspectrumanalyzer/__main__.py +++ b/qspectrumanalyzer/__main__.py @@ -185,13 +185,74 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin backend = settings.value("backend", "rtl_power") if backend == "rtl_power_fftw": + self.gainSpinBox.setMinimum(-1) + self.gainSpinBox.setMaximum(49) + self.gainSpinBox.setValue(-1) + self.startFreqSpinBox.setMinimum(24) + self.startFreqSpinBox.setMaximum(1766) + self.startFreqSpinBox.setValue(87) + self.stopFreqSpinBox.setMinimum(24) + self.stopFreqSpinBox.setMaximum(1766) + self.stopFreqSpinBox.setValue(108) + self.binSizeSpinBox.setMinimum(0) + self.binSizeSpinBox.setMaximum(2800) + self.binSizeSpinBox.setValue(10) + self.intervalSpinBox.setMinimum(0) + self.intervalSpinBox.setMaximum(999) + self.intervalSpinBox.setValue(10) + self.ppmSpinBox.setMinimum(-999) + self.ppmSpinBox.setMaximum(999) + self.ppmSpinBox.setValue(0) + self.cropSpinBox.setMinimum(0) + self.cropSpinBox.setMaximum(99) + self.cropSpinBox.setValue(0) self.rtl_power_thread = RtlPowerFftwThread(self.data_storage) elif backend == "hackrf_sweep": + self.gainSpinBox.setMinimum(0) self.gainSpinBox.setMaximum(102) self.gainSpinBox.setValue(40) + self.startFreqSpinBox.setMinimum(0) + self.startFreqSpinBox.setMaximum(7230) + self.startFreqSpinBox.setValue(0) + self.stopFreqSpinBox.setMinimum(0) + self.stopFreqSpinBox.setMaximum(7250) + self.stopFreqSpinBox.setValue(6000) + self.binSizeSpinBox.setMinimum(40) + self.binSizeSpinBox.setMaximum(5000) + self.binSizeSpinBox.setValue(1000) + self.intervalSpinBox.setMinimum(0) + self.intervalSpinBox.setMaximum(0) + self.intervalSpinBox.setValue(0) + self.ppmSpinBox.setMinimum(0) + self.ppmSpinBox.setMaximum(0) + self.ppmSpinBox.setValue(0) + self.cropSpinBox.setMinimum(0) + self.cropSpinBox.setMaximum(0) + self.cropSpinBox.setValue(0) self.rtl_power_thread = HackRFSweepThread(self.data_storage) print(self.rtl_power_thread) else: + self.gainSpinBox.setMinimum(-1) + self.gainSpinBox.setMaximum(49) + self.gainSpinBox.setValue(-1) + self.startFreqSpinBox.setMinimum(24) + self.startFreqSpinBox.setMaximum(1766) + self.startFreqSpinBox.setValue(87) + self.stopFreqSpinBox.setMinimum(24) + self.stopFreqSpinBox.setMaximum(1766) + self.stopFreqSpinBox.setValue(108) + self.binSizeSpinBox.setMinimum(0) + self.binSizeSpinBox.setMaximum(2800) + self.binSizeSpinBox.setValue(10) + self.intervalSpinBox.setMinimum(0) + self.intervalSpinBox.setMaximum(999) + self.intervalSpinBox.setValue(10) + self.ppmSpinBox.setMinimum(-999) + self.ppmSpinBox.setMaximum(999) + self.ppmSpinBox.setValue(0) + self.cropSpinBox.setMinimum(0) + self.cropSpinBox.setMaximum(99) + self.cropSpinBox.setValue(0) self.rtl_power_thread = RtlPowerThread(self.data_storage) self.rtl_power_thread.rtlPowerStarted.connect(self.update_buttons) @@ -225,9 +286,9 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin def load_settings(self): """Restore spectrum analyzer settings and window geometry""" settings = QtCore.QSettings() - self.startFreqSpinBox.setValue(settings.value("start_freq", 88.0, float)) + self.startFreqSpinBox.setValue(settings.value("start_freq", 87.0, float)) self.stopFreqSpinBox.setValue(settings.value("stop_freq", 108.0, float)) - self.binSizeSpinBox.setValue(settings.value("bin_size", 100.0, float)) + self.binSizeSpinBox.setValue(settings.value("bin_size", 10.0, float)) self.intervalSpinBox.setValue(settings.value("interval", 10.0, float)) self.gainSpinBox.setValue(settings.value("gain", 0, int)) self.ppmSpinBox.setValue(settings.value("ppm", 0, int)) diff --git a/qspectrumanalyzer/qspectrumanalyzer.ui b/qspectrumanalyzer/qspectrumanalyzer.ui index 2562645..63467ba 100644 --- a/qspectrumanalyzer/qspectrumanalyzer.ui +++ b/qspectrumanalyzer/qspectrumanalyzer.ui @@ -189,13 +189,13 @@ 3 - 0.000000000000000 + 24.000000000000000 - 6000.000000000000000 + 1766.000000000000000 - 88.000000000000000 + 87.000000000000000 @@ -227,10 +227,10 @@ 3 - 0.000000000000000 + 24.000000000000000 - 6000.000000000000000 + 1766.000000000000000 108.000000000000000 @@ -265,10 +265,10 @@ 3 - 5000.000000000000000 + 2800.000000000000000 - 100.000000000000000 + 10.000000000000000 diff --git a/qspectrumanalyzer/ui_qspectrumanalyzer.py b/qspectrumanalyzer/ui_qspectrumanalyzer.py index 115af1d..db0ed27 100644 --- a/qspectrumanalyzer/ui_qspectrumanalyzer.py +++ b/qspectrumanalyzer/ui_qspectrumanalyzer.py @@ -116,9 +116,9 @@ class Ui_QSpectrumAnalyzerMainWindow(object): self.startFreqSpinBox.setSizePolicy(sizePolicy) self.startFreqSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.startFreqSpinBox.setDecimals(3) - self.startFreqSpinBox.setMinimum(0.0) - self.startFreqSpinBox.setMaximum(6000.0) - self.startFreqSpinBox.setProperty("value", 88.0) + self.startFreqSpinBox.setMinimum(24.0) + self.startFreqSpinBox.setMaximum(1766.0) + self.startFreqSpinBox.setProperty("value", 87.0) self.startFreqSpinBox.setObjectName(_fromUtf8("startFreqSpinBox")) self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.startFreqSpinBox) self.label_3 = QtGui.QLabel(self.frequencyDockWidgetContents) @@ -132,8 +132,8 @@ class Ui_QSpectrumAnalyzerMainWindow(object): self.stopFreqSpinBox.setSizePolicy(sizePolicy) self.stopFreqSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.stopFreqSpinBox.setDecimals(3) - self.stopFreqSpinBox.setMinimum(0.0) - self.stopFreqSpinBox.setMaximum(6000.0) + self.stopFreqSpinBox.setMinimum(24.0) + self.stopFreqSpinBox.setMaximum(1766.0) self.stopFreqSpinBox.setProperty("value", 108.0) self.stopFreqSpinBox.setObjectName(_fromUtf8("stopFreqSpinBox")) self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.stopFreqSpinBox) @@ -148,8 +148,8 @@ class Ui_QSpectrumAnalyzerMainWindow(object): self.binSizeSpinBox.setSizePolicy(sizePolicy) self.binSizeSpinBox.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.binSizeSpinBox.setDecimals(3) - self.binSizeSpinBox.setMaximum(5000.0) - self.binSizeSpinBox.setProperty("value", 100.0) + self.binSizeSpinBox.setMaximum(2800.0) + self.binSizeSpinBox.setProperty("value", 10.0) self.binSizeSpinBox.setObjectName(_fromUtf8("binSizeSpinBox")) self.formLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.binSizeSpinBox) spacerItem1 = QtGui.QSpacerItem(20, 0, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) From ba3e113e2994e814a398069a8f10b2d3367f6a10 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Sat, 11 Feb 2017 23:05:08 -0700 Subject: [PATCH 16/17] set sample rate when backend is selected --- qspectrumanalyzer/__main__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/qspectrumanalyzer/__main__.py b/qspectrumanalyzer/__main__.py index 9945745..a61562f 100755 --- a/qspectrumanalyzer/__main__.py +++ b/qspectrumanalyzer/__main__.py @@ -53,6 +53,14 @@ class QSpectrumAnalyzerSettings(QtGui.QDialog, Ui_QSpectrumAnalyzerSettings): def on_backendComboBox_currentIndexChanged(self, text): """Change executable when backend is changed""" self.executableEdit.setText(text) + if text == "hackrf_sweep": + self.sampleRateSpinBox.setMinimum(20000000) + self.sampleRateSpinBox.setMaximum(20000000) + self.sampleRateSpinBox.setValue(20000000) + else: + self.sampleRateSpinBox.setMinimum(0) + self.sampleRateSpinBox.setMaximum(25000000) + self.sampleRateSpinBox.setValue(2560000) def accept(self): """Save settings when dialog is accepted""" From 70b921992764ca8988564001d49041bf2f5e1594 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Sun, 12 Feb 2017 15:03:05 -0700 Subject: [PATCH 17/17] Changed labels from dBm to dB. hackrf_sweep outputs dBFS, but rtl_power outputs dB with some weird offset. In no case would dBm be correct without a calibration option. The relative "dB" label should be acceptable for all cases. --- qspectrumanalyzer/plot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qspectrumanalyzer/plot.py b/qspectrumanalyzer/plot.py index e9fbd55..7b2c159 100644 --- a/qspectrumanalyzer/plot.py +++ b/qspectrumanalyzer/plot.py @@ -37,7 +37,7 @@ class SpectrumPlotWidget: self.posLabel = self.layout.addLabel(row=0, col=0, justify="right") self.plot = self.layout.addPlot(row=1, col=0) self.plot.showGrid(x=True, y=True) - self.plot.setLabel("left", "Power", units="dBm") + self.plot.setLabel("left", "Power", units="dB") self.plot.setLabel("bottom", "Frequency", units="Hz") self.plot.setLimits(xMin=0) self.plot.showButtons() @@ -204,7 +204,7 @@ class SpectrumPlotWidget: if self.plot.sceneBoundingRect().contains(pos): mousePoint = self.plot.vb.mapSceneToView(pos) self.posLabel.setText( - "f={:0.3f} MHz, P={:0.3f} dBm".format( + "f={:0.3f} MHz, P={:0.3f} dB".format( mousePoint.x() / 1e6, mousePoint.y() )