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))