diff --git a/QtTSAfilebrowse.py b/QtTSAfilebrowse.py new file mode 100644 index 0000000..101e0ee --- /dev/null +++ b/QtTSAfilebrowse.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'filebrowse.ui' +# +# Created by: PyQt5 UI code generator 5.15.10 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_Filebrowse(object): + def setupUi(self, Filebrowse): + Filebrowse.setObjectName("Filebrowse") + Filebrowse.resize(818, 405) + self.listWidget = QtWidgets.QListWidget(Filebrowse) + self.listWidget.setGeometry(QtCore.QRect(10, 20, 301, 311)) + self.listWidget.setObjectName("listWidget") + self.download = QtWidgets.QPushButton(Filebrowse) + self.download.setGeometry(QtCore.QRect(10, 350, 80, 26)) + self.download.setObjectName("download") + self.picture = QtWidgets.QLabel(Filebrowse) + self.picture.setGeometry(QtCore.QRect(320, 20, 481, 321)) + self.picture.setText("") + self.picture.setScaledContents(False) + self.picture.setObjectName("picture") + self.downloadBar = QtWidgets.QProgressBar(Filebrowse) + self.downloadBar.setGeometry(QtCore.QRect(100, 350, 211, 26)) + self.downloadBar.setProperty("value", 0) + self.downloadBar.setObjectName("downloadBar") + self.downloadInfo = QtWidgets.QLabel(Filebrowse) + self.downloadInfo.setGeometry(QtCore.QRect(10, 380, 791, 20)) + self.downloadInfo.setText("") + self.downloadInfo.setObjectName("downloadInfo") + + self.retranslateUi(Filebrowse) + QtCore.QMetaObject.connectSlotsByName(Filebrowse) + + def retranslateUi(self, Filebrowse): + _translate = QtCore.QCoreApplication.translate + Filebrowse.setWindowTitle(_translate("Filebrowse", "File List")) + self.download.setText(_translate("Filebrowse", "Save")) diff --git a/QtTSApreferences.py b/QtTSApreferences.py index 02dc0e1..b490d59 100644 --- a/QtTSApreferences.py +++ b/QtTSApreferences.py @@ -14,7 +14,7 @@ class Ui_Preferences(object): def setupUi(self, Preferences): Preferences.setObjectName("Preferences") - Preferences.resize(800, 600) + Preferences.resize(817, 600) self.layoutWidget = QtWidgets.QWidget(Preferences) self.layoutWidget.setGeometry(QtCore.QRect(10, 10, 411, 572)) self.layoutWidget.setObjectName("layoutWidget") @@ -94,73 +94,51 @@ def setupUi(self, Preferences): self.importButton.setObjectName("importButton") self.gridLayout.addWidget(self.importButton, 4, 0, 1, 1) self.layoutWidget1 = QtWidgets.QWidget(Preferences) - self.layoutWidget1.setGeometry(QtCore.QRect(440, 10, 356, 571)) + self.layoutWidget1.setGeometry(QtCore.QRect(440, 10, 361, 571)) self.layoutWidget1.setObjectName("layoutWidget1") self.gridLayout_2 = QtWidgets.QGridLayout(self.layoutWidget1) self.gridLayout_2.setContentsMargins(0, 0, 0, 0) self.gridLayout_2.setObjectName("gridLayout_2") - self.zeroLine = QtWidgets.QCheckBox(self.layoutWidget1) - self.zeroLine.setChecked(False) - self.zeroLine.setObjectName("zeroLine") - self.gridLayout_2.addWidget(self.zeroLine, 2, 1, 1, 1) - self.label = QtWidgets.QLabel(self.layoutWidget1) - self.label.setObjectName("label") - self.gridLayout_2.addWidget(self.label, 7, 0, 1, 1) - self.minPoints = QtWidgets.QSpinBox(self.layoutWidget1) - self.minPoints.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) - self.minPoints.setMinimum(25) - self.minPoints.setMaximum(450) - self.minPoints.setProperty("value", 450) - self.minPoints.setObjectName("minPoints") - self.gridLayout_2.addWidget(self.minPoints, 8, 1, 1, 1) - self.label_14 = QtWidgets.QLabel(self.layoutWidget1) + self.label_13 = QtWidgets.QLabel(self.layoutWidget1) font = QtGui.QFont() font.setBold(True) font.setWeight(75) - self.label_14.setFont(font) - self.label_14.setObjectName("label_14") - self.gridLayout_2.addWidget(self.label_14, 10, 0, 1, 2) - self.peakThreshold = QtWidgets.QSpinBox(self.layoutWidget1) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.peakThreshold.sizePolicy().hasHeightForWidth()) - self.peakThreshold.setSizePolicy(sizePolicy) - self.peakThreshold.setMaximumSize(QtCore.QSize(83, 16777215)) - font = QtGui.QFont() - font.setPointSize(9) - self.peakThreshold.setFont(font) - self.peakThreshold.setPrefix("") - self.peakThreshold.setMinimum(-120) - self.peakThreshold.setMaximum(-20) - self.peakThreshold.setProperty("value", -90) - self.peakThreshold.setObjectName("peakThreshold") - self.gridLayout_2.addWidget(self.peakThreshold, 5, 1, 1, 1) - spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.gridLayout_2.addItem(spacerItem, 16, 0, 1, 1) - self.freqLO = QtWidgets.QDoubleSpinBox(self.layoutWidget1) - self.freqLO.setDecimals(6) - self.freqLO.setMinimum(0.0) - self.freqLO.setMaximum(100000.0) - self.freqLO.setProperty("value", 0.0) - self.freqLO.setObjectName("freqLO") - self.gridLayout_2.addWidget(self.freqLO, 12, 1, 1, 1) + self.label_13.setFont(font) + self.label_13.setObjectName("label_13") + self.gridLayout_2.addWidget(self.label_13, 11, 0, 1, 1) self.rbw_x = QtWidgets.QSpinBox(self.layoutWidget1) self.rbw_x.setMinimum(2) self.rbw_x.setMaximum(10) self.rbw_x.setProperty("value", 3) self.rbw_x.setObjectName("rbw_x") - self.gridLayout_2.addWidget(self.rbw_x, 7, 1, 1, 1) - self.label_4 = QtWidgets.QLabel(self.layoutWidget1) - self.label_4.setObjectName("label_4") - self.gridLayout_2.addWidget(self.label_4, 9, 0, 1, 1) - self.label_8 = QtWidgets.QLabel(self.layoutWidget1) + self.gridLayout_2.addWidget(self.rbw_x, 5, 1, 1, 1) + self.syncTime = QtWidgets.QCheckBox(self.layoutWidget1) + self.syncTime.setObjectName("syncTime") + self.gridLayout_2.addWidget(self.syncTime, 13, 1, 1, 1) + self.plus6Line = QtWidgets.QCheckBox(self.layoutWidget1) + self.plus6Line.setChecked(False) + self.plus6Line.setObjectName("plus6Line") + self.gridLayout_2.addWidget(self.plus6Line, 1, 1, 1, 1) + self.label = QtWidgets.QLabel(self.layoutWidget1) + self.label.setObjectName("label") + self.gridLayout_2.addWidget(self.label, 5, 0, 1, 1) + self.label_14 = QtWidgets.QLabel(self.layoutWidget1) font = QtGui.QFont() font.setBold(True) font.setWeight(75) - self.label_8.setFont(font) - self.label_8.setObjectName("label_8") - self.gridLayout_2.addWidget(self.label_8, 6, 0, 1, 1) + self.label_14.setFont(font) + self.label_14.setObjectName("label_14") + self.gridLayout_2.addWidget(self.label_14, 8, 0, 1, 2) + self.label_11 = QtWidgets.QLabel(self.layoutWidget1) + self.label_11.setObjectName("label_11") + self.gridLayout_2.addWidget(self.label_11, 1, 0, 1, 1) + self.maxPoints = QtWidgets.QSpinBox(self.layoutWidget1) + self.maxPoints.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + self.maxPoints.setMinimum(25) + self.maxPoints.setMaximum(100000) + self.maxPoints.setProperty("value", 30000) + self.maxPoints.setObjectName("maxPoints") + self.gridLayout_2.addWidget(self.maxPoints, 7, 1, 1, 1) self.label_6 = QtWidgets.QLabel(self.layoutWidget1) font = QtGui.QFont() font.setBold(True) @@ -168,56 +146,80 @@ def setupUi(self, Preferences): self.label_6.setFont(font) self.label_6.setObjectName("label_6") self.gridLayout_2.addWidget(self.label_6, 0, 0, 1, 2) + self.label_10 = QtWidgets.QLabel(self.layoutWidget1) + self.label_10.setObjectName("label_10") + self.gridLayout_2.addWidget(self.label_10, 2, 0, 1, 1) self.neg25Line = QtWidgets.QCheckBox(self.layoutWidget1) self.neg25Line.setChecked(False) self.neg25Line.setObjectName("neg25Line") self.gridLayout_2.addWidget(self.neg25Line, 3, 1, 1, 1) - self.label_11 = QtWidgets.QLabel(self.layoutWidget1) - self.label_11.setObjectName("label_11") - self.gridLayout_2.addWidget(self.label_11, 1, 0, 1, 1) + self.label_2 = QtWidgets.QLabel(self.layoutWidget1) + self.label_2.setObjectName("label_2") + self.gridLayout_2.addWidget(self.label_2, 12, 0, 1, 1) + self.label_4 = QtWidgets.QLabel(self.layoutWidget1) + self.label_4.setObjectName("label_4") + self.gridLayout_2.addWidget(self.label_4, 7, 0, 1, 1) + self.highLO = QtWidgets.QCheckBox(self.layoutWidget1) + self.highLO.setObjectName("highLO") + self.gridLayout_2.addWidget(self.highLO, 9, 1, 1, 1) self.label_9 = QtWidgets.QLabel(self.layoutWidget1) self.label_9.setObjectName("label_9") self.gridLayout_2.addWidget(self.label_9, 3, 0, 1, 1) self.label_15 = QtWidgets.QLabel(self.layoutWidget1) self.label_15.setObjectName("label_15") - self.gridLayout_2.addWidget(self.label_15, 11, 0, 1, 1) - self.plus6Line = QtWidgets.QCheckBox(self.layoutWidget1) - self.plus6Line.setChecked(False) - self.plus6Line.setObjectName("plus6Line") - self.gridLayout_2.addWidget(self.plus6Line, 1, 1, 1, 1) - self.label_13 = QtWidgets.QLabel(self.layoutWidget1) - self.label_13.setText("") - self.label_13.setObjectName("label_13") - self.gridLayout_2.addWidget(self.label_13, 13, 0, 1, 1) - self.label_2 = QtWidgets.QLabel(self.layoutWidget1) - self.label_2.setObjectName("label_2") - self.gridLayout_2.addWidget(self.label_2, 5, 0, 1, 1) - self.label_10 = QtWidgets.QLabel(self.layoutWidget1) - self.label_10.setObjectName("label_10") - self.gridLayout_2.addWidget(self.label_10, 2, 0, 1, 1) - self.maxPoints = QtWidgets.QSpinBox(self.layoutWidget1) - self.maxPoints.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) - self.maxPoints.setMinimum(25) - self.maxPoints.setMaximum(100000) - self.maxPoints.setProperty("value", 30000) - self.maxPoints.setObjectName("maxPoints") - self.gridLayout_2.addWidget(self.maxPoints, 9, 1, 1, 1) - self.label_7 = QtWidgets.QLabel(self.layoutWidget1) - font = QtGui.QFont() - font.setBold(True) - font.setWeight(75) - self.label_7.setFont(font) - self.label_7.setObjectName("label_7") - self.gridLayout_2.addWidget(self.label_7, 4, 0, 1, 1) + self.gridLayout_2.addWidget(self.label_15, 9, 0, 1, 1) + self.zeroLine = QtWidgets.QCheckBox(self.layoutWidget1) + self.zeroLine.setChecked(False) + self.zeroLine.setObjectName("zeroLine") + self.gridLayout_2.addWidget(self.zeroLine, 2, 1, 1, 1) self.label_3 = QtWidgets.QLabel(self.layoutWidget1) self.label_3.setObjectName("label_3") - self.gridLayout_2.addWidget(self.label_3, 8, 0, 1, 1) + self.gridLayout_2.addWidget(self.label_3, 6, 0, 1, 1) self.label_16 = QtWidgets.QLabel(self.layoutWidget1) self.label_16.setObjectName("label_16") - self.gridLayout_2.addWidget(self.label_16, 12, 0, 1, 1) - self.highLO = QtWidgets.QCheckBox(self.layoutWidget1) - self.highLO.setObjectName("highLO") - self.gridLayout_2.addWidget(self.highLO, 11, 1, 1, 1) + self.gridLayout_2.addWidget(self.label_16, 10, 0, 1, 1) + self.peakThreshold = QtWidgets.QSpinBox(self.layoutWidget1) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.peakThreshold.sizePolicy().hasHeightForWidth()) + self.peakThreshold.setSizePolicy(sizePolicy) + self.peakThreshold.setMaximumSize(QtCore.QSize(83, 16777215)) + font = QtGui.QFont() + font.setPointSize(9) + self.peakThreshold.setFont(font) + self.peakThreshold.setPrefix("") + self.peakThreshold.setMinimum(-120) + self.peakThreshold.setMaximum(-20) + self.peakThreshold.setProperty("value", -90) + self.peakThreshold.setObjectName("peakThreshold") + self.gridLayout_2.addWidget(self.peakThreshold, 12, 1, 1, 1) + self.label_8 = QtWidgets.QLabel(self.layoutWidget1) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_8.setFont(font) + self.label_8.setObjectName("label_8") + self.gridLayout_2.addWidget(self.label_8, 4, 0, 1, 1) + self.minPoints = QtWidgets.QSpinBox(self.layoutWidget1) + self.minPoints.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + self.minPoints.setMinimum(25) + self.minPoints.setMaximum(450) + self.minPoints.setProperty("value", 450) + self.minPoints.setObjectName("minPoints") + self.gridLayout_2.addWidget(self.minPoints, 6, 1, 1, 1) + self.freqLO = QtWidgets.QDoubleSpinBox(self.layoutWidget1) + self.freqLO.setDecimals(6) + self.freqLO.setMinimum(0.0) + self.freqLO.setMaximum(100000.0) + self.freqLO.setProperty("value", 0.0) + self.freqLO.setObjectName("freqLO") + self.gridLayout_2.addWidget(self.freqLO, 10, 1, 1, 1) + spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_2.addItem(spacerItem, 14, 0, 1, 1) + self.label_17 = QtWidgets.QLabel(self.layoutWidget1) + self.label_17.setObjectName("label_17") + self.gridLayout_2.addWidget(self.label_17, 13, 0, 1, 1) self.retranslateUi(Preferences) QtCore.QMetaObject.connectSlotsByName(Preferences) @@ -232,22 +234,24 @@ def retranslateUi(self, Preferences): self.label_12.setText(_translate("Preferences", "Filter on:")) self.exportButton.setText(_translate("Preferences", "Export")) self.importButton.setText(_translate("Preferences", "Import")) - self.zeroLine.setText(_translate("Preferences", " 0dBm")) + self.label_13.setText(_translate("Preferences", "Miscellaneous")) + self.syncTime.setText(_translate("Preferences", "Sync to PC")) + self.plus6Line.setText(_translate("Preferences", "+6dBm")) self.label.setText(_translate("Preferences", "Points / Resolution Bandwidth")) self.label_14.setText(_translate("Preferences", "External Mixer / LNB")) - self.peakThreshold.setSuffix(_translate("Preferences", "dBm")) - self.freqLO.setSuffix(_translate("Preferences", "MHz")) - self.label_4.setText(_translate("Preferences", "Auto maximum points")) - self.label_8.setText(_translate("Preferences", "Scan Points Settings")) + self.label_11.setText(_translate("Preferences", "Absolute maximum")) self.label_6.setText(_translate("Preferences", "Signal Level Reminder lines")) + self.label_10.setText(_translate("Preferences", "Max with auto attenuator")) self.neg25Line.setText(_translate("Preferences", "-25dBm")) - self.label_11.setText(_translate("Preferences", "Absolute maximum")) + self.label_2.setText(_translate("Preferences", "Peak marker detection threshold")) + self.label_4.setText(_translate("Preferences", "Auto maximum points")) + self.highLO.setText(_translate("Preferences", "True")) self.label_9.setText(_translate("Preferences", "Max for best accuracy")) self.label_15.setText(_translate("Preferences", "LO above displayed Freq")) - self.plus6Line.setText(_translate("Preferences", "+6dBm")) - self.label_2.setText(_translate("Preferences", "Detection Threshold")) - self.label_10.setText(_translate("Preferences", "Max with auto attenuator")) - self.label_7.setText(_translate("Preferences", "Peak Marker Settings")) + self.zeroLine.setText(_translate("Preferences", " 0dBm")) self.label_3.setText(_translate("Preferences", "Auto minimum points")) self.label_16.setText(_translate("Preferences", "LO Frequency")) - self.highLO.setText(_translate("Preferences", "True")) + self.peakThreshold.setSuffix(_translate("Preferences", "dBm")) + self.label_8.setText(_translate("Preferences", "Scan Points Settings")) + self.freqLO.setSuffix(_translate("Preferences", "MHz")) + self.label_17.setText(_translate("Preferences", "Date and Time")) diff --git a/QtTSAprefs.db b/QtTSAprefs.db index 3e02207..fda8be8 100644 Binary files a/QtTSAprefs.db and b/QtTSAprefs.db differ diff --git a/QtTinySA.py b/QtTinySA.py index ff50d3c..0f8aed2 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -25,14 +25,18 @@ import csv from platform import system from PyQt5 import QtWidgets, QtCore -from PyQt5.QtWidgets import QMessageBox, QDataWidgetMapper, QFileDialog +from PyQt5.QtWidgets import QMessageBox, QDataWidgetMapper, QFileDialog, QInputDialog, QLineEdit from PyQt5.QtSql import QSqlDatabase, QSqlRelation, QSqlRelationalTableModel, QSqlRelationalDelegate +from PyQt5.QtGui import QPixmap +from datetime import datetime import pyqtgraph import QtTinySpectrum # the GUI import QtTSApreferences # the GUI preferences window +import QtTSAfilebrowse import struct import serial from serial.tools import list_ports +from io import BytesIO # For 3D import pyqtgraph.opengl as pyqtgl @@ -65,6 +69,7 @@ class analyser: def __init__(self): self.usb = None self.sweeping = False + self.threadRunning = False self.signals = WorkerSignals() self.signals.result.connect(self.sigProcess) self.signals.fullSweep.connect(self.updateGUI) @@ -72,7 +77,6 @@ def __init__(self): self.runTimer = QtCore.QElapsedTimer() # debug self.scale = 174 self.scanMemory = 50 - # self.scan3D = False self.surface = None self.vGrid = None self.usbCheck = QtCore.QTimer() @@ -82,6 +86,8 @@ def __init__(self): self.fifoTimer.timeout.connect(self.usbSend) self.tinySA4 = None self.maxF = 6000 + self.directory = None + self.memF = BytesIO() def openPort(self): self.dev = None @@ -102,7 +108,8 @@ def openPort(self): self.usb = serial.Serial(self.dev, baudrate=576000) logging.info(f'Serial port open: {self.usb.isOpen()}') except serial.SerialException: - logging.info('serial port exception') + logging.info('Serial port exception. Is your username in the "dialout" group?') + logging.info(f'groups: {os.getgroups()}') popUp('Serial Port Exception', QMessageBox.Ok, QMessageBox.Critical) if self.dev and self.usb: self.initialise() @@ -121,11 +128,11 @@ def isConnected(self): self.usbCheck.stop() def initialise(self): - i = 0 + i = 1 hardware = '' - while hardware[:6] != 'tinySA' and i < 3: # try 3 times to detect TinySA + while hardware[:6] != 'tinySA' and i < 4: # try 3 times to detect TinySA hardware = self.version() - logging.info(f'{hardware[:16]}') + logging.info(f'Hardware detection attempt {i}: TinySA version: {hardware[:16]}') i += 1 time.sleep(0.5) # hardware = 'tinySA' # used for testing @@ -153,6 +160,8 @@ def initialise(self): ui.battery.setText(self.battery()) ui.version.setText(hardware[8:16]) + self.setTime() + # connect the rbw & frequency boxes here or it causes startup index errors when they are populated ui.rbw_box.currentIndexChanged.connect(rbwChanged) ui.rbw_auto.clicked.connect(rbwChanged) @@ -160,6 +169,7 @@ def initialise(self): ui.stop_freq.editingFinished.connect(self.freq_changed) ui.centre_freq.valueChanged.connect(lambda: self.freq_changed(True)) # centre/span mode ui.span_freq.valueChanged.connect(lambda: self.freq_changed(True)) # centre/span mode + ui.band_box.currentIndexChanged.connect(band_changed) ui.band_box.activated.connect(band_changed) self.fifoTimer.start(500) # calls self.usbSend() every 500mS to execute serial commands whilst not scanning @@ -205,7 +215,9 @@ def scan(self): # called by 'run' button self.scanCount = 1 self.clearBuffer() self.setRBW() + self.sampleRep() self.runButton('Stop') + self.pause() self.usbSend() self.startMeasurement() # runs measurement in separate thread except serial.SerialException: @@ -235,15 +247,15 @@ def usbSend(self): while self.fifo.qsize() > 0: command = self.fifo.get(block=True, timeout=None) logging.debug(command) - self.usb.write(command) + self.usb.write(command.encode()) self.usb.read_until(b'ch> ') # skip command echo and prompt def serialQuery(self, command): self.usb.timeout = 1 logging.debug(command) - self.usb.write(command) - self.usb.read_until(command + b'\n') # skip command echo - response = self.usb.read_until(b'ch> ') + self.usb.write(command.encode()) + self.usb.read_until(command.encode() + b'\n') # skip command echo + response = self.usb.read_until(b'ch> ') # until prompt logging.debug(response) return response[:-6].decode() # remove prompt @@ -253,6 +265,7 @@ def set_frequencies(self): # creates a numpy array of equi-spaced freqs in Hz. points = self.setPoints() frequencies = np.linspace(startF, stopF, points, dtype=np.int64) logging.debug(f'frequencies = {frequencies}') + self.fPrecision(frequencies) return frequencies def freq_changed(self, centre=False): @@ -270,7 +283,7 @@ def freq_changed(self, centre=False): ui.centre_freq.setValue(startF + (stopF - startF) / 2) ui.span_freq.setValue(stopF - startF) ui.graphWidget.setXRange(startF, stopF) - self.resume() + self.resume() # # without this command, the trace doesn't update def freqOffset(self, frequencies): # for mixers or LNBs external to TinySA startF = frequencies[0] @@ -290,7 +303,7 @@ def freqOffset(self, frequencies): # for mixers or LNBs external to TinySA def setRBW(self): # may be called by measurement thread as well as normally rbw = ui.rbw_box.currentText() # ui values are discrete ones in kHz logging.debug(f'rbw = {rbw}') - command = f'rbw {rbw}\r'.encode() + command = f'rbw {rbw}\r' self.fifo.put(command) def setPoints(self): # may be called by measurement thread as well as normally @@ -339,19 +352,19 @@ def measurement(self, frequencies, readings): # runs in a separate thread self.usb.timeout = self.sweepTimeout(frequencies) if preferences.freqLO != 0: startF, stopF = self.freqOffset(frequencies) - command = f'scanraw {int(startF)} {int(stopF)} {int(points)}\r'.encode() + command = f'scanraw {int(startF)} {int(stopF)} {int(points)}\r' else: - command = f'scanraw {int(frequencies[0])} {int(frequencies[-1])} {int(points)}\r'.encode() + command = f'scanraw {int(frequencies[0])} {int(frequencies[-1])} {int(points)}\r' logging.debug(f'measurement: command = {command}') - self.usb.write(command) + self.usb.write(command.encode()) index = 0 # self.runTimer.start() # debug - self.usb.read_until(command + b'\n{') # skip command echo + self.usb.read_until(command.encode() + b'\n{') # skip command echo dataBlock = '' while dataBlock != b'}ch' and index < points: # if '}ch' it's reached the end of the scan points dataBlock = (self.usb.read(3)) # read a block of 3 bytes of data logging.debug(f'dataBlock: {dataBlock}\n') - if dataBlock == b'}ch': + if dataBlock == b'}ch' or dataBlock == b'}': # from FW165 jog button press returns different value logging.info('jog button pressed') self.sweeping = False break @@ -527,26 +540,26 @@ def runButton(self, action): def pause(self): # pauses the sweeping in either input or output mode - command = 'pause\r'.encode() + command = 'pause\r' self.fifo.put(command) def resume(self): # resumes the sweeping in either input or output mode - command = 'resume\r'.encode() + command = 'resume\r' self.fifo.put(command) def reset(self): # not yet found any detail for what is actually reset - command = 'reset\r'.encode() + command = 'reset\r' self.fifo.put(command) def battery(self): - command = 'vbat\r'.encode() + command = 'vbat\r' vbat = self.serialQuery(command) return vbat def version(self): - command = 'version\r'.encode() + command = 'version\r' version = self.serialQuery(command) return version @@ -554,23 +567,107 @@ def spur(self): sType = ui.spur_box.checkState() options = {0: 'Spur Off', 1: 'Spur Auto', 2: 'Spur On'} ui.spur_box.setText(options.get(sType)) - options = {0: 'spur off\r'.encode(), 1: 'spur auto\r'.encode(), 2: 'spur on\r'.encode()} + options = {0: 'spur off\r', 1: 'spur auto\r', 2: 'spur on\r'} command = options.get(sType) self.fifo.put(command) def lna(self): if ui.lna_box.isChecked(): - command = 'lna on\r'.encode() + command = 'lna on\r' ui.atten_auto.setEnabled(False) # attenuator and lna are switched so mutually exclusive ui.atten_auto.setChecked(False) ui.atten_box.setEnabled(False) ui.atten_box.setValue(0) else: - command = 'lna off\r'.encode() + command = 'lna off\r' ui.atten_auto.setEnabled(True) ui.atten_auto.setChecked(True) self.fifo.put(command) + def setTime(self): + if self.tinySA4 and preferences.syncTime.isChecked(): + dt = datetime.now() + y = dt.year - 2000 + command = f'time b 0x{y}{dt.month:02d}{dt.day:02d} 0x{dt.hour:02d}{dt.minute:02d}{dt.second:02d}\r' + self.fifo.put(command) + + def example(self): + command = 'example\r' + self.fifo.put(command) + + def sampleRep(self): + # sets the number of repeat measurements at each frequency point to the value in the GUI + command = f'repeat {ui.sampleRepeat.value()}\r' + self.fifo.put(command) + + def fPrecision(self, frequencies): # sets the marker indicated frequency precision + fInc = frequencies[1] - frequencies[0] + if fInc > 0: + self.dp = np.clip(int(np.log10(frequencies[0] / fInc)), 0, 5) # number of decicimal places required + logging.debug(f'fPrecision: fInc = {fInc} dp = {self.dp}') + else: + self.dp = 6 + + def listSD(self): + if self.usb: + self.clearBuffer() # clear the USB serial buffer + command = 'sd_list\r' + ls = self.serialQuery(command) + return ls + + def readSD(self, fileName): + command = ('sd_read %s\r' % fileName) + self.usb.write(command.encode()) + self.usb.readline() # discard empty line + format_string = "<1i" # little-endian single integer of 4 bytes + self.usb.timeout = None + buffer = self.usb.read(4) + size = struct.unpack(format_string, buffer) + size = size[0] + data = self.usb.read(size) + self.usb.timeout = 1 + return data + + def dialogBrowse(self): + if self.threadRunning: + popUp("Cannot browse tinySA whilst a scan is running", QMessageBox.Ok, QMessageBox.Information) + return + elif self.usb: + SD = self.listSD() + filebrowse.listWidget.clear() + ls = [] + for i in range(len(SD.splitlines())): + ls.append(SD.splitlines()[i].split(" ")[0]) + filebrowse.listWidget.insertItems(0, ls) + fwindow.show() + else: + popUp('TinySA not found', QMessageBox.Ok, QMessageBox.Critical) + + def fileDownload(self): + selected = filebrowse.listWidget.currentItem().text() # the file selected in the list widget + if self.directory: # already saved a file so use the same folder path as the default + folder = os.path.join(self.directory, selected) + fileName = QFileDialog.getSaveFileName(caption="Save As", directory=folder) + else: + fileName = QFileDialog.getSaveFileName(caption="Save As", directory=selected) + with open(str(fileName[0]), "wb") as file: + file.write(self.memF.getvalue()) + self.directory = os.path.dirname(fileName[0]) + filebrowse.downloadInfo.setText(self.directory) # show the path where the file was saved + + def fileShow(self): + filebrowse.downloadBar.setValue(0) # reset the fake progress bar + filebrowse.picture.clear() + fileName = filebrowse.listWidget.currentItem().text() + self.clearBuffer() # clear the tinySA serial buffer + filebrowse.downloadBar.setValue(20) # update the fake progress bar to show start of download + self.memF.write(self.readSD(fileName)) + if fileName[-3:] == 'bmp': + pixmap = QPixmap() + pixmap.loadFromData(self.memF.getvalue()) + filebrowse.picture.setPixmap(pixmap) + filebrowse.downloadBar.setValue(100) # update the fake progress bar to complete + class display: def __init__(self, name, pen): @@ -672,7 +769,7 @@ def updateMarker(self, frequencies, readings, fPeaks): # called by updateGUI() markerF = options.get(self.markerType) if markerF * 1e6 < np.min(frequencies) or markerF * 1e6 > np.max(frequencies): # marker is out of scan range so just show its frequency - self.vline.label.setText(f'M{self.vline.name()} {self.vline.value():.3f}MHz') + self.vline.label.setText(f'M{self.vline.name()} {self.vline.value():.{tinySA.dp}f}MHz') else: # marker is in scan range fIndex = np.argmin(np.abs(frequencies - (markerF * 1e6))) # find closest value in freq array @@ -680,14 +777,14 @@ def updateMarker(self, frequencies, readings, fPeaks): # called by updateGUI() if dBm > S4.hline.value() or self.markerType[:4] != 'Peak': self.vline.setValue(frequencies[fIndex] / 1e6) # set to the discrete value from frequencies[] if self.markerType == 'Delta': - self.vline.label.setText(f'M{self.vline.name()} {chr(916)}{self.deltaF:.3f}MHz {dBm:.1f}dBm') + self.vline.label.setText(f'M{self.vline.name()} {chr(916)}{self.deltaF:.{tinySA.dp}f}MHz {dBm:.1f}dBm') else: - self.vline.label.setText(f'M{self.vline.name()} {self.vline.value():.3f}MHz {dBm:.1f}dBm') + self.vline.label.setText(f'M{self.vline.name()} {self.vline.value():.{tinySA.dp}f}MHz {dBm:.1f}dBm') def addFreqMarker(self, freq, colour, name, position): # adds simple freq marker without full marker capability if ui.presetLabel.isChecked(): self.marker = ui.graphWidget.addLine(freq, 90, pen=pyqtgraph.mkPen(colour, width=0.5, style=QtCore.Qt.DashLine), - label=name, labelOpts={'position': position, 'color': (colour)}) + label=name, labelOpts={'position': position, 'color': (colour)}) self.marker.label.setMovable(True) else: self.marker = ui.graphWidget.addLine(freq, 90, pen=pyqtgraph.mkPen(colour, width=0.5, style=QtCore.Qt.DashLine)) @@ -728,40 +825,30 @@ class database(): def __init__(self): self.db = None self.dbName = "QtTSAprefs.db" - self.personalDir = platformdirs.user_config_dir(appname=app.applicationName(), ensure_exists=True) - self.globalDir = platformdirs.site_config_dir(appname=app.applicationName()) + self.personalDir = platformdirs.user_config_dir(appname=app.applicationName(), appauthor=False) + self.globalDir = platformdirs.site_config_dir(appname=app.applicationName(), appauthor=False) self.workingDirs = [os.path.dirname(__file__), os.path.dirname(os.path.realpath(__file__)), os.getcwd()] self.dbpath = self._getPersonalisedPath() def _getPersonalisedPath(self): - returnpath = None - # check if config database file exists in ~/.config/qttinysa/ - if os.path.exists(os.path.join(self.personalDir, self.dbName)): - returnpath = self.personalDir + if os.path.exists(os.path.join(self.personalDir, self.dbName)): # check if personal config database file exists logging.info(f'Personal configuration database found at {self.personalDir}') - elif os.path.exists(os.path.join(self.globalDir, self.dbName)): - logging.info(f'Personal configuration database not found in {self.personalDir}') - c = shutil.copy(os.path.join(self.globalDir, self.dbName), self.personalDir) - if os.path.exists(os.path.join(self.personalDir, self.dbName)): - returnpath = self.personalDir - logging.info(f'Global configuration database copied to {c}') - popUp(f'Personal configuration database created at \n{c}', QMessageBox.Ok, QMessageBox.Information) - if returnpath is None: - # no config database file found in personal or global directories - logging.info(f'No configuration database file exists in {self.personalDir} or {self.globalDir}') - # Look for one in the current working folder and in the folder where the python file is stored (or linked to): - # In case QtTinySA is called from outside the stored folder. - for workingDir in self.workingDirs: - if os.path.exists(os.path.join(workingDir, self.dbName)): - logging.info(f'Copying configuration database from {workingDir}') - c = shutil.copy(os.path.join(workingDir, self.dbName), self.personalDir) - if os.path.exists(os.path.join(self.personalDir, self.dbName)): - returnpath = self.personalDir - logging.info(f'Personal configuration database created at {c}') - popUp(f'Personal configuration database created at \n{c}', QMessageBox.Ok, QMessageBox.Information) - else: - raise FileNotFoundError("Unable to create a personal configuration database") - return returnpath + return self.personalDir + if not os.path.exists(self.personalDir): + os.mkdir(self.personalDir) + if os.path.exists(os.path.join(self.globalDir, self.dbName)): + logging.info(f'Global configuration database found at {self.globalDir}') + shutil.copy(os.path.join(self.globalDir, self.dbName), self.personalDir) + logging.info(f'Global configuration database copied from {self.globalDir} to {self.personalDir}') + return self.personalDir + logging.info(f'No configuration database file exists in {self.personalDir} or {self.globalDir}') + # Look in current working folder & where the python file is stored/linked from + for workingDir in self.workingDirs: + if os.path.exists(os.path.join(workingDir, self.dbName)): + shutil.copy(os.path.join(workingDir, self.dbName), self.personalDir) + logging.info(f'Personal configuration database copied from {workingDir} to {self.personalDir}') + return self.personalDir + raise FileNotFoundError("Unable to find the configuration database QtTSAprefs.db") def connect(self): self.db = QSqlDatabase.addDatabase('QSQLITE') @@ -788,6 +875,7 @@ def __init__(self, tableName): self.tm = QSqlRelationalTableModel() self.dwm = QDataWidgetMapper() self.currentRow = 0 + self.currentBand = 0 def createTableModel(self): self.tm.setTable(self.tableName) @@ -820,10 +908,12 @@ def tableClicked(self): def insertData(self, **data): record = self.tm.record() + logging.info(f'insertData: record = {record}') for key, value in data.items(): + logging.info(f'insertData: key = {key} value={value}') record.setValue(str(key), value) self.tm.insertRecord(-1, record) - self.tm.select() + # self.tm.select() self.tm.layoutChanged.emit() self.dwm.submit() @@ -832,13 +922,16 @@ def filterType(self, prefsDialog, boxText): if prefsDialog: if boxText == 'show all': sql = '' + self.tm.setFilter(sql) else: sql = 'visible = "1" AND preset = "' + boxText + '"' if boxText == 'show all': sql = 'visible = "1"' if tinySA.tinySA4 is False: # It's a tinySA basic with limited frequency range sql = sql + ' AND startF <= "960"' - bands.tm.setFilter(sql) + index = ui.band_box.currentIndex() + self.tm.setFilter(sql) + ui.band_box.setCurrentIndex(index) def readCSV(self, fileName): with open(fileName, "r") as fileInput: @@ -849,7 +942,7 @@ def readCSV(self, fileName): # don't understand how to make relation work for these fields if key == 'preset': value = presetID(value) - if key.lower() == 'colour': + if key == 'colour': value = colourID(value) if key == 'value': value = int(eval(value)) @@ -893,14 +986,14 @@ def mapWidget(self, modelName): # maps the widget combo-box fields to the datab def band_changed(): index = ui.band_box.currentIndex() - if bandstype.tm.record(ui.filterBox.currentIndex()).value('type') == 'band': - startF = bands.tm.record(index).value('StartF') - stopF = bands.tm.record(index).value('StopF') + if bandselect.tm.record(index).value('stopF') != '': + startF = bandselect.tm.record(index).value('StartF') + stopF = bandselect.tm.record(index).value('StopF') ui.start_freq.setValue(startF) ui.stop_freq.setValue(stopF) tinySA.freq_changed(False) # start/stop mode else: - centreF = bands.tm.record(index).value('StartF') + centreF = bandselect.tm.record(index).value('StartF') ui.centre_freq.setValue(centreF) ui.span_freq.setValue(1) tinySA.freq_changed(True) # centre mode @@ -908,18 +1001,27 @@ def band_changed(): def addBandPressed(): - if ui.marker1.isChecked() and ui.marker2.isChecked(): + if not ui.marker1.isChecked(): + message = 'Please enable Marker 1' + popUp(message, QMessageBox.Ok, QMessageBox.Information) + return + if ui.marker1.isChecked() and ui.marker2.isChecked(): # Two markers are to set the band limits of a new band if S1.vline.value() >= S2.vline.value(): message = 'M1 frequency >= M2 frequency' popUp(message, QMessageBox.Ok, QMessageBox.Information) return - bandName = 'M' + str(round(S1.vline.value(), 6)) ID = presetID(str(ui.filterBox.currentText())) - bands.insertData(name=bandName, preset=ID, startF=f'{S1.vline.value():.6f}', - stopF=f'{S2.vline.value():.6f}', value=1, colour="aliceblue") - else: - message = 'M1 and M2 must both be enabled to add a new Band' - popUp(message, QMessageBox.Ok, QMessageBox.Information) + title = "New Frequency Band" + message = "Enter a name for the new band." + bandName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal, "") + bands.insertData(name=bandName, type=ID, startF=f'{S1.vline.value():.6f}', + stopF=f'{S2.vline.value():.6f}', visible=1, colour=colourID('green')) # colourID(value) + else: # If only Marker 1 is enabled then this creates a spot Frequency marker + title = "New Spot Frequency Marker" + message = "Enter a name for the Spot Frequency" + spotName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal, "") + bands.insertData(name=spotName, type=12, startF=f'{S1.vline.value():.6f}', + stopF='', visible=1, colour=colourID('orange')) # preset 12 is Marker (spot frequency). def attenuate_changed(): @@ -930,15 +1032,18 @@ def attenuate_changed(): else: if not ui.lna_box.isChecked(): # attenuator and lna are switched so mutually exclusive ui.atten_box.setEnabled(True) - command = f'attenuate {str(atten)}\r'.encode() + command = f'attenuate {str(atten)}\r' tinySA.fifo.put(command) def rbwChanged(): if ui.rbw_auto.isChecked(): # can't calculate Points because we don't know what the RBW will be ui.rbw_box.setEnabled(False) + ui.points_auto.setChecked(False) + ui.points_auto.setEnabled(False) else: ui.rbw_box.setEnabled(True) + ui.points_auto.setEnabled(True) tinySA.setRBW() # if measurement thread is running, calling setRBW() will force it to update scan parameters @@ -948,7 +1053,7 @@ def pointsChanged(): ui.rbw_box.setEnabled(True) else: ui.points_box.setEnabled(True) - tinySA.resume() + tinySA.resume() # without this command, the trace doesn't update def memChanged(): @@ -985,7 +1090,7 @@ def setPreferences(): checkboxes.dwm.submit() bands.tm.submitAll() S4.hline.setValue(preferences.peakThreshold.value()) - bands.filterType(False, ui.filterBox.currentText()) + bandselect.filterType(False, ui.filterBox.currentText()) if ui.presetMarker.isChecked(): freqMarkers() isMixerMode() @@ -993,7 +1098,7 @@ def setPreferences(): def dialogPrefs(): bands.filterType(True, preferences.filterBox.currentText()) - bands.tm.select() + bands.tm.select() # stopping marker add bands.currentRow = 0 preferences.freqBands.selectRow(bands.currentRow) pwindow.show() @@ -1004,6 +1109,7 @@ def about(): .format(app.applicationVersion(), config.dbpath)) popUp(message, QMessageBox.Ok, QMessageBox.Information) + ############################################################################## # other methods @@ -1025,6 +1131,7 @@ def exit_handler(): while tinySA.threadRunning: time.sleep(0.1) # wait for measurements to stop tinySA.resume() + tinySA.usbSend() tinySA.closePort() # close USB connection config.disconnect() # close database logging.info('QtTinySA Closed') @@ -1040,7 +1147,7 @@ def popUp(message, button, icon): def freqMarkers(): - presetmarker.tm.select() + # presetmarker.tm.select() S1.delFreqMarkers() S2.delFreqMarkers() for i in range(0, presetmarker.tm.rowCount()): @@ -1049,11 +1156,11 @@ def freqMarkers(): colour = presetmarker.tm.record(i).value('colour') name = presetmarker.tm.record(i).value('name') if ui.presetMarker.isChecked() and presetmarker.tm.record(i).value('visible')\ - and presetmarker.tm.record(i).value('type') != 'band': + and presetmarker.tm.record(i).value('stopF') == '': S1.addFreqMarker(startF, colour, name, 0.05) if ui.presetLabel.isChecked() and ui.presetLabel.checkState() == 2: S1.marker.label.setAngle(90) - if presetmarker.tm.record(i).value('type') == 'band': + if presetmarker.tm.record(i).value('stopF') != '': stopF = presetmarker.tm.record(i).value('StopF') S1.addFreqMarker(startF, colour, name, 0.98) S2.addFreqMarker(stopF, colour, name, 0.98) @@ -1123,7 +1230,7 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. app = QtWidgets.QApplication([]) # create QApplication for the GUI app.setApplicationName('QtTinySA') -app.setApplicationVersion(' v0.10.3') +app.setApplicationVersion(' v0.10.6') window = QtWidgets.QMainWindow() ui = QtTinySpectrum.Ui_MainWindow() ui.setupUi(window) @@ -1132,6 +1239,10 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. preferences = QtTSApreferences.Ui_Preferences() preferences.setupUi(pwindow) +fwindow = QtWidgets.QDialog() # fwindow is the filebrowse dialogue box +filebrowse = QtTSAfilebrowse.Ui_Filebrowse() +filebrowse.setupUi(fwindow) + # Traces & markers S1 = display('1', yellow) S2 = display('2', magenta) @@ -1141,7 +1252,6 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. # Data models for configuration settings config = database() config.connect() -bands = modelView('frequencies') checkboxes = modelView('checkboxes') numbers = modelView('numbers') markers = modelView('marker') @@ -1152,16 +1262,18 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. bandstype = modelView('freqtype') colours = modelView('SVGColour') maps = modelView('mapping') - -presetmarker = modelView(('frequencies')) +bands = modelView('frequencies') +presetmarker = modelView('frequencies') +bandselect = modelView('frequencies') ############################################################################### # GUI settings # pyqtgraph settings for spectrum display ui.graphWidget.disableAutoRange() # supposed to make pyqtgraph plot faster +# ui.graphWidget.setYRange(-110, 5) -ui.graphWidget.setXRange(87.5, 108) +# ui.graphWidget.setXRange(87.5, 108) ui.graphWidget.setBackground('k') # black ui.graphWidget.showGrid(x=True, y=True) @@ -1223,7 +1335,7 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. # frequency band markers ui.presetMarker.clicked.connect(freqMarkers) -ui.presetLabel.stateChanged.connect(freqMarkerLabel) +ui.presetLabel.clicked.connect(freqMarkerLabel) ui.mToBand.clicked.connect(addBandPressed) ui.filterBox.currentTextChanged.connect(freqMarkers) @@ -1239,6 +1351,8 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. ui.t3_type.activated.connect(S3.tType) ui.t4_type.activated.connect(S4.tType) +ui.sampleRepeat.valueChanged.connect(tinySA.sampleRep) + # 3D graph controls ui.orbitL.clicked.connect(lambda: tinySA.orbit3D(1, True)) ui.orbitR.clicked.connect(lambda: tinySA.orbit3D(-1, True)) @@ -1266,22 +1380,33 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. preferences.deleteAll.clicked.connect(lambda: bands.deleteRow(False)) preferences.freqBands.clicked.connect(bands.tableClicked) preferences.filterBox.currentTextChanged.connect(lambda: bands.filterType(True, preferences.filterBox.currentText())) -ui.filterBox.currentTextChanged.connect(lambda: bands.filterType(False, ui.filterBox.currentText())) +ui.filterBox.currentTextChanged.connect(lambda: bandselect.filterType(False, ui.filterBox.currentText())) ui.actionPreferences.triggered.connect(dialogPrefs) # open preferences dialogue when its menu is clicked ui.actionAbout_QtTinySA.triggered.connect(about) pwindow.finished.connect(setPreferences) # update database checkboxes table on dialogue window close preferences.exportButton.pressed.connect(exportData) preferences.importButton.pressed.connect(importData) +# filebrowse +ui.actionBrowse_TinySA.triggered.connect(tinySA.dialogBrowse) +filebrowse.download.clicked.connect(tinySA.fileDownload) +filebrowse.listWidget.itemSelectionChanged.connect(tinySA.fileShow) + +# Quit +ui.actionQuit.triggered.connect(app.closeAllWindows) + ############################################################################### # set up the application logging.info(f'{app.applicationName()}{app.applicationVersion()}') # table models - read/write views of the configuration data + +# field mapping of the checkboxes from the database maps.createTableModel() maps.tm.select() +# to populate the preset bands and markers relational table in the preferences dialogue bands.createTableModel() bands.tm.setSort(3, QtCore.Qt.AscendingOrder) bands.tm.setHeaderData(5, QtCore.Qt.Horizontal, 'visible') @@ -1290,29 +1415,35 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. bands.tm.setRelation(2, QSqlRelation('freqtype', 'ID', 'preset')) # set 'type' column to a freq type choice combo box bands.tm.setRelation(5, QSqlRelation('boolean', 'ID', 'value')) # set 'view' column to a True/False choice combo box bands.tm.setRelation(6, QSqlRelation('SVGColour', 'ID', 'colour')) # set 'marker' column to a colours choice combo box - presets = QSqlRelationalDelegate(preferences.freqBands) preferences.freqBands.setItemDelegate(presets) colHeader = preferences.freqBands.horizontalHeader() colHeader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents) +# for the filter combo box in the preferences dialogue bandstype.createTableModel() bandstype.tm.select() +# to lookup the preset bands and markers colours because can't get the relationships to work colours.createTableModel() colours.tm.select() +# for the preset markers, which need different filtering to the preferences dialogue presetmarker.createTableModel() presetmarker.tm.setRelation(6, QSqlRelation('SVGColour', 'ID', 'colour')) -presetmarker.tm.setRelation(2, QSqlRelation('freqtype', 'ID', 'type')) +presetmarker.tm.setRelation(2, QSqlRelation('freqtype', 'ID', 'preset')) +presetmarker.tm.setSort(3, QtCore.Qt.AscendingOrder) presetmarker.tm.select() -# populate the band presets combo box -ui.band_box.setModel(bands.tm) +# populate the ui band selection combo box, which needs different filter to preferences dialogue and preset markers +bandselect.createTableModel() +bandselect.tm.setRelation(2, QSqlRelation('freqtype', 'ID', 'preset')) +bandselect.tm.setSort(3, QtCore.Qt.AscendingOrder) +ui.band_box.setModel(bandselect.tm) ui.band_box.setModelColumn(1) -bands.tm.select() # initially select the data in the model +bandselect.tm.select() -# populate the preferences and main GUI filter combo boxes +# populate the preferences dialogue and ui filter combo boxes preferences.filterBox.setModel(bandstype.tm) preferences.filterBox.setModelColumn(1) ui.filterBox.setModel(bandstype.tm) diff --git a/QtTinySpectrum.py b/QtTinySpectrum.py index 89220d3..c7e05b3 100644 --- a/QtTinySpectrum.py +++ b/QtTinySpectrum.py @@ -14,7 +14,7 @@ class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") - MainWindow.resize(1000, 603) + MainWindow.resize(1024, 614) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -39,39 +39,14 @@ def setupUi(self, MainWindow): self.gridLayout.setObjectName("gridLayout") self.gridLayout_3 = QtWidgets.QGridLayout() self.gridLayout_3.setObjectName("gridLayout_3") - spacerItem = QtWidgets.QSpacerItem(17, 13, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.gridLayout_3.addItem(spacerItem, 22, 0, 1, 1) - self.atten_auto = QtWidgets.QCheckBox(self.ViewNormal) + self.trace4 = QtWidgets.QCheckBox(self.ViewNormal) font = QtGui.QFont() font.setPointSize(7) font.setBold(True) font.setWeight(75) - self.atten_auto.setFont(font) - self.atten_auto.setChecked(True) - self.atten_auto.setObjectName("atten_auto") - self.gridLayout_3.addWidget(self.atten_auto, 3, 0, 1, 1) - self.t2_type = QtWidgets.QComboBox(self.ViewNormal) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.t2_type.sizePolicy().hasHeightForWidth()) - self.t2_type.setSizePolicy(sizePolicy) - font = QtGui.QFont() - font.setPointSize(7) - self.t2_type.setFont(font) - self.t2_type.setObjectName("t2_type") - self.gridLayout_3.addWidget(self.t2_type, 15, 0, 1, 1) - self.version = QtWidgets.QLabel(self.ViewNormal) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.version.sizePolicy().hasHeightForWidth()) - self.version.setSizePolicy(sizePolicy) - font = QtGui.QFont() - font.setPointSize(7) - self.version.setFont(font) - self.version.setObjectName("version") - self.gridLayout_3.addWidget(self.version, 24, 0, 1, 1) + self.trace4.setFont(font) + self.trace4.setObjectName("trace4") + self.gridLayout_3.addWidget(self.trace4, 20, 0, 1, 1) self.spur_box = QtWidgets.QCheckBox(self.ViewNormal) font = QtGui.QFont() font.setPointSize(7) @@ -80,14 +55,17 @@ def setupUi(self, MainWindow): self.spur_box.setTristate(True) self.spur_box.setObjectName("spur_box") self.gridLayout_3.addWidget(self.spur_box, 1, 0, 1, 1) - self.trace4 = QtWidgets.QCheckBox(self.ViewNormal) + self.t2_type = QtWidgets.QComboBox(self.ViewNormal) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.t2_type.sizePolicy().hasHeightForWidth()) + self.t2_type.setSizePolicy(sizePolicy) font = QtGui.QFont() font.setPointSize(7) - font.setBold(True) - font.setWeight(75) - self.trace4.setFont(font) - self.trace4.setObjectName("trace4") - self.gridLayout_3.addWidget(self.trace4, 18, 0, 1, 1) + self.t2_type.setFont(font) + self.t2_type.setObjectName("t2_type") + self.gridLayout_3.addWidget(self.t2_type, 17, 0, 1, 1) self.t4_type = QtWidgets.QComboBox(self.ViewNormal) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -98,7 +76,18 @@ def setupUi(self, MainWindow): font.setPointSize(7) self.t4_type.setFont(font) self.t4_type.setObjectName("t4_type") - self.gridLayout_3.addWidget(self.t4_type, 19, 0, 1, 1) + self.gridLayout_3.addWidget(self.t4_type, 21, 0, 1, 1) + self.t3_type = QtWidgets.QComboBox(self.ViewNormal) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.t3_type.sizePolicy().hasHeightForWidth()) + self.t3_type.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setPointSize(7) + self.t3_type.setFont(font) + self.t3_type.setObjectName("t3_type") + self.gridLayout_3.addWidget(self.t3_type, 19, 0, 1, 1) self.atten_box = QtWidgets.QSpinBox(self.ViewNormal) self.atten_box.setEnabled(False) font = QtGui.QFont() @@ -110,6 +99,51 @@ def setupUi(self, MainWindow): self.atten_box.setProperty("value", 0) self.atten_box.setObjectName("atten_box") self.gridLayout_3.addWidget(self.atten_box, 4, 0, 1, 1) + self.version = QtWidgets.QLabel(self.ViewNormal) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.version.sizePolicy().hasHeightForWidth()) + self.version.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setPointSize(7) + self.version.setFont(font) + self.version.setObjectName("version") + self.gridLayout_3.addWidget(self.version, 26, 0, 1, 1) + self.t1_type = QtWidgets.QComboBox(self.ViewNormal) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.t1_type.sizePolicy().hasHeightForWidth()) + self.t1_type.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setPointSize(7) + self.t1_type.setFont(font) + self.t1_type.setModelColumn(0) + self.t1_type.setObjectName("t1_type") + self.gridLayout_3.addWidget(self.t1_type, 14, 0, 1, 1) + self.points_box = QtWidgets.QSpinBox(self.ViewNormal) + font = QtGui.QFont() + font.setPointSize(7) + self.points_box.setFont(font) + self.points_box.setSuffix("") + self.points_box.setMinimum(1) + self.points_box.setMaximum(30000) + self.points_box.setSingleStep(100) + self.points_box.setStepType(QtWidgets.QAbstractSpinBox.AdaptiveDecimalStepType) + self.points_box.setProperty("value", 400) + self.points_box.setObjectName("points_box") + self.gridLayout_3.addWidget(self.points_box, 11, 0, 1, 1) + self.points_auto = QtWidgets.QCheckBox(self.ViewNormal) + self.points_auto.setEnabled(True) + font = QtGui.QFont() + font.setPointSize(7) + font.setBold(True) + font.setWeight(75) + self.points_auto.setFont(font) + self.points_auto.setChecked(True) + self.points_auto.setObjectName("points_auto") + self.gridLayout_3.addWidget(self.points_auto, 10, 0, 1, 1) self.mixerMode = QtWidgets.QLabel(self.ViewNormal) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -128,15 +162,33 @@ def setupUi(self, MainWindow): self.mixerMode.setScaledContents(False) self.mixerMode.setAlignment(QtCore.Qt.AlignCenter) self.mixerMode.setObjectName("mixerMode") - self.gridLayout_3.addWidget(self.mixerMode, 23, 0, 1, 1) - self.lna_box = QtWidgets.QCheckBox(self.ViewNormal) + self.gridLayout_3.addWidget(self.mixerMode, 25, 0, 1, 1) + self.atten_auto = QtWidgets.QCheckBox(self.ViewNormal) font = QtGui.QFont() font.setPointSize(7) - font.setBold(False) - font.setWeight(50) - self.lna_box.setFont(font) - self.lna_box.setObjectName("lna_box") - self.gridLayout_3.addWidget(self.lna_box, 0, 0, 1, 1) + font.setBold(True) + font.setWeight(75) + self.atten_auto.setFont(font) + self.atten_auto.setChecked(True) + self.atten_auto.setObjectName("atten_auto") + self.gridLayout_3.addWidget(self.atten_auto, 3, 0, 1, 1) + self.trace3 = QtWidgets.QCheckBox(self.ViewNormal) + font = QtGui.QFont() + font.setPointSize(7) + font.setBold(True) + font.setWeight(75) + self.trace3.setFont(font) + self.trace3.setObjectName("trace3") + self.gridLayout_3.addWidget(self.trace3, 18, 0, 1, 1) + self.trace2 = QtWidgets.QCheckBox(self.ViewNormal) + font = QtGui.QFont() + font.setPointSize(7) + font.setBold(True) + font.setWeight(75) + self.trace2.setFont(font) + self.trace2.setTristate(False) + self.trace2.setObjectName("trace2") + self.gridLayout_3.addWidget(self.trace2, 16, 0, 1, 1) self.trace1 = QtWidgets.QCheckBox(self.ViewNormal) font = QtGui.QFont() font.setPointSize(7) @@ -146,15 +198,15 @@ def setupUi(self, MainWindow): self.trace1.setChecked(True) self.trace1.setTristate(False) self.trace1.setObjectName("trace1") - self.gridLayout_3.addWidget(self.trace1, 11, 0, 1, 1) - self.trace3 = QtWidgets.QCheckBox(self.ViewNormal) + self.gridLayout_3.addWidget(self.trace1, 13, 0, 1, 1) + self.lna_box = QtWidgets.QCheckBox(self.ViewNormal) font = QtGui.QFont() font.setPointSize(7) - font.setBold(True) - font.setWeight(75) - self.trace3.setFont(font) - self.trace3.setObjectName("trace3") - self.gridLayout_3.addWidget(self.trace3, 16, 0, 1, 1) + font.setBold(False) + font.setWeight(50) + self.lna_box.setFont(font) + self.lna_box.setObjectName("lna_box") + self.gridLayout_3.addWidget(self.lna_box, 0, 0, 1, 1) self.battery = QtWidgets.QLabel(self.ViewNormal) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) @@ -165,64 +217,9 @@ def setupUi(self, MainWindow): font.setPointSize(7) self.battery.setFont(font) self.battery.setObjectName("battery") - self.gridLayout_3.addWidget(self.battery, 25, 0, 1, 1) - self.t3_type = QtWidgets.QComboBox(self.ViewNormal) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.t3_type.sizePolicy().hasHeightForWidth()) - self.t3_type.setSizePolicy(sizePolicy) - font = QtGui.QFont() - font.setPointSize(7) - self.t3_type.setFont(font) - self.t3_type.setObjectName("t3_type") - self.gridLayout_3.addWidget(self.t3_type, 17, 0, 1, 1) - self.t1_type = QtWidgets.QComboBox(self.ViewNormal) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.t1_type.sizePolicy().hasHeightForWidth()) - self.t1_type.setSizePolicy(sizePolicy) - font = QtGui.QFont() - font.setPointSize(7) - self.t1_type.setFont(font) - self.t1_type.setModelColumn(0) - self.t1_type.setObjectName("t1_type") - self.gridLayout_3.addWidget(self.t1_type, 12, 0, 1, 1) - self.points_auto = QtWidgets.QCheckBox(self.ViewNormal) - self.points_auto.setEnabled(True) - font = QtGui.QFont() - font.setPointSize(7) - font.setBold(True) - font.setWeight(75) - self.points_auto.setFont(font) - self.points_auto.setChecked(True) - self.points_auto.setObjectName("points_auto") - self.rbwpointsgroup = QtWidgets.QButtonGroup(MainWindow) - self.rbwpointsgroup.setObjectName("rbwpointsgroup") - self.rbwpointsgroup.addButton(self.points_auto) - self.gridLayout_3.addWidget(self.points_auto, 9, 0, 1, 1) - self.trace2 = QtWidgets.QCheckBox(self.ViewNormal) - font = QtGui.QFont() - font.setPointSize(7) - font.setBold(True) - font.setWeight(75) - self.trace2.setFont(font) - self.trace2.setTristate(False) - self.trace2.setObjectName("trace2") - self.gridLayout_3.addWidget(self.trace2, 14, 0, 1, 1) - self.points_box = QtWidgets.QSpinBox(self.ViewNormal) - font = QtGui.QFont() - font.setPointSize(7) - self.points_box.setFont(font) - self.points_box.setSuffix("") - self.points_box.setMinimum(1) - self.points_box.setMaximum(30000) - self.points_box.setSingleStep(100) - self.points_box.setStepType(QtWidgets.QAbstractSpinBox.AdaptiveDecimalStepType) - self.points_box.setProperty("value", 400) - self.points_box.setObjectName("points_box") - self.gridLayout_3.addWidget(self.points_box, 10, 0, 1, 1) + self.gridLayout_3.addWidget(self.battery, 27, 0, 1, 1) + spacerItem = QtWidgets.QSpacerItem(17, 13, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.gridLayout_3.addItem(spacerItem, 24, 0, 1, 1) self.rbw_box = QtWidgets.QComboBox(self.ViewNormal) self.rbw_box.setEnabled(True) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) @@ -236,6 +233,11 @@ def setupUi(self, MainWindow): self.rbw_box.setEditable(True) self.rbw_box.setObjectName("rbw_box") self.gridLayout_3.addWidget(self.rbw_box, 8, 0, 1, 1) + self.line_2 = QtWidgets.QFrame(self.ViewNormal) + self.line_2.setFrameShape(QtWidgets.QFrame.HLine) + self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_2.setObjectName("line_2") + self.gridLayout_3.addWidget(self.line_2, 2, 0, 1, 1) self.rbw_auto = QtWidgets.QCheckBox(self.ViewNormal) font = QtGui.QFont() font.setPointSize(7) @@ -243,13 +245,16 @@ def setupUi(self, MainWindow): font.setWeight(75) self.rbw_auto.setFont(font) self.rbw_auto.setObjectName("rbw_auto") - self.rbwpointsgroup.addButton(self.rbw_auto) self.gridLayout_3.addWidget(self.rbw_auto, 7, 0, 1, 1) - self.line_2 = QtWidgets.QFrame(self.ViewNormal) - self.line_2.setFrameShape(QtWidgets.QFrame.HLine) - self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken) - self.line_2.setObjectName("line_2") - self.gridLayout_3.addWidget(self.line_2, 2, 0, 1, 1) + self.sampleRepeat = QtWidgets.QSpinBox(self.ViewNormal) + font = QtGui.QFont() + font.setPointSize(7) + self.sampleRepeat.setFont(font) + self.sampleRepeat.setMinimum(1) + self.sampleRepeat.setMaximum(10000) + self.sampleRepeat.setStepType(QtWidgets.QAbstractSpinBox.AdaptiveDecimalStepType) + self.sampleRepeat.setObjectName("sampleRepeat") + self.gridLayout_3.addWidget(self.sampleRepeat, 12, 0, 1, 1) self.gridLayout.addLayout(self.gridLayout_3, 0, 0, 1, 1) self.gridLayout_2 = QtWidgets.QGridLayout() self.gridLayout_2.setObjectName("gridLayout_2") @@ -346,7 +351,7 @@ def setupUi(self, MainWindow): self.avgSlider.setFont(font) self.avgSlider.setMinimum(1) self.avgSlider.setMaximum(101) - self.avgSlider.setProperty("value", 10) + self.avgSlider.setProperty("value", 1) self.avgSlider.setOrientation(QtCore.Qt.Horizontal) self.avgSlider.setInvertedAppearance(False) self.avgSlider.setInvertedControls(False) @@ -901,12 +906,14 @@ def setupUi(self, MainWindow): self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.menuBar = QtWidgets.QMenuBar(MainWindow) - self.menuBar.setGeometry(QtCore.QRect(0, 0, 1000, 23)) + self.menuBar.setGeometry(QtCore.QRect(0, 0, 1024, 23)) self.menuBar.setObjectName("menuBar") self.menu_Help = QtWidgets.QMenu(self.menuBar) self.menu_Help.setObjectName("menu_Help") self.menuSettings = QtWidgets.QMenu(self.menuBar) self.menuSettings.setObjectName("menuSettings") + self.menuFile = QtWidgets.QMenu(self.menuBar) + self.menuFile.setObjectName("menuFile") MainWindow.setMenuBar(self.menuBar) self.actionsetting = QtWidgets.QAction(MainWindow) self.actionsetting.setObjectName("actionsetting") @@ -920,8 +927,15 @@ def setupUi(self, MainWindow): self.actionSweeps.setObjectName("actionSweeps") self.actionbands = QtWidgets.QAction(MainWindow) self.actionbands.setObjectName("actionbands") + self.actionBrowse_TinySA = QtWidgets.QAction(MainWindow) + self.actionBrowse_TinySA.setObjectName("actionBrowse_TinySA") + self.actionQuit = QtWidgets.QAction(MainWindow) + self.actionQuit.setObjectName("actionQuit") self.menu_Help.addAction(self.actionAbout_QtTinySA) self.menuSettings.addAction(self.actionPreferences) + self.menuFile.addAction(self.actionBrowse_TinySA) + self.menuFile.addAction(self.actionQuit) + self.menuBar.addAction(self.menuFile.menuAction()) self.menuBar.addAction(self.menuSettings.menuAction()) self.menuBar.addAction(self.menu_Help.menuAction()) @@ -931,37 +945,38 @@ def setupUi(self, MainWindow): def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate - self.atten_auto.setToolTip(_translate("MainWindow", "Enable auto attenuator")) - self.atten_auto.setText(_translate("MainWindow", "Auto Att")) - self.t2_type.setToolTip(_translate("MainWindow", "Trace Type")) - self.version.setToolTip(_translate("MainWindow", "Firmware Version")) - self.version.setText(_translate("MainWindow", "Cnx")) - self.spur_box.setToolTip(_translate("MainWindow", "Spur reduction - impacts scanning speed")) - self.spur_box.setText(_translate("MainWindow", "Spurs")) self.trace4.setToolTip(_translate("MainWindow", "Enable Trace")) self.trace4.setText(_translate("MainWindow", "Trace 4")) + self.spur_box.setToolTip(_translate("MainWindow", "Spur reduction - impacts scanning speed")) + self.spur_box.setText(_translate("MainWindow", "Spurs")) + self.t2_type.setToolTip(_translate("MainWindow", "Trace Type")) self.t4_type.setToolTip(_translate("MainWindow", "Trace Type")) + self.t3_type.setToolTip(_translate("MainWindow", "Trace Type")) self.atten_box.setToolTip(_translate("MainWindow", "Set Attenuation")) self.atten_box.setSuffix(_translate("MainWindow", "dB")) - self.mixerMode.setText(_translate("MainWindow", "LNB")) - self.lna_box.setToolTip(_translate("MainWindow", "Enable Low Noise Amplifier")) - self.lna_box.setText(_translate("MainWindow", "LNA")) - self.trace1.setToolTip(_translate("MainWindow", "Enable Trace")) - self.trace1.setText(_translate("MainWindow", "Trace 1")) - self.trace3.setToolTip(_translate("MainWindow", "Enable Trace")) - self.trace3.setText(_translate("MainWindow", "Trace 3")) - self.battery.setToolTip(_translate("MainWindow", "Battery Voltage")) - self.battery.setText(_translate("MainWindow", "Battery")) - self.t3_type.setToolTip(_translate("MainWindow", "Trace Type")) + self.version.setToolTip(_translate("MainWindow", "Firmware Version")) + self.version.setText(_translate("MainWindow", "Cnx")) self.t1_type.setToolTip(_translate("MainWindow", "Trace Type")) + self.points_box.setToolTip(_translate("MainWindow", "Set the number of frequency Points to measure")) self.points_auto.setToolTip(_translate("MainWindow", "Auto set points to suit RBW")) self.points_auto.setText(_translate("MainWindow", "Auto Pts")) + self.mixerMode.setText(_translate("MainWindow", "LNB")) + self.atten_auto.setToolTip(_translate("MainWindow", "Enable auto attenuator")) + self.atten_auto.setText(_translate("MainWindow", "Auto Att")) + self.trace3.setToolTip(_translate("MainWindow", "Enable Trace")) + self.trace3.setText(_translate("MainWindow", "Trace 3")) self.trace2.setToolTip(_translate("MainWindow", "Enable Trace")) self.trace2.setText(_translate("MainWindow", "Trace 2")) - self.points_box.setToolTip(_translate("MainWindow", "Set Points")) + self.trace1.setToolTip(_translate("MainWindow", "Enable Trace")) + self.trace1.setText(_translate("MainWindow", "Trace 1")) + self.lna_box.setToolTip(_translate("MainWindow", "Enable Low Noise Amplifier")) + self.lna_box.setText(_translate("MainWindow", "LNA")) + self.battery.setToolTip(_translate("MainWindow", "Battery Voltage")) + self.battery.setText(_translate("MainWindow", "Battery")) self.rbw_box.setToolTip(_translate("MainWindow", "Set Resolution Bandwidth")) self.rbw_auto.setToolTip(_translate("MainWindow", "Enable Auto Resolution Bandwidth")) self.rbw_auto.setText(_translate("MainWindow", "Auto RBW")) + self.sampleRepeat.setToolTip(_translate("MainWindow", "Set the number of repeat measurements at each frequency Point")) self.mkr_start.setToolTip(_translate("MainWindow", "Set markers to start frequency")) self.mkr_start.setText(_translate("MainWindow", "...")) self.stop_freq.setToolTip(_translate("MainWindow", "Sweep Stop")) @@ -972,7 +987,7 @@ def retranslateUi(self, MainWindow): self.mkr_centre.setToolTip(_translate("MainWindow", "Set markers in range")) self.mkr_centre.setText(_translate("MainWindow", "...")) self.avgSlider.setToolTip(_translate("MainWindow", "Averaging")) - self.mToBand.setToolTip(_translate("MainWindow", "Add Band preset from M1 and M2")) + self.mToBand.setToolTip(_translate("MainWindow", "Use M1 to add Marker, or M1 and M2 to add Band")) self.mToBand.setText(_translate("MainWindow", "+")) self.filterBox.setToolTip(_translate("MainWindow", "Filter preset list")) self.centre_freq.setToolTip(_translate("MainWindow", "Sweep Centre")) @@ -1022,11 +1037,14 @@ def retranslateUi(self, MainWindow): self.grid.setText(_translate("MainWindow", "Show Grid")) self.menu_Help.setTitle(_translate("MainWindow", "&Help")) self.menuSettings.setTitle(_translate("MainWindow", "&Settings")) + self.menuFile.setTitle(_translate("MainWindow", "File")) self.actionsetting.setText(_translate("MainWindow", "setting")) self.actionlevel.setText(_translate("MainWindow", "level")) self.actionAbout_QtTinySA.setText(_translate("MainWindow", "About QtTinySA")) self.actionPreferences.setText(_translate("MainWindow", "Preferences")) self.actionSweeps.setText(_translate("MainWindow", "Sweeps")) self.actionbands.setText(_translate("MainWindow", "Bands and Markers")) + self.actionBrowse_TinySA.setText(_translate("MainWindow", "Browse TinySA")) + self.actionQuit.setText(_translate("MainWindow", "Quit")) from pyqtgraph import PlotWidget from pyqtgraph.opengl import GLViewWidget diff --git a/QtTinySpectrum.ui b/QtTinySpectrum.ui index b7814d7..b46da7d 100644 --- a/QtTinySpectrum.ui +++ b/QtTinySpectrum.ui @@ -7,8 +7,8 @@ 0 0 - 1000 - 603 + 1024 + 614 @@ -43,45 +43,45 @@ - - + + 7 + 75 + true - - Qt::Vertical + + Enable Trace - - - 17 - 13 - + + Trace 4 - + - - + + 7 - 75 - true - Enable auto attenuator + Spur reduction - impacts scanning speed - Auto Att + Spurs true + + true + - + @@ -99,10 +99,10 @@ - - + + - + 0 0 @@ -113,53 +113,81 @@ - Firmware Version + Trace Type - - Cnx + + + + + + + 0 + 0 + + + + + 7 + + + + Trace Type - - + + + + false + 7 - Spur reduction - impacts scanning speed + Set Attenuation - - Spurs + + false - - true + + dB - - true + + 0 + + + 31 + + + 0 - - + + + + + 0 + 0 + + 7 - 75 - true - Enable Trace + Firmware Version - Trace 4 + Cnx - - + + 0 @@ -174,39 +202,65 @@ Trace Type + + 0 + - - - - false - + + 7 - Set Attenuation - - - false + Set the number of frequency Points to measure - dB + - 0 + 1 - 31 + 30000 + + + 100 + + + QAbstractSpinBox::AdaptiveDecimalStepType - 0 + 400 - + + + + true + + + + 7 + 75 + true + + + + Auto set points to suit RBW + + + Auto Pts + + + true + + + + @@ -251,25 +305,8 @@ - - - - - 7 - 50 - false - - - - Enable Low Noise Amplifier - - - LNA - - - - - + + 7 @@ -278,20 +315,17 @@ - Enable Trace + Enable auto attenuator - Trace 1 + Auto Att true - - false - - + @@ -308,141 +342,104 @@ - - - - - 0 - 0 - - + + 7 + 75 + true - Battery Voltage + Enable Trace - Battery + Trace 2 + + + false - - - - - 0 - 0 - - + + 7 + 75 + true - Trace Type - - - - - - - - 0 - 0 - + Enable Trace - - - 7 - + + Trace 1 - - Trace Type + + true - - 0 + + false - - - - true - + + 7 - 75 - true + 50 + false - Auto set points to suit RBW + Enable Low Noise Amplifier - Auto Pts - - - true + LNA - - rbwpointsgroup - - - + + + + + 0 + 0 + + 7 - 75 - true - Enable Trace + Battery Voltage - Trace 2 - - - false + Battery - - + + 7 - - Set Points - - - - - - 1 - - - 30000 - - - 100 - - - QAbstractSpinBox::AdaptiveDecimalStepType + + Qt::Vertical - - 400 + + + 17 + 13 + - + @@ -468,6 +465,13 @@ + + + + Qt::Horizontal + + + @@ -483,15 +487,26 @@ Auto RBW - - rbwpointsgroup - - - - - Qt::Horizontal + + + + + 7 + + + + Set the number of repeat measurements at each frequency Point + + + 1 + + + 10000 + + + QAbstractSpinBox::AdaptiveDecimalStepType @@ -715,7 +730,7 @@ 101 - 10 + 1 Qt::Horizontal @@ -742,7 +757,7 @@ - Add Band preset from M1 and M2 + Use M1 to add Marker, or M1 and M2 to add Band + @@ -1930,7 +1945,7 @@ 0 0 - 1000 + 1024 23 @@ -1946,6 +1961,14 @@ + + + File + + + + + @@ -1979,6 +2002,16 @@ Bands and Markers + + + Browse TinySA + + + + + Quit + + @@ -2012,7 +2045,4 @@ true - - - diff --git a/Screenshots/3D_20240418_103532.png b/Screenshots/3D_20240418_103532.png new file mode 100644 index 0000000..ae646e7 Binary files /dev/null and b/Screenshots/3D_20240418_103532.png differ diff --git a/Screenshots/3D_20240418_103613.png b/Screenshots/3D_20240418_103613.png new file mode 100644 index 0000000..a745214 Binary files /dev/null and b/Screenshots/3D_20240418_103613.png differ diff --git a/Screenshots/tv_channels.png b/Screenshots/tv_channels.png new file mode 100644 index 0000000..42efa5d Binary files /dev/null and b/Screenshots/tv_channels.png differ diff --git a/filebrowse.ui b/filebrowse.ui new file mode 100644 index 0000000..d041750 --- /dev/null +++ b/filebrowse.ui @@ -0,0 +1,84 @@ + + + Filebrowse + + + + 0 + 0 + 818 + 405 + + + + File List + + + + + 10 + 20 + 301 + 311 + + + + + + + 10 + 350 + 80 + 26 + + + + Save + + + + + + 320 + 20 + 481 + 321 + + + + + + + false + + + + + + 100 + 350 + 211 + 26 + + + + 0 + + + + + + 10 + 380 + 791 + 20 + + + + + + + + + + diff --git a/preferences.ui b/preferences.ui index 128a4c9..fd4cb26 100644 --- a/preferences.ui +++ b/preferences.ui @@ -6,7 +6,7 @@ 0 0 - 800 + 817 600 @@ -156,46 +156,13 @@ 440 10 - 356 + 361 571 - - - - 0dBm - - - false - - - - - - - Points / Resolution Bandwidth - - - - - - - QAbstractSpinBox::NoButtons - - - 25 - - - 450 - - - 450 - - - - - + + 75 @@ -203,100 +170,49 @@ - External Mixer / LNB + Miscellaneous - - - - 0 - 0 - - - - - 83 - 16777215 - - - - - 9 - - - - dBm - - - - + - -120 + 2 - -20 + 10 - -90 + 3 - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - MHz - - - 6 - - - 0.000000000000000 - - - 100000.000000000000000 - - - 0.000000000000000 + + + + Sync to PC - - - - 2 - - - 10 + + + + +6dBm - - 3 + + false - - + + - Auto maximum points + Points / Resolution Bandwidth - - + + 75 @@ -304,7 +220,30 @@ - Scan Points Settings + External Mixer / LNB + + + + + + + Absolute maximum + + + + + + + QAbstractSpinBox::NoButtons + + + 25 + + + 100000 + + + 30000 @@ -321,6 +260,13 @@ + + + + Max with auto attenuator + + + @@ -331,10 +277,24 @@ - - + + - Absolute maximum + Peak marker detection threshold + + + + + + + Auto maximum points + + + + + + + True @@ -345,62 +305,75 @@ - + LO above displayed Freq - - + + - +6dBm + 0dBm false - - + + - + Auto minimum points - - + + - Detection Threshold + LO Frequency - - - - Max with auto attenuator + + + + + 0 + 0 + - - - - - - QAbstractSpinBox::NoButtons + + + 83 + 16777215 + + + + + 9 + + + + dBm + + + - 25 + -120 - 100000 + -20 - 30000 + -90 - + 75 @@ -408,28 +381,62 @@ - Peak Marker Settings + Scan Points Settings - - - - Auto minimum points + + + + QAbstractSpinBox::NoButtons + + + 25 + + + 450 + + + 450 - - - - LO Frequency + + + + MHz + + + 6 + + + 0.000000000000000 + + + 100000.000000000000000 + + + 0.000000000000000 - - + + + + Qt::Vertical + + + + 20 + 40 + + + + + + - True + Date and Time