Merge pull request #10 from xmikos/hackrf_sweep

Merge hackrf_sweep branch into master
This commit is contained in:
Michal Krenek (Mikos) 2017-02-14 13:54:21 +01:00 committed by GitHub
commit 7fa0985133
6 changed files with 257 additions and 99 deletions

View File

@ -5,7 +5,7 @@ import sys, signal, time
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from qspectrumanalyzer.version import __version__ from qspectrumanalyzer.version import __version__
from qspectrumanalyzer.backend import RtlPowerThread, RtlPowerFftwThread, SoapyPowerThread, RxPowerThread from qspectrumanalyzer.backend import RtlPowerThread, RtlPowerFftwThread, SoapyPowerThread, RxPowerThread, HackRFSweepThread
from qspectrumanalyzer.data import DataStorage from qspectrumanalyzer.data import DataStorage
from qspectrumanalyzer.plot import SpectrumPlotWidget, WaterfallPlotWidget from qspectrumanalyzer.plot import SpectrumPlotWidget, WaterfallPlotWidget
from qspectrumanalyzer.utils import color_to_str, str_to_color from qspectrumanalyzer.utils import color_to_str, str_to_color
@ -57,6 +57,15 @@ class QSpectrumAnalyzerSettings(QtGui.QDialog, Ui_QSpectrumAnalyzerSettings):
self.executableEdit.setText(text) self.executableEdit.setText(text)
self.deviceEdit.setText("") self.deviceEdit.setText("")
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): def accept(self):
"""Save settings when dialog is accepted""" """Save settings when dialog is accepted"""
settings = QtCore.QSettings() settings = QtCore.QSettings()
@ -193,6 +202,29 @@ class QSpectrumAnalyzerMainWindow(QtGui.QMainWindow, Ui_QSpectrumAnalyzerMainWin
self.power_thread = RxPowerThread(self.data_storage) self.power_thread = RxPowerThread(self.data_storage)
elif backend == "rtl_power_fftw": elif backend == "rtl_power_fftw":
self.power_thread = RtlPowerFftwThread(self.data_storage) self.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.power_thread = HackRFSweepThread(self.data_storage)
else: else:
self.power_thread = RtlPowerThread(self.data_storage) self.power_thread = RtlPowerThread(self.data_storage)

View File

@ -2,6 +2,7 @@ import subprocess, math, pprint, re
import numpy as np import numpy as np
from PyQt4 import QtCore from PyQt4 import QtCore
import struct
class BasePowerThread(QtCore.QThread): class BasePowerThread(QtCore.QThread):
@ -147,6 +148,8 @@ class RtlPowerThread(BasePowerThread):
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=0, sample_rate=2560000): ppm=0, crop=0, single_shot=False, device=0, sample_rate=2560000):
"""Setup rtl_power params""" """Setup rtl_power params"""
if bin_size > 2800:
bin_size = 2800
self.params = { self.params = {
"start_freq": start_freq, "start_freq": start_freq,
"stop_freq": stop_freq, "stop_freq": stop_freq,
@ -235,6 +238,8 @@ class RtlPowerFftwThread(BasePowerThread):
min_overhang = sample_rate * overlap * 0.01 min_overhang = sample_rate * overlap * 0.01
hops = math.ceil((freq_range - min_overhang) / (sample_rate - min_overhang)) hops = math.ceil((freq_range - min_overhang) / (sample_rate - min_overhang))
overhang = (hops * sample_rate - freq_range) / (hops - 1) if hops > 1 else 0 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)) bins = math.ceil(sample_rate / (bin_size * 1e3))
crop_freq = sample_rate * crop * 0.01 crop_freq = sample_rate * crop * 0.01
@ -450,3 +455,112 @@ class SoapyPowerThread(BasePowerThread):
self.databuffer_hop["y"].append(power) self.databuffer_hop["y"].append(power)
self.prev_line = line self.prev_line = line
class HackRFSweepThread(BasePowerThread):
"""Thread which runs hackrf_sweep process"""
def setup(self, start_freq=0, stop_freq=6000, bin_size=1000,
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
# unlikely to result in acceptable performance
if bin_size < 40:
bin_size = 40
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
# 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
"hops": 0,
"device_index": 0,
"sample_rate": 20e6, # sps
"bin_size": bin_size, # kHz
"interval": 0, # seconds
"gain": gain,
"lna_gain": lna_gain,
"vga_gain": vga_gain,
"ppm": 0,
"crop": 0,
"single_shot": single_shot
}
self.databuffer = {"timestamp": [], "x": [], "y": []}
print("hackrf_sweep params:")
pprint.pprint(self.params)
print()
def process_start(self):
"""Start hackrf_sweep process"""
if not self.process and self.params:
settings = QtCore.QSettings()
cmdline = [
settings.value("executable", "hackrf_sweep"),
"-f", "{}:{}".format(int(self.params["start_freq"]),
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"]:
cmdline.append("-1")
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"""
(low_edge, high_edge) = struct.unpack('QQ', buf[:16])
data = np.fromstring(buf[16:], dtype='<f4')
step = (high_edge - low_edge) / len(data)
if (low_edge//1000000) <= (self.params["start_freq"]):
# Reset databuffer at the start of each sweep even if we somehow
# did not complete the previous sweep.
self.databuffer = {"timestamp": [], "x": [], "y": []}
x_axis = list(np.arange(low_edge + step/2, high_edge, step))
self.databuffer["x"].extend(x_axis)
for i in range(len(data)):
self.databuffer["y"].append(data[i])
if (high_edge / 1e6) >= (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)
def run(self):
"""hackrf_sweep thread main loop"""
self.process_start()
self.alive = True
self.powerThreadStarted.emit()
while self.alive:
buf = self.process.stdout.read(4)
if 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
self.powerThreadStopped.emit()

View File

@ -81,7 +81,22 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../__main__.py" line="472"/> <location filename="../__main__.py" line="337"/>
<source>Frequency hops: {} | Sweep time: {:.2f} s | FPS: {:.2f}</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../__main__.py" line="337"/>
<source>N/A</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../__main__.py" line="504"/>
<source>About - QSpectrumAnalyzer</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../__main__.py" line="504"/>
<source>QSpectrumAnalyzer {}</source> <source>QSpectrumAnalyzer {}</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -111,23 +126,18 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui_qspectrumanalyzer.py" line="341"/> <location filename="../ui_qspectrumanalyzer.py" line="319"/>
<source>&amp;Settings...</source> <source>Start:</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui_qspectrumanalyzer.py" line="342"/> <location filename="../ui_qspectrumanalyzer.py" line="321"/>
<source>&amp;Quit</source> <source>Stop:</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui_qspectrumanalyzer.py" line="343"/> <location filename="../ui_qspectrumanalyzer.py" line="323"/>
<source>Ctrl+Q</source> <source>Bin size:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="344"/>
<source>&amp;About</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
@ -151,18 +161,28 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui_qspectrumanalyzer.py" line="319"/> <location filename="../ui_qspectrumanalyzer.py" line="331"/>
<source>Start:</source> <source>Main curve</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui_qspectrumanalyzer.py" line="321"/> <location filename="../ui_qspectrumanalyzer.py" line="332"/>
<source>Stop:</source> <source>Colors...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui_qspectrumanalyzer.py" line="323"/> <location filename="../ui_qspectrumanalyzer.py" line="333"/>
<source>Bin size:</source> <source>Max. hold</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="334"/>
<source>Min. hold</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="335"/>
<source>Average</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
@ -175,49 +195,29 @@
<source>...</source> <source>...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="../__main__.py" line="305"/>
<source>N/A</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../__main__.py" line="472"/>
<source>About - QSpectrumAnalyzer</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<location filename="../ui_qspectrumanalyzer.py" line="338"/> <location filename="../ui_qspectrumanalyzer.py" line="338"/>
<source>Persistence</source> <source>Persistence</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui_qspectrumanalyzer.py" line="335"/> <location filename="../ui_qspectrumanalyzer.py" line="341"/>
<source>Average</source> <source>&amp;Settings...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui_qspectrumanalyzer.py" line="332"/> <location filename="../ui_qspectrumanalyzer.py" line="342"/>
<source>Colors...</source> <source>&amp;Quit</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui_qspectrumanalyzer.py" line="331"/> <location filename="../ui_qspectrumanalyzer.py" line="343"/>
<source>Main curve</source> <source>Ctrl+Q</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../__main__.py" line="305"/> <location filename="../ui_qspectrumanalyzer.py" line="344"/>
<source>Frequency hops: {} | Sweep time: {:.2f} s | FPS: {:.2f}</source> <source>&amp;About</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="333"/>
<source>Max. hold</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer.py" line="334"/>
<source>Min. hold</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
@ -228,11 +228,6 @@
<source>Persistence - QSpectrumAnalyzer</source> <source>Persistence - QSpectrumAnalyzer</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="../ui_qspectrumanalyzer_persistence.py" line="72"/>
<source>Persistence length:</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<location filename="../ui_qspectrumanalyzer_persistence.py" line="69"/> <location filename="../ui_qspectrumanalyzer_persistence.py" line="69"/>
<source>Decay function:</source> <source>Decay function:</source>
@ -248,72 +243,87 @@
<source>exponential</source> <source>exponential</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="../ui_qspectrumanalyzer_persistence.py" line="72"/>
<source>Persistence length:</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>QSpectrumAnalyzerSettings</name> <name>QSpectrumAnalyzerSettings</name>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="111"/>
<source>rtl_power</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="114"/>
<source>...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="110"/>
<source>rtl_power_fftw</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="107"/>
<source>&amp;Backend:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="112"/>
<source>E&amp;xecutable:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="117"/>
<source>&amp;Waterfall history size:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="116"/>
<source>Sa&amp;mple rate:</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<location filename="../__main__.py" line="50"/> <location filename="../__main__.py" line="50"/>
<source>Select executable - QSpectrumAnalyzer</source> <source>Select executable - QSpectrumAnalyzer</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="106"/> <location filename="../ui_qspectrumanalyzer_settings.py" line="107"/>
<source>Settings - QSpectrumAnalyzer</source> <source>Settings - QSpectrumAnalyzer</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="113"/> <location filename="../ui_qspectrumanalyzer_settings.py" line="108"/>
<source>soapy_power</source> <source>&amp;Backend:</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="115"/> <location filename="../ui_qspectrumanalyzer_settings.py" line="115"/>
<source>soapy_power</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="110"/>
<source>rx_power</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="111"/>
<source>rtl_power_fftw</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="112"/>
<source>rtl_power</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="113"/>
<source>hackrf_sweep</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="114"/>
<source>E&amp;xecutable:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="116"/>
<source>...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="117"/>
<source>Device:</source> <source>Device:</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="109"/> <location filename="../ui_qspectrumanalyzer_settings.py" line="118"/>
<source>rx_power</source> <source>Sa&amp;mple rate:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ui_qspectrumanalyzer_settings.py" line="119"/>
<source>&amp;Waterfall history size:</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context> <context>
<name>QSpectrumAnalyzerSmooth</name> <name>QSpectrumAnalyzerSmooth</name>
<message>
<location filename="../ui_qspectrumanalyzer_smooth.py" line="73"/>
<source>Smoothing - QSpectrumAnalyzer</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<location filename="../ui_qspectrumanalyzer_smooth.py" line="74"/> <location filename="../ui_qspectrumanalyzer_smooth.py" line="74"/>
<source>&amp;Window function:</source> <source>&amp;Window function:</source>
@ -349,10 +359,5 @@
<source>Window len&amp;gth:</source> <source>Window len&amp;gth:</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="../ui_qspectrumanalyzer_smooth.py" line="73"/>
<source>Smoothing - QSpectrumAnalyzer</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
</TS> </TS>

View File

@ -37,7 +37,7 @@ class SpectrumPlotWidget:
self.posLabel = self.layout.addLabel(row=0, col=0, justify="right") self.posLabel = self.layout.addLabel(row=0, col=0, justify="right")
self.plot = self.layout.addPlot(row=1, col=0) self.plot = self.layout.addPlot(row=1, col=0)
self.plot.showGrid(x=True, y=True) 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.setLabel("bottom", "Frequency", units="Hz")
self.plot.setLimits(xMin=0) self.plot.setLimits(xMin=0)
self.plot.showButtons() self.plot.showButtons()
@ -204,7 +204,7 @@ class SpectrumPlotWidget:
if self.plot.sceneBoundingRect().contains(pos): if self.plot.sceneBoundingRect().contains(pos):
mousePoint = self.plot.vb.mapSceneToView(pos) mousePoint = self.plot.vb.mapSceneToView(pos)
self.posLabel.setText( self.posLabel.setText(
"<span style='font-size: 12pt'>f={:0.3f} MHz, P={:0.3f} dBm</span>".format( "<span style='font-size: 12pt'>f={:0.3f} MHz, P={:0.3f} dB</span>".format(
mousePoint.x() / 1e6, mousePoint.x() / 1e6,
mousePoint.y() mousePoint.y()
) )

View File

@ -48,6 +48,11 @@
<string>rtl_power</string> <string>rtl_power</string>
</property> </property>
</item> </item>
<item>
<property name="text">
<string>hackrf_sweep</string>
</property>
</item>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">

View File

@ -39,6 +39,7 @@ class Ui_QSpectrumAnalyzerSettings(object):
self.backendComboBox.addItem(_fromUtf8("")) self.backendComboBox.addItem(_fromUtf8(""))
self.backendComboBox.addItem(_fromUtf8("")) self.backendComboBox.addItem(_fromUtf8(""))
self.backendComboBox.addItem(_fromUtf8("")) self.backendComboBox.addItem(_fromUtf8(""))
self.backendComboBox.addItem(_fromUtf8(""))
self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.backendComboBox) self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.backendComboBox)
self.label = QtGui.QLabel(QSpectrumAnalyzerSettings) self.label = QtGui.QLabel(QSpectrumAnalyzerSettings)
self.label.setObjectName(_fromUtf8("label")) self.label.setObjectName(_fromUtf8("label"))
@ -109,6 +110,7 @@ class Ui_QSpectrumAnalyzerSettings(object):
self.backendComboBox.setItemText(1, _translate("QSpectrumAnalyzerSettings", "rx_power", None)) self.backendComboBox.setItemText(1, _translate("QSpectrumAnalyzerSettings", "rx_power", None))
self.backendComboBox.setItemText(2, _translate("QSpectrumAnalyzerSettings", "rtl_power_fftw", None)) self.backendComboBox.setItemText(2, _translate("QSpectrumAnalyzerSettings", "rtl_power_fftw", None))
self.backendComboBox.setItemText(3, _translate("QSpectrumAnalyzerSettings", "rtl_power", None)) self.backendComboBox.setItemText(3, _translate("QSpectrumAnalyzerSettings", "rtl_power", None))
self.backendComboBox.setItemText(4, _translate("QSpectrumAnalyzerSettings", "hackrf_sweep", None))
self.label.setText(_translate("QSpectrumAnalyzerSettings", "E&xecutable:", None)) self.label.setText(_translate("QSpectrumAnalyzerSettings", "E&xecutable:", None))
self.executableEdit.setText(_translate("QSpectrumAnalyzerSettings", "soapy_power", None)) self.executableEdit.setText(_translate("QSpectrumAnalyzerSettings", "soapy_power", None))
self.executableButton.setText(_translate("QSpectrumAnalyzerSettings", "...", None)) self.executableButton.setText(_translate("QSpectrumAnalyzerSettings", "...", None))