From 98a9300227fb7d0904190562a2bfa2721c11b9d1 Mon Sep 17 00:00:00 2001 From: Clive Tombs Date: Sun, 28 Apr 2024 10:56:45 +0100 Subject: [PATCH 01/18] Add files via upload --- QtTinySA.py | 27 ++++++++++++++++++--------- QtTinySpectrum.py | 6 +++--- QtTinySpectrum.ui | 10 +++++----- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/QtTinySA.py b/QtTinySA.py index ff50d3c..3bd626a 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -25,7 +25,7 @@ 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 import pyqtgraph import QtTinySpectrum # the GUI @@ -849,7 +849,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)) @@ -908,18 +908,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())) + title = "New Frequency Band" + message = "Input the name of your new band." + bandName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal,"") 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) + stopF=f'{S2.vline.value():.6f}', value=1, colour= colourID('green')) # colourID(value) + else: # If only Marker 1 is enabled then this creates a spot Frequency marker + title = "New Spot Frequency" + message = "Input the Name for your Spot Frequency" + spotName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal, "") + bands.insertData(name=spotName, preset="12", startF=f'{S1.vline.value():.6f}', + stopF='', value=1, colour= colourID('orange')) # preset 12 is Marker (spot frequency). def attenuate_changed(): @@ -1123,7 +1132,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.4') window = QtWidgets.QMainWindow() ui = QtTinySpectrum.Ui_MainWindow() ui.setupUi(window) diff --git a/QtTinySpectrum.py b/QtTinySpectrum.py index 89220d3..3e27686 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, 617) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -901,7 +901,7 @@ 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, 30)) self.menuBar.setObjectName("menuBar") self.menu_Help = QtWidgets.QMenu(self.menuBar) self.menu_Help.setObjectName("menu_Help") @@ -972,7 +972,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")) diff --git a/QtTinySpectrum.ui b/QtTinySpectrum.ui index b7814d7..5b02a75 100644 --- a/QtTinySpectrum.ui +++ b/QtTinySpectrum.ui @@ -7,8 +7,8 @@ 0 0 - 1000 - 603 + 1024 + 617 @@ -742,7 +742,7 @@ - Add Band preset from M1 and M2 + Use M1 to add Marker, or M1 and M2 to add Band + @@ -1930,8 +1930,8 @@ 0 0 - 1000 - 23 + 1024 + 30 From 25c6772e6591f5261796cd5edc9b5f602a28e889 Mon Sep 17 00:00:00 2001 From: G4IXT Date: Sun, 28 Apr 2024 20:44:24 +0100 Subject: [PATCH 02/18] v0.10.4 removes need for a preset 'type' of 'band' in config database --- QtTinySA.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/QtTinySA.py b/QtTinySA.py index 3bd626a..0b51cdc 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -893,7 +893,7 @@ 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': + if bands.tm.record(index).value('stopF') != '': startF = bands.tm.record(index).value('StartF') stopF = bands.tm.record(index).value('StopF') ui.start_freq.setValue(startF) @@ -1058,11 +1058,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) From 2522f6717ad68327c1e9b01931e2a2fc70c8243b Mon Sep 17 00:00:00 2001 From: G4IXT Date: Mon, 29 Apr 2024 16:42:00 +0100 Subject: [PATCH 03/18] v0.10.4 wip fixes for band combo-box filtering --- QtTinySA.py | 56 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/QtTinySA.py b/QtTinySA.py index 0b51cdc..8bc2876 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -102,7 +102,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() @@ -160,8 +161,10 @@ 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.activated.connect(band_changed) + ui.band_box.currentIndexChanged.connect(band_changed) ui.band_box.activated.connect(band_changed) - + logging.info(f'groups: {os.getgroups()}') self.fifoTimer.start(500) # calls self.usbSend() every 500mS to execute serial commands whilst not scanning def restoreSettings(self): @@ -788,6 +791,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) @@ -823,7 +827,7 @@ def insertData(self, **data): for key, value in data.items(): 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 +836,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: @@ -891,16 +898,31 @@ def mapWidget(self, modelName): # maps the widget combo-box fields to the datab # respond to GUI signals +# def band_changed(): +# index = ui.band_box.currentIndex() +# if bands.tm.record(index).value('stopF') != '': +# startF = bands.tm.record(index).value('StartF') +# stopF = bands.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') +# ui.centre_freq.setValue(centreF) +# ui.span_freq.setValue(1) +# tinySA.freq_changed(True) # centre mode +# freqMarkers() + def band_changed(): index = ui.band_box.currentIndex() - if bands.tm.record(index).value('stopF') != '': - startF = bands.tm.record(index).value('StartF') - stopF = bands.tm.record(index).value('StopF') + if presetmarker.tm.record(index).value('stopF') != '': + startF = presetmarker.tm.record(index).value('StartF') + stopF = presetmarker.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 = presetmarker.tm.record(index).value('StartF') ui.centre_freq.setValue(centreF) ui.span_freq.setValue(1) tinySA.freq_changed(True) # centre mode @@ -994,7 +1016,7 @@ def setPreferences(): checkboxes.dwm.submit() bands.tm.submitAll() S4.hline.setValue(preferences.peakThreshold.value()) - bands.filterType(False, ui.filterBox.currentText()) + presetmarker.filterType(False, ui.filterBox.currentText()) if ui.presetMarker.isChecked(): freqMarkers() isMixerMode() @@ -1049,7 +1071,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()): @@ -1232,7 +1254,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) @@ -1275,7 +1297,7 @@ 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: presetmarker.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 @@ -1314,12 +1336,18 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. presetmarker.createTableModel() presetmarker.tm.setRelation(6, QSqlRelation('SVGColour', 'ID', 'colour')) presetmarker.tm.setRelation(2, QSqlRelation('freqtype', 'ID', 'type')) +presetmarker.tm.setSort(3, QtCore.Qt.AscendingOrder) presetmarker.tm.select() # populate the band presets combo box -ui.band_box.setModel(bands.tm) +# ui.band_box.setModel(bands.tm) +# ui.band_box.setModelColumn(1) +# bands.tm.select() # initially select the data in the model + +ui.band_box.setModel(presetmarker.tm) ui.band_box.setModelColumn(1) -bands.tm.select() # initially select the data in the model +# bands.tm.select() # initially select the data in the model + # populate the preferences and main GUI filter combo boxes preferences.filterBox.setModel(bandstype.tm) From e0af12d089c713426026088ede56f6c8a4bd737b Mon Sep 17 00:00:00 2001 From: G4IXT Date: Tue, 30 Apr 2024 15:16:08 +0100 Subject: [PATCH 04/18] v0.10.4 wip wip --- QtTinySA.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/QtTinySA.py b/QtTinySA.py index 8bc2876..8389437 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -164,7 +164,6 @@ def initialise(self): # ui.band_box.activated.connect(band_changed) ui.band_box.currentIndexChanged.connect(band_changed) ui.band_box.activated.connect(band_changed) - logging.info(f'groups: {os.getgroups()}') self.fifoTimer.start(500) # calls self.usbSend() every 500mS to execute serial commands whilst not scanning def restoreSettings(self): @@ -1184,15 +1183,16 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. colours = modelView('SVGColour') maps = modelView('mapping') -presetmarker = modelView(('frequencies')) +presetmarker = 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) From f75e4fe43b443db6da9fd380893ca9a2efc0bf16 Mon Sep 17 00:00:00 2001 From: Clive Tombs Date: Tue, 30 Apr 2024 16:16:16 +0100 Subject: [PATCH 05/18] Update QtTinySA.py Marker frequency precision now variable depending on sweep width --- QtTinySA.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/QtTinySA.py b/QtTinySA.py index 8389437..3eaec0f 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -23,6 +23,7 @@ import shutil import platformdirs import csv +import math from platform import system from PyQt5 import QtWidgets, QtCore from PyQt5.QtWidgets import QMessageBox, QDataWidgetMapper, QFileDialog, QInputDialog, QLineEdit @@ -573,6 +574,12 @@ def lna(self): ui.atten_auto.setChecked(True) self.fifo.put(command) + def FPrecision(self): # sets the marker and band precision based on scan width(meaningless acuracy) + ScanWidth = (ui.stop_freq.value()*1e6 - ui.start_freq.value()*1e6) + res = int(math.log10(ScanWidth)) + self.dp = (8 - res) # number of decicimal places required + if self.dp < 0: + self.dp = 0 class display: def __init__(self, name, pen): @@ -677,14 +684,17 @@ def updateMarker(self, frequencies, readings, fPeaks): # called by updateGUI() self.vline.label.setText(f'M{self.vline.name()} {self.vline.value():.3f}MHz') else: # marker is in scan range + a = analyser() # get the required precision of the frequency + a.FPrecision() + dp = a.dp fIndex = np.argmin(np.abs(frequencies - (markerF * 1e6))) # find closest value in freq array dBm = readings[fIndex] 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:.{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():.{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(): @@ -933,6 +943,9 @@ def addBandPressed(): message = 'Please enable Marker 1' popUp(message, QMessageBox.Ok, QMessageBox.Information) return + a = analyser() # get the required precision of the frequency + a.FPrecision() + dp = a.dp 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' @@ -942,13 +955,13 @@ def addBandPressed(): title = "New Frequency Band" message = "Input the name of your new band." bandName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal,"") - bands.insertData(name=bandName, preset=ID, startF=f'{S1.vline.value():.6f}', - stopF=f'{S2.vline.value():.6f}', value=1, colour= colourID('green')) # colourID(value) + bands.insertData(name=bandName, preset=ID, startF=f'{S1.vline.value():.{dp}f}', + stopF=f'{S2.vline.value():.{dp}f}', value=1, colour= colourID('green')) # colourID(value) else: # If only Marker 1 is enabled then this creates a spot Frequency marker title = "New Spot Frequency" message = "Input the Name for your Spot Frequency" spotName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal, "") - bands.insertData(name=spotName, preset="12", startF=f'{S1.vline.value():.6f}', + bands.insertData(name=spotName, preset="12", startF=f'{S1.vline.value():.{dp}f}', stopF='', value=1, colour= colourID('orange')) # preset 12 is Marker (spot frequency). From 9f2de7487fefd5bb5a5106a1f1fb3e578886d9fb Mon Sep 17 00:00:00 2001 From: G4IXT Date: Tue, 30 Apr 2024 22:27:06 +0100 Subject: [PATCH 06/18] v0.10.4 Fixed band changing when preferences dialogue closed --- QtTinySA.py | 67 +++++++++++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 40 deletions(-) diff --git a/QtTinySA.py b/QtTinySA.py index 8389437..6bc2845 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -161,7 +161,6 @@ 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.activated.connect(band_changed) 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 @@ -689,7 +688,7 @@ def updateMarker(self, frequencies, readings, fPeaks): # called by updateGUI() 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)) @@ -751,7 +750,7 @@ def _getPersonalisedPath(self): 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): + # Look for one in the current working folder and in the folder where the python file is stored (or linked): # 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)): @@ -897,31 +896,16 @@ def mapWidget(self, modelName): # maps the widget combo-box fields to the datab # respond to GUI signals -# def band_changed(): -# index = ui.band_box.currentIndex() -# if bands.tm.record(index).value('stopF') != '': -# startF = bands.tm.record(index).value('StartF') -# stopF = bands.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') -# ui.centre_freq.setValue(centreF) -# ui.span_freq.setValue(1) -# tinySA.freq_changed(True) # centre mode -# freqMarkers() - def band_changed(): index = ui.band_box.currentIndex() - if presetmarker.tm.record(index).value('stopF') != '': - startF = presetmarker.tm.record(index).value('StartF') - stopF = presetmarker.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 = presetmarker.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 @@ -933,7 +917,7 @@ def addBandPressed(): 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 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) @@ -941,15 +925,15 @@ def addBandPressed(): ID = presetID(str(ui.filterBox.currentText())) title = "New Frequency Band" message = "Input the name of your new band." - bandName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal,"") + bandName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal, "") bands.insertData(name=bandName, preset=ID, startF=f'{S1.vline.value():.6f}', - stopF=f'{S2.vline.value():.6f}', value=1, colour= colourID('green')) # colourID(value) + stopF=f'{S2.vline.value():.6f}', value=1, colour=colourID('green')) # colourID(value) else: # If only Marker 1 is enabled then this creates a spot Frequency marker title = "New Spot Frequency" message = "Input the Name for your Spot Frequency" spotName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal, "") bands.insertData(name=spotName, preset="12", startF=f'{S1.vline.value():.6f}', - stopF='', value=1, colour= colourID('orange')) # preset 12 is Marker (spot frequency). + stopF='', value=1, colour=colourID('orange')) # preset 12 is Marker (spot frequency). def attenuate_changed(): @@ -1015,7 +999,7 @@ def setPreferences(): checkboxes.dwm.submit() bands.tm.submitAll() S4.hline.setValue(preferences.peakThreshold.value()) - presetmarker.filterType(False, ui.filterBox.currentText()) + bandselect.filterType(False, ui.filterBox.currentText()) if ui.presetMarker.isChecked(): freqMarkers() isMixerMode() @@ -1171,7 +1155,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') @@ -1182,8 +1165,9 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. bandstype = modelView('freqtype') colours = modelView('SVGColour') maps = modelView('mapping') - +bands = modelView('frequencies') presetmarker = modelView('frequencies') +bandselect = modelView('frequencies') ############################################################################### # GUI settings @@ -1297,7 +1281,7 @@ 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: presetmarker.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 @@ -1310,9 +1294,12 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. 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') @@ -1321,35 +1308,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.setSort(3, QtCore.Qt.AscendingOrder) presetmarker.tm.select() -# populate the band presets combo box -# ui.band_box.setModel(bands.tm) -# ui.band_box.setModelColumn(1) -# bands.tm.select() # initially select the data in the model - -ui.band_box.setModel(presetmarker.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', 'type')) +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) From 668e00d84a1d48c2480df3d9798de27c831f4954 Mon Sep 17 00:00:00 2001 From: Clive Tombs Date: Wed, 1 May 2024 22:35:06 +0100 Subject: [PATCH 07/18] Update QtTinySA.py no math import. use of existing instance of analyser --- QtTinySA.py | 82 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 29 deletions(-) diff --git a/QtTinySA.py b/QtTinySA.py index 6bc2845..e82635f 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -161,6 +161,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.activated.connect(band_changed) 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 @@ -572,6 +573,15 @@ def lna(self): ui.atten_auto.setChecked(True) self.fifo.put(command) + def FPrecision(self): + # sets the marker and band precision based on scan width to remove meaningless accuracy + ScanWidth = np.ptp(tinySA.set_frequencies()) + res = int(np.log10(ScanWidth)) + self.dp = (8 - res) # dp is number of decicimal places suggested + if self.dp < 0: + self.dp = 0 + return self.dp + class display: def __init__(self, name, pen): @@ -676,14 +686,15 @@ def updateMarker(self, frequencies, readings, fPeaks): # called by updateGUI() self.vline.label.setText(f'M{self.vline.name()} {self.vline.value():.3f}MHz') else: # marker is in scan range + dp = tinySA.FPrecision() # get the required precision of the frequency fIndex = np.argmin(np.abs(frequencies - (markerF * 1e6))) # find closest value in freq array dBm = readings[fIndex] 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:.{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():.{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(): @@ -750,7 +761,7 @@ def _getPersonalisedPath(self): 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): + # 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)): @@ -896,16 +907,31 @@ def mapWidget(self, modelName): # maps the widget combo-box fields to the datab # respond to GUI signals +# def band_changed(): +# index = ui.band_box.currentIndex() +# if bands.tm.record(index).value('stopF') != '': +# startF = bands.tm.record(index).value('StartF') +# stopF = bands.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') +# ui.centre_freq.setValue(centreF) +# ui.span_freq.setValue(1) +# tinySA.freq_changed(True) # centre mode +# freqMarkers() + def band_changed(): index = ui.band_box.currentIndex() - if bandselect.tm.record(index).value('stopF') != '': - startF = bandselect.tm.record(index).value('StartF') - stopF = bandselect.tm.record(index).value('StopF') + if presetmarker.tm.record(index).value('stopF') != '': + startF = presetmarker.tm.record(index).value('StartF') + stopF = presetmarker.tm.record(index).value('StopF') ui.start_freq.setValue(startF) ui.stop_freq.setValue(stopF) tinySA.freq_changed(False) # start/stop mode else: - centreF = bandselect.tm.record(index).value('StartF') + centreF = presetmarker.tm.record(index).value('StartF') ui.centre_freq.setValue(centreF) ui.span_freq.setValue(1) tinySA.freq_changed(True) # centre mode @@ -917,6 +943,7 @@ def addBandPressed(): message = 'Please enable Marker 1' popUp(message, QMessageBox.Ok, QMessageBox.Information) return + dp = tinySA.FPrecision() # get the required precision of the frequency 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' @@ -924,15 +951,15 @@ def addBandPressed(): return ID = presetID(str(ui.filterBox.currentText())) title = "New Frequency Band" - message = "Input the name of your new band." + message = "Input the name of the new band." bandName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal, "") - bands.insertData(name=bandName, preset=ID, startF=f'{S1.vline.value():.6f}', - stopF=f'{S2.vline.value():.6f}', value=1, colour=colourID('green')) # colourID(value) + bands.insertData(name=bandName, preset=ID, startF=f'{S1.vline.value():.{dp}f}', + stopF=f'{S2.vline.value():.{dp}f}', value=1, colour=colourID('green')) # colourID(value) else: # If only Marker 1 is enabled then this creates a spot Frequency marker title = "New Spot Frequency" - message = "Input the Name for your Spot Frequency" + message = "Input the name for your Spot Frequency" spotName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal, "") - bands.insertData(name=spotName, preset="12", startF=f'{S1.vline.value():.6f}', + bands.insertData(name=spotName, preset="12", startF=f'{S1.vline.value():.{dp}f}', stopF='', value=1, colour=colourID('orange')) # preset 12 is Marker (spot frequency). @@ -999,7 +1026,7 @@ def setPreferences(): checkboxes.dwm.submit() bands.tm.submitAll() S4.hline.setValue(preferences.peakThreshold.value()) - bandselect.filterType(False, ui.filterBox.currentText()) + presetmarker.filterType(False, ui.filterBox.currentText()) if ui.presetMarker.isChecked(): freqMarkers() isMixerMode() @@ -1155,6 +1182,7 @@ 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') @@ -1165,9 +1193,8 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. bandstype = modelView('freqtype') colours = modelView('SVGColour') maps = modelView('mapping') -bands = modelView('frequencies') + presetmarker = modelView('frequencies') -bandselect = modelView('frequencies') ############################################################################### # GUI settings @@ -1281,7 +1308,7 @@ 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: bandselect.filterType(False, ui.filterBox.currentText())) +ui.filterBox.currentTextChanged.connect(lambda: presetmarker.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 @@ -1294,12 +1321,9 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. 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') @@ -1308,35 +1332,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.setSort(3, QtCore.Qt.AscendingOrder) presetmarker.tm.select() -# 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', 'type')) -bandselect.tm.setSort(3, QtCore.Qt.AscendingOrder) -ui.band_box.setModel(bandselect.tm) +# populate the band presets combo box +# ui.band_box.setModel(bands.tm) +# ui.band_box.setModelColumn(1) +# bands.tm.select() # initially select the data in the model + +ui.band_box.setModel(presetmarker.tm) ui.band_box.setModelColumn(1) -bandselect.tm.select() +# bands.tm.select() # initially select the data in the model + -# populate the preferences dialogue and ui filter combo boxes +# populate the preferences and main GUI filter combo boxes preferences.filterBox.setModel(bandstype.tm) preferences.filterBox.setModelColumn(1) ui.filterBox.setModel(bandstype.tm) From 3ee4e5811ecde0d549894b908e53ccbc1a9f1340 Mon Sep 17 00:00:00 2001 From: G4IXT Date: Sun, 5 May 2024 22:38:23 +0100 Subject: [PATCH 08/18] Revert "Update QtTinySA.py" This reverts commit 668e00d84a1d48c2480df3d9798de27c831f4954. --- QtTinySA.py | 82 +++++++++++++++++++---------------------------------- 1 file changed, 29 insertions(+), 53 deletions(-) diff --git a/QtTinySA.py b/QtTinySA.py index e82635f..6bc2845 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -161,7 +161,6 @@ 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.activated.connect(band_changed) 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 @@ -573,15 +572,6 @@ def lna(self): ui.atten_auto.setChecked(True) self.fifo.put(command) - def FPrecision(self): - # sets the marker and band precision based on scan width to remove meaningless accuracy - ScanWidth = np.ptp(tinySA.set_frequencies()) - res = int(np.log10(ScanWidth)) - self.dp = (8 - res) # dp is number of decicimal places suggested - if self.dp < 0: - self.dp = 0 - return self.dp - class display: def __init__(self, name, pen): @@ -686,15 +676,14 @@ def updateMarker(self, frequencies, readings, fPeaks): # called by updateGUI() self.vline.label.setText(f'M{self.vline.name()} {self.vline.value():.3f}MHz') else: # marker is in scan range - dp = tinySA.FPrecision() # get the required precision of the frequency fIndex = np.argmin(np.abs(frequencies - (markerF * 1e6))) # find closest value in freq array dBm = readings[fIndex] 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:.{dp}f}MHz {dBm:.1f}dBm') + self.vline.label.setText(f'M{self.vline.name()} {chr(916)}{self.deltaF:.3f}MHz {dBm:.1f}dBm') else: - self.vline.label.setText(f'M{self.vline.name()} {self.vline.value():.{dp}f}MHz {dBm:.1f}dBm') + self.vline.label.setText(f'M{self.vline.name()} {self.vline.value():.3f}MHz {dBm:.1f}dBm') def addFreqMarker(self, freq, colour, name, position): # adds simple freq marker without full marker capability if ui.presetLabel.isChecked(): @@ -761,7 +750,7 @@ def _getPersonalisedPath(self): 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): + # Look for one in the current working folder and in the folder where the python file is stored (or linked): # 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)): @@ -907,31 +896,16 @@ def mapWidget(self, modelName): # maps the widget combo-box fields to the datab # respond to GUI signals -# def band_changed(): -# index = ui.band_box.currentIndex() -# if bands.tm.record(index).value('stopF') != '': -# startF = bands.tm.record(index).value('StartF') -# stopF = bands.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') -# ui.centre_freq.setValue(centreF) -# ui.span_freq.setValue(1) -# tinySA.freq_changed(True) # centre mode -# freqMarkers() - def band_changed(): index = ui.band_box.currentIndex() - if presetmarker.tm.record(index).value('stopF') != '': - startF = presetmarker.tm.record(index).value('StartF') - stopF = presetmarker.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 = presetmarker.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 @@ -943,7 +917,6 @@ def addBandPressed(): message = 'Please enable Marker 1' popUp(message, QMessageBox.Ok, QMessageBox.Information) return - dp = tinySA.FPrecision() # get the required precision of the frequency 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' @@ -951,15 +924,15 @@ def addBandPressed(): return ID = presetID(str(ui.filterBox.currentText())) title = "New Frequency Band" - message = "Input the name of the new band." + message = "Input the name of your new band." bandName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal, "") - bands.insertData(name=bandName, preset=ID, startF=f'{S1.vline.value():.{dp}f}', - stopF=f'{S2.vline.value():.{dp}f}', value=1, colour=colourID('green')) # colourID(value) + bands.insertData(name=bandName, preset=ID, startF=f'{S1.vline.value():.6f}', + stopF=f'{S2.vline.value():.6f}', value=1, colour=colourID('green')) # colourID(value) else: # If only Marker 1 is enabled then this creates a spot Frequency marker title = "New Spot Frequency" - message = "Input the name for your Spot Frequency" + message = "Input the Name for your Spot Frequency" spotName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal, "") - bands.insertData(name=spotName, preset="12", startF=f'{S1.vline.value():.{dp}f}', + bands.insertData(name=spotName, preset="12", startF=f'{S1.vline.value():.6f}', stopF='', value=1, colour=colourID('orange')) # preset 12 is Marker (spot frequency). @@ -1026,7 +999,7 @@ def setPreferences(): checkboxes.dwm.submit() bands.tm.submitAll() S4.hline.setValue(preferences.peakThreshold.value()) - presetmarker.filterType(False, ui.filterBox.currentText()) + bandselect.filterType(False, ui.filterBox.currentText()) if ui.presetMarker.isChecked(): freqMarkers() isMixerMode() @@ -1182,7 +1155,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') @@ -1193,8 +1165,9 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. bandstype = modelView('freqtype') colours = modelView('SVGColour') maps = modelView('mapping') - +bands = modelView('frequencies') presetmarker = modelView('frequencies') +bandselect = modelView('frequencies') ############################################################################### # GUI settings @@ -1308,7 +1281,7 @@ 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: presetmarker.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 @@ -1321,9 +1294,12 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. 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') @@ -1332,35 +1308,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.setSort(3, QtCore.Qt.AscendingOrder) presetmarker.tm.select() -# populate the band presets combo box -# ui.band_box.setModel(bands.tm) -# ui.band_box.setModelColumn(1) -# bands.tm.select() # initially select the data in the model - -ui.band_box.setModel(presetmarker.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', 'type')) +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) From 5f40ee334774876841f5e3d0f8039c47a349f799 Mon Sep 17 00:00:00 2001 From: G4IXT Date: Fri, 17 May 2024 22:21:26 +0100 Subject: [PATCH 09/18] v0.10.5 wip wip --- QtTSApreferences.py | 206 ++++++++++++------------- QtTSAprefs.db | Bin 131072 -> 131072 bytes QtTinySA.py | 61 ++++++-- QtTinySpectrum.py | 278 +++++++++++++++++----------------- QtTinySpectrum.ui | 359 +++++++++++++++++++++++--------------------- preferences.ui | 303 +++++++++++++++++++------------------ 6 files changed, 643 insertions(+), 564 deletions(-) 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 3e02207fb2140ad8e9fb536c93ab8eec3c6cff60..e1db8bd6dd55a76ff6db8de2841b55a9b3597cb0 100644 GIT binary patch delta 1462 zcmaJ>Z){Ul6u+l?>$~^8ci-*)z*Z)#s}&XJwsy=B!iN=B2)b^tmFRHBwA~vlbbr!z zg5kqjGe_z$fxx^8TRX@S)Gx9+W9K5DMKt1!e}b3=5~qTKe-iyLLA)I>7K6L*-TUr6 zzjMy--22YCu^5iU@R3#d&T@p1P4tzb=OAQy;%K?cETk3BKAmoVswroP(#d4go>-1U zy?9Lie49zVn2smZwGM|=lULRoHad*3(H%4*6_J;_gTBr#!{rR_HG=yBJqL`)Pf`)LNWV0}t}zm>j$wvr7rY66ts+>pvpmyVnNp zO;8_LQ3)}L26#nDfwjnLHJN1DT2N5ZZglwjLQ!;X*KhC+Oik2Q1-txR{;1Ix-cVE3 z*c0yZhjtkLeXcvxJ0ncMyLIEzA@AtE+`4g^82R&fXaUhfUjG4aXSe$$BkbQ}yl;bl zn#f42A|iQP&CkfauoJMFuPUlZnNP=05SyBhEO+C?OTTiC$z(}b5}8#8;m5)fEsA9G zb;M@$O?obEH(#ff+PoIcKI!-dkhFQ#mX06FE!O69hZpyahNM!tsH_ZzTevg=FG$TE zcLV=u5FX`+N1(Cawxy||!R_%BV0il0qduCeU+9 zsKD9$&Jyz9Q?S~sJT895;L-Rirs^_&mJ7ohCqOe*+cIKt(!^(kT`B8E8f!u94>rfX zV&~b1Y=WI)!>pf$*^8_t9j|7*O4g%&c;rQ=u3O5R8-Mk_=Wkxcy*oR$08Zq$YYgl%Pyu|oZpeog;IpVpIlOb_X; zx?5kT7tot@j($cn^dwEtFnylZ(QFW1Z{?bDQ8}k%l+((vGN441fU;AmQ!2MAYZOXulAp=9xd<*}AFX8j}1N;`|co6roUvYqa$0pfvwx8|BE!dNe_sdmcD^>Ta zhV>%*{S~sD3}ciBV&mX)YVbA8Dw$nrm^0T3#e2oOM4x?vWU(wdMKCG?84=jFx!UWI z>JH|2WLphCdI2^*EL8HASRug^S>$nPFbhBM8yBEhnni4OGSBiQv)iCZ++#p67B40X R115}+ySG@5^Ymq~{R^jwdGP=M delta 893 zcmY*XZAep57(Vaa)qC&edRsPiTj|osKcd^L{Mn++zNVEkRFJy+Fr(#ca~KG<8zDv? zxROq7nSv4ib7RngvL8Vp^+O91B9cWU6@5tk2>MZ-YhrrNd7pEi=e+O3xra9z6{Asc zz`VwGg%Dz8QNTh&2)8j-;mBobW&5X;Z)=05N3Tl{H5`j-%5;CO3Db2cC8bOMd7ZSF_zbl;a~Dc?)5H>I4yvkC_$Vv#sDKs1!(7Z#dK z0vje%zRBlrmm`60GTAgw#>rfAm&+3h%090g?zY=(2mJC0y9$o_VCbYA_=nCGa9zBk zk2w$L@9wW!rE`w*uYzNh{mxOnw+bTrmgl;Tcjo@%_pI={K^i%(WxqP6GiQV@I0z}_ zv2^X|Lq1d73-8uiv~5HX9uRykPKq&MUi(XUpnW2E)R|IRIbQg{e@-bMc%CJ;#FQqk z+MKZu!3f7Q(~g+puL`zP{&GIy^?NTL0v0rUH@z8zzsNGw9Gf$6$vfX(lZIzMq3k5 zPWKK&6`e?cgZ_wvjTYZxv@*`1ECDsN>n8h}R-sMR9>rk`y%cB3!#5esC!p$j9E$1G zFgQxs6Xand!SDDMU*c1Ih)Ep6%Xk($@d!4ilxn1n89BV&uF27xCf#-*-MWttoZ`z8d{M?NWc`y-(RAENq%JAyq{IS|mZ z`;0 0: command = self.fifo.get(block=True, timeout=None) - logging.debug(command) + logging.info(command) self.usb.write(command) self.usb.read_until(b'ch> ') # skip command echo and prompt @@ -254,6 +261,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): @@ -572,6 +580,25 @@ def lna(self): 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'.encode() + 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'.encode() + self.fifo.put(command) + + def fPrecision(self, frequencies): # sets the marker and band indicated frequency precision + fInc = frequencies[1] - frequencies[0] + self.dp = int(np.log10(frequencies[0] / fInc)) # number of decicimal places required + logging.info(f'fPrecision: fInc = {fInc} dp = {self.dp}') + if self.dp < 0: + self.dp = 0 + class display: def __init__(self, name, pen): @@ -662,6 +689,10 @@ def updateTrace(self, frequencies, readings): # called by sigProcess() for ever else: tinySA.vGrid.hide() if not tinySA.sweeping: # measurement thread is stopping + # test + # command = 'abort\r'.encode() + # tinySA.usb.write(command) + # test ui.scan_button.setText('Stopping ...') ui.scan_button.setStyleSheet('background-color: orange') ui.run3D.setText('Stopping ...') @@ -673,7 +704,8 @@ 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():.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 @@ -681,9 +713,11 @@ 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:.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():.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(): @@ -926,13 +960,13 @@ def addBandPressed(): title = "New Frequency Band" message = "Input the name of your new band." bandName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal, "") - bands.insertData(name=bandName, preset=ID, startF=f'{S1.vline.value():.6f}', - stopF=f'{S2.vline.value():.6f}', value=1, colour=colourID('green')) # colourID(value) + bands.insertData(name=bandName, preset=ID, startF=f'{S1.vline.value():.{tinySA.dp}f}', + stopF=f'{S2.vline.value():.{tinySA.dp}f}', value=1, colour=colourID('green')) # colourID(value) else: # If only Marker 1 is enabled then this creates a spot Frequency marker title = "New Spot Frequency" message = "Input the Name for your Spot Frequency" spotName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal, "") - bands.insertData(name=spotName, preset="12", startF=f'{S1.vline.value():.6f}', + bands.insertData(name=spotName, preset="12", startF=f'{S1.vline.value():.{tinySA.dp}f}', stopF='', value=1, colour=colourID('orange')) # preset 12 is Marker (spot frequency). @@ -1018,6 +1052,7 @@ def about(): .format(app.applicationVersion(), config.dbpath)) popUp(message, QMessageBox.Ok, QMessageBox.Information) + ############################################################################## # other methods @@ -1137,7 +1172,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.4') +app.setApplicationVersion(' v0.10.5a') window = QtWidgets.QMainWindow() ui = QtTinySpectrum.Ui_MainWindow() ui.setupUi(window) @@ -1254,6 +1289,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)) diff --git a/QtTinySpectrum.py b/QtTinySpectrum.py index 3e27686..ddc3e70 100644 --- a/QtTinySpectrum.py +++ b/QtTinySpectrum.py @@ -14,7 +14,7 @@ class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") - MainWindow.resize(1024, 617) + 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,54 @@ 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.rbwpointsgroup = QtWidgets.QButtonGroup(MainWindow) + self.rbwpointsgroup.setObjectName("rbwpointsgroup") + self.rbwpointsgroup.addButton(self.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 +165,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 +201,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 +220,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 +236,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) @@ -245,11 +250,15 @@ def setupUi(self, MainWindow): 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 +355,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,7 +910,7 @@ def setupUi(self, MainWindow): self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.menuBar = QtWidgets.QMenuBar(MainWindow) - self.menuBar.setGeometry(QtCore.QRect(0, 0, 1024, 30)) + 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") @@ -931,37 +940,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 Points")) 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")) 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")) diff --git a/QtTinySpectrum.ui b/QtTinySpectrum.ui index 5b02a75..e7817a4 100644 --- a/QtTinySpectrum.ui +++ b/QtTinySpectrum.ui @@ -8,7 +8,7 @@ 0 0 1024 - 617 + 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,68 @@ Trace Type + + 0 + - - - - false - + + 7 - Set Attenuation - - - false + Set Points - dB + - 0 + 1 - 31 + 30000 + + + 100 + + + QAbstractSpinBox::AdaptiveDecimalStepType - 0 + 400 - + + + + true + + + + 7 + 75 + true + + + + Auto set points to suit RBW + + + Auto Pts + + + true + + + rbwpointsgroup + + + + @@ -251,25 +308,8 @@ - - - - - 7 - 50 - false - - - - Enable Low Noise Amplifier - - - LNA - - - - - + + 7 @@ -278,20 +318,17 @@ - Enable Trace + Enable auto attenuator - Trace 1 + Auto Att true - - false - - + @@ -308,141 +345,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 +468,13 @@ + + + + Qt::Horizontal + + + @@ -488,10 +495,24 @@ - - - - Qt::Horizontal + + + + + 7 + + + + Set the number of repeat measurements at each frequency + + + 1 + + + 10000 + + + QAbstractSpinBox::AdaptiveDecimalStepType @@ -715,7 +736,7 @@ 101 - 10 + 1 Qt::Horizontal @@ -1931,7 +1952,7 @@ 0 0 1024 - 30 + 23 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 From 373ce17b3c451cda368332042a83e952d25d57f2 Mon Sep 17 00:00:00 2001 From: G4IXT Date: Sun, 19 May 2024 21:54:01 +0100 Subject: [PATCH 10/18] v0.10.5b wip probably fixed band/marker add bug fixed auto rbw / auto points exclusivity removed fPrecision on marker /band creation updated some comments still coding for 'abort' of slow scan --- QtTSAprefs.db | Bin 131072 -> 131072 bytes QtTinySA.py | 31 ++++++++++++++++++------------- QtTinySpectrum.py | 8 ++------ QtTinySpectrum.ui | 13 ++----------- 4 files changed, 22 insertions(+), 30 deletions(-) diff --git a/QtTSAprefs.db b/QtTSAprefs.db index e1db8bd6dd55a76ff6db8de2841b55a9b3597cb0..4a3867980a02d6d6215f100da9d7cfdca90668c3 100644 GIT binary patch delta 822 zcmZo@;Am*zm>?~c^w%?U!R2G<2z#_xLpUlAjiT^48P5yKI2l%(}FXf-f-_2jgpU$3> zDJag!FIPy+$=S|mz{oDZ$pmzyC=g47_#gPUB``(_)^jWxP$-Mn`i+h2*qE3KHa6xk zO-|UuvR$m2@rMW>+go0Jo?pDhYz{mnJS7_&Ef}|lOk{k+!pl>_uofH z%w_bO{-ujiVETe?Mv?8T^B8|KE(eC6z_tyH2L$Sw!Uq(};?~v!@$6x0K{TI%*?>Rq&-o`m{o^CFWhTm%2s>UYW~^$sT&*J`6c*g zgVg|)Ap;(+$+iBnjNO}O`)40u>15#V+}N1OpEUtVO$(e|$|%Vo&B+7=j2xoU!jj^W zl1xnO?2^L5(kZEFiKRIu3@1WAFs@_VvBBIRIX5vku_U#$$imdXFTW@^F()83G1~~j zgpiCI7&mRdE6=Dbu$|F?kxgK`fCJ-q{)quxoJ>GFMS)lZB=LcNTLNR0AR9LqHxt*d zjg7N7MYx3+1fb61fYHnOsu@N3cvINk^6K;a;wb^ajg1zJ+e0QYzG0a-VLl7bFV?Bs zA5UYP$jSoL)HboPY5K1@j9CheauV#~va*a#{3VG=IjLzysf8t#1#nt=`sBHce*6k9 zu5ON@J|PO~Nr`zW>f0aAWt3-9;N{O_;NcHu;Qz$`l>aLKDgHhD8~7LUPvx)YFXE5q z58l{l%s)NZo>7{Gzn6h!dL59(!T*o}?CR+=?HPUKflguJW@KTI=7k0r4+9H>EN^i} zez`(oPR_Osj0XhR_@g+2Ib1h3*0T%oM==;c{S2h|3mBBPho4}aDbC2T{o{Q`egy!v C4}^XI diff --git a/QtTinySA.py b/QtTinySA.py index cce80d4..4a77abd 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -126,7 +126,7 @@ def initialise(self): hardware = '' while hardware[:6] != 'tinySA' and i < 4: # try 3 times to detect TinySA hardware = self.version() - logging.info(f'Try {i} to detect TinySA version: {hardware[:16]}') + logging.info(f'Try {i}: TinySA version: {hardware[:16]}') i += 1 time.sleep(0.5) # hardware = 'tinySA' # used for testing @@ -242,7 +242,7 @@ def usbSend(self): logging.info(f'Serial port open: {self.usb.isOpen()}') while self.fifo.qsize() > 0: command = self.fifo.get(block=True, timeout=None) - logging.info(command) + logging.debug(command) self.usb.write(command) self.usb.read_until(b'ch> ') # skip command echo and prompt @@ -279,7 +279,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] @@ -592,10 +592,10 @@ def sampleRep(self): command = f'repeat {ui.sampleRepeat.value()}\r'.encode() self.fifo.put(command) - def fPrecision(self, frequencies): # sets the marker and band indicated frequency precision + def fPrecision(self, frequencies): # sets the marker indicated frequency precision fInc = frequencies[1] - frequencies[0] self.dp = int(np.log10(frequencies[0] / fInc)) # number of decicimal places required - logging.info(f'fPrecision: fInc = {fInc} dp = {self.dp}') + logging.debug(f'fPrecision: fInc = {fInc} dp = {self.dp}') if self.dp < 0: self.dp = 0 @@ -856,7 +856,9 @@ 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() @@ -960,14 +962,14 @@ def addBandPressed(): title = "New Frequency Band" message = "Input the name of your new band." bandName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal, "") - bands.insertData(name=bandName, preset=ID, startF=f'{S1.vline.value():.{tinySA.dp}f}', - stopF=f'{S2.vline.value():.{tinySA.dp}f}', value=1, colour=colourID('green')) # colourID(value) + 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" message = "Input the Name for your Spot Frequency" spotName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal, "") - bands.insertData(name=spotName, preset="12", startF=f'{S1.vline.value():.{tinySA.dp}f}', - stopF='', value=1, colour=colourID('orange')) # preset 12 is Marker (spot frequency). + 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(): @@ -985,8 +987,11 @@ def attenuate_changed(): 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 @@ -996,7 +1001,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(): @@ -1172,7 +1177,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.5a') +app.setApplicationVersion(' v0.10.5b') window = QtWidgets.QMainWindow() ui = QtTinySpectrum.Ui_MainWindow() ui.setupUi(window) @@ -1361,13 +1366,13 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. # 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 ui band selection combo box, which needs different filter to preferences dialogue and preset markers bandselect.createTableModel() -bandselect.tm.setRelation(2, QSqlRelation('freqtype', 'ID', 'type')) +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) diff --git a/QtTinySpectrum.py b/QtTinySpectrum.py index ddc3e70..1c8b009 100644 --- a/QtTinySpectrum.py +++ b/QtTinySpectrum.py @@ -143,9 +143,6 @@ def setupUi(self, MainWindow): 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, 10, 0, 1, 1) self.mixerMode = QtWidgets.QLabel(self.ViewNormal) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) @@ -248,7 +245,6 @@ 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.sampleRepeat = QtWidgets.QSpinBox(self.ViewNormal) font = QtGui.QFont() @@ -952,7 +948,7 @@ def retranslateUi(self, MainWindow): 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 Points")) + 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")) @@ -971,7 +967,7 @@ def retranslateUi(self, MainWindow): 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")) + 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")) diff --git a/QtTinySpectrum.ui b/QtTinySpectrum.ui index e7817a4..04790a7 100644 --- a/QtTinySpectrum.ui +++ b/QtTinySpectrum.ui @@ -215,7 +215,7 @@ - Set Points + Set the number of frequency Points to measure @@ -258,9 +258,6 @@ true - - rbwpointsgroup - @@ -490,9 +487,6 @@ Auto RBW - - rbwpointsgroup - @@ -503,7 +497,7 @@ - Set the number of repeat measurements at each frequency + Set the number of repeat measurements at each frequency Point 1 @@ -2033,7 +2027,4 @@ true - - - From 83dafec0ee2573799145a0e14dcb09032c3b95e9 Mon Sep 17 00:00:00 2001 From: G4IXT Date: Mon, 27 May 2024 21:29:12 +0100 Subject: [PATCH 11/18] v0.10.5 wip file menu added file > quit file > browse sd card code development --- QtTSAfilebrowse.py | 36 +++++++++++++ QtTinySA.py | 132 +++++++++++++++++++++++++++++++++------------ QtTinySpectrum.py | 12 +++++ QtTinySpectrum.ui | 18 +++++++ filebrowse.ui | 58 ++++++++++++++++++++ 5 files changed, 222 insertions(+), 34 deletions(-) create mode 100644 QtTSAfilebrowse.py create mode 100644 filebrowse.ui diff --git a/QtTSAfilebrowse.py b/QtTSAfilebrowse.py new file mode 100644 index 0000000..21b1176 --- /dev/null +++ b/QtTSAfilebrowse.py @@ -0,0 +1,36 @@ +# -*- 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.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", "Download")) diff --git a/QtTinySA.py b/QtTinySA.py index 4a77abd..2c34cff 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -27,10 +27,12 @@ from PyQt5 import QtWidgets, QtCore 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 @@ -66,6 +68,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) @@ -165,6 +168,7 @@ def initialise(self): 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 def restoreSettings(self): @@ -204,10 +208,6 @@ def scan(self): # called by 'run' button ui.run3D.setEnabled(False) else: try: # start measurements - # test - command = 'abort on\r'.encode() - self.usb.write(command) - # test self.fifoTimer.stop() self.scanCount = 1 self.clearBuffer() @@ -243,15 +243,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 @@ -299,7 +299,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 @@ -348,19 +348,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 @@ -536,26 +536,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 @@ -563,19 +563,19 @@ 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) @@ -584,21 +584,38 @@ 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'.encode() + 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 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'.encode() + 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] - self.dp = int(np.log10(frequencies[0] / fInc)) # number of decicimal places required - logging.debug(f'fPrecision: fInc = {fInc} dp = {self.dp}') - if self.dp < 0: + if fInc > 0: + self.dp = int(np.log10(frequencies[0] / fInc)) # number of decicimal places required + logging.debug(f'fPrecision: fInc = {fInc} dp = {self.dp}') + else: self.dp = 0 + def listSD(self): + 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 + buffer = self.usb.read(4) + size = struct.unpack(format_string, buffer) + size = size[0] + data = self.usb.read(size) + return (data) + class display: def __init__(self, name, pen): @@ -689,10 +706,6 @@ def updateTrace(self, frequencies, readings): # called by sigProcess() for ever else: tinySA.vGrid.hide() if not tinySA.sweeping: # measurement thread is stopping - # test - # command = 'abort\r'.encode() - # tinySA.usb.write(command) - # test ui.scan_button.setText('Stopping ...') ui.scan_button.setStyleSheet('background-color: orange') ui.run3D.setText('Stopping ...') @@ -704,7 +717,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():.3f}MHz') self.vline.label.setText(f'M{self.vline.name()} {self.vline.value():.{tinySA.dp}f}MHz') else: # marker is in scan range @@ -980,7 +993,7 @@ 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) @@ -1052,6 +1065,45 @@ def dialogPrefs(): pwindow.show() +def dialogBrowse(): + if tinySA.threadRunning: + popUp("Cannot browse tinySA whilst a scan is running", QMessageBox.Ok, QMessageBox.Information) + return + else: + tinySA.clearBuffer() + ls = tinySA.listSD() + logging.info(f'list = {ls}') + filebrowse.listWidget.clear() + filebrowse.listWidget.insertItems(0, ls.splitlines()) + fwindow.show() + + +def fileDownload(): + fileName = filebrowse.listWidget.currentItem() + fileName = fileName.text().split(" ")[0] + with open(fileName, "wb") as file: + fileContent = tinySA.readSD(fileName) + file.write(fileContent) + file.close() + tinySA.clearBuffer() + pixmap = QPixmap(fileName) + filebrowse.picture.setPixmap(pixmap) + + +def fileShow(): + fileName = filebrowse.listWidget.currentItem() + fileName = fileName.text().split(" ")[0] + if fileName[-3:] == 'bmp': + if os.path.isfile(fileName): + logging.info(f'filename = {fileName}') + pixmap = QPixmap(fileName) + filebrowse.picture.setPixmap(pixmap) + else: + fileDownload() + else: + filebrowse.picture.clear() + + def about(): message = ('TinySA Ultra GUI programme using Qt5 and PyQt\nAuthor: Ian Jefferson G4IXT\n\nVersion: {} \nConfig: {}' .format(app.applicationVersion(), config.dbpath)) @@ -1177,7 +1229,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.5b') +app.setApplicationVersion(' v0.10.5c') window = QtWidgets.QMainWindow() ui = QtTinySpectrum.Ui_MainWindow() ui.setupUi(window) @@ -1186,6 +1238,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) @@ -1330,6 +1386,14 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. preferences.exportButton.pressed.connect(exportData) preferences.importButton.pressed.connect(importData) +# filebrowse +ui.actionBrowse_TinySA.triggered.connect(dialogBrowse) +filebrowse.download.clicked.connect(fileDownload) +filebrowse.listWidget.itemSelectionChanged.connect(fileShow) + +# Quit +ui.actionQuit.triggered.connect(app.closeAllWindows) + ############################################################################### # set up the application diff --git a/QtTinySpectrum.py b/QtTinySpectrum.py index 1c8b009..c7e05b3 100644 --- a/QtTinySpectrum.py +++ b/QtTinySpectrum.py @@ -912,6 +912,8 @@ def setupUi(self, MainWindow): 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") @@ -925,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()) @@ -1028,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 04790a7..b46da7d 100644 --- a/QtTinySpectrum.ui +++ b/QtTinySpectrum.ui @@ -1961,6 +1961,14 @@ + + + File + + + + + @@ -1994,6 +2002,16 @@ Bands and Markers + + + Browse TinySA + + + + + Quit + + diff --git a/filebrowse.ui b/filebrowse.ui new file mode 100644 index 0000000..bbff051 --- /dev/null +++ b/filebrowse.ui @@ -0,0 +1,58 @@ + + + Filebrowse + + + + 0 + 0 + 818 + 405 + + + + File List + + + + + 10 + 20 + 301 + 311 + + + + + + + 10 + 350 + 80 + 26 + + + + Download + + + + + + 320 + 20 + 481 + 321 + + + + + + + false + + + + + + From ba27ad6f061c7cebd01b9c52c0cb7175a1342170 Mon Sep 17 00:00:00 2001 From: G4IXT Date: Tue, 28 May 2024 17:52:49 +0100 Subject: [PATCH 12/18] v0.10.5d file browse for testing --- QtTSAfilebrowse.py | 8 ++++ QtTinySA.py | 99 ++++++++++++++++++++++++++-------------------- filebrowse.ui | 26 ++++++++++++ 3 files changed, 91 insertions(+), 42 deletions(-) diff --git a/QtTSAfilebrowse.py b/QtTSAfilebrowse.py index 21b1176..3796205 100644 --- a/QtTSAfilebrowse.py +++ b/QtTSAfilebrowse.py @@ -26,6 +26,14 @@ def setupUi(self, Filebrowse): 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) diff --git a/QtTinySA.py b/QtTinySA.py index 2c34cff..ab93d88 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -85,6 +85,7 @@ def __init__(self): self.fifoTimer.timeout.connect(self.usbSend) self.tinySA4 = None self.maxF = 6000 + self.directory = None def openPort(self): self.dev = None @@ -601,20 +602,70 @@ def fPrecision(self, frequencies): # sets the marker indicated frequency precis self.dp = 0 def listSD(self): + self.clearBuffer() # clear the USB serial buffer command = 'sd_list\r' ls = self.serialQuery(command) return ls def readSD(self, fileName): + # self.clearBuffer() # clear the USB serial buffer 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) - return (data) + 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 + else: + 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() + + def fileDownload(self): + selected = filebrowse.listWidget.currentItem().text() # the file selected in the list widget + if self.directory: # already downloaded 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) + filebrowse.downloadBar.setValue(20) # update the fake progress bar to show start of download + with open(str(fileName[0]), "wb") as file: + file.write(self.readSD(selected)) + self.clearBuffer() + # pixmap = QPixmap(selected) + pixmap = QPixmap(fileName[0]) + filebrowse.picture.setPixmap(pixmap) + filebrowse.downloadBar.setValue(100) # update the fake progress bar to complete + 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) + fileName = filebrowse.listWidget.currentItem().text() + if self.directory: + filebrowse.downloadInfo.setText(self.directory) + folder = os.path.join(self.directory, fileName) + else: + folder = fileName + if not os.path.isfile(folder): + filebrowse.picture.clear() + if fileName[-3:] == 'bmp': + if os.path.isfile(folder): + pixmap = QPixmap(folder) + filebrowse.picture.setPixmap(pixmap) class display: @@ -1065,43 +1116,7 @@ def dialogPrefs(): pwindow.show() -def dialogBrowse(): - if tinySA.threadRunning: - popUp("Cannot browse tinySA whilst a scan is running", QMessageBox.Ok, QMessageBox.Information) - return - else: - tinySA.clearBuffer() - ls = tinySA.listSD() - logging.info(f'list = {ls}') - filebrowse.listWidget.clear() - filebrowse.listWidget.insertItems(0, ls.splitlines()) - fwindow.show() - - -def fileDownload(): - fileName = filebrowse.listWidget.currentItem() - fileName = fileName.text().split(" ")[0] - with open(fileName, "wb") as file: - fileContent = tinySA.readSD(fileName) - file.write(fileContent) - file.close() - tinySA.clearBuffer() - pixmap = QPixmap(fileName) - filebrowse.picture.setPixmap(pixmap) - - -def fileShow(): - fileName = filebrowse.listWidget.currentItem() - fileName = fileName.text().split(" ")[0] - if fileName[-3:] == 'bmp': - if os.path.isfile(fileName): - logging.info(f'filename = {fileName}') - pixmap = QPixmap(fileName) - filebrowse.picture.setPixmap(pixmap) - else: - fileDownload() - else: - filebrowse.picture.clear() + def about(): @@ -1229,7 +1244,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.5c') +app.setApplicationVersion(' v0.10.5d') window = QtWidgets.QMainWindow() ui = QtTinySpectrum.Ui_MainWindow() ui.setupUi(window) @@ -1387,9 +1402,9 @@ def colourID(shade): # using the QSQLRelation directly doesn't work for colour. preferences.importButton.pressed.connect(importData) # filebrowse -ui.actionBrowse_TinySA.triggered.connect(dialogBrowse) -filebrowse.download.clicked.connect(fileDownload) -filebrowse.listWidget.itemSelectionChanged.connect(fileShow) +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) diff --git a/filebrowse.ui b/filebrowse.ui index bbff051..8a2fddb 100644 --- a/filebrowse.ui +++ b/filebrowse.ui @@ -52,6 +52,32 @@ false + + + + 100 + 350 + 211 + 26 + + + + 0 + + + + + + 10 + 380 + 791 + 20 + + + + + + From 743bffd6cf5c30495d89d7a071c0cc97a4ab3641 Mon Sep 17 00:00:00 2001 From: G4IXT Date: Tue, 28 May 2024 20:49:31 +0100 Subject: [PATCH 13/18] v0.10.5d marker precision clipped --- QtTinySA.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtTinySA.py b/QtTinySA.py index ab93d88..a31ec99 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -596,7 +596,7 @@ def sampleRep(self): def fPrecision(self, frequencies): # sets the marker indicated frequency precision fInc = frequencies[1] - frequencies[0] if fInc > 0: - self.dp = int(np.log10(frequencies[0] / fInc)) # number of decicimal places required + 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 = 0 From 458954f954db0ae6c42ea7fcb5625240e2f71a59 Mon Sep 17 00:00:00 2001 From: G4IXT Date: Tue, 28 May 2024 21:13:48 +0100 Subject: [PATCH 14/18] v0.10.5d error trapping --- QtTinySA.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/QtTinySA.py b/QtTinySA.py index a31ec99..b17ef83 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -602,13 +602,13 @@ def fPrecision(self, frequencies): # sets the marker indicated frequency precis self.dp = 0 def listSD(self): - self.clearBuffer() # clear the USB serial buffer - command = 'sd_list\r' - ls = self.serialQuery(command) - return ls + if self.usb: + self.clearBuffer() # clear the USB serial buffer + command = 'sd_list\r' + ls = self.serialQuery(command) + return ls def readSD(self, fileName): - # self.clearBuffer() # clear the USB serial buffer command = ('sd_read %s\r' % fileName) self.usb.write(command.encode()) self.usb.readline() # discard empty line @@ -625,7 +625,7 @@ def dialogBrowse(self): if self.threadRunning: popUp("Cannot browse tinySA whilst a scan is running", QMessageBox.Ok, QMessageBox.Information) return - else: + elif self.usb: SD = self.listSD() filebrowse.listWidget.clear() ls = [] From 7f9b3de834aa0734f0bd333a52730124a749a6fc Mon Sep 17 00:00:00 2001 From: G4IXT Date: Tue, 4 Jun 2024 13:25:17 +0100 Subject: [PATCH 15/18] v0.10.5e improved SD browse Improved SD card browse to use BytesIO temporary storage --- QtTSAfilebrowse.py | 2 +- QtTinySA.py | 32 ++++++++++++-------------------- filebrowse.ui | 2 +- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/QtTSAfilebrowse.py b/QtTSAfilebrowse.py index 3796205..101e0ee 100644 --- a/QtTSAfilebrowse.py +++ b/QtTSAfilebrowse.py @@ -41,4 +41,4 @@ def setupUi(self, Filebrowse): def retranslateUi(self, Filebrowse): _translate = QtCore.QCoreApplication.translate Filebrowse.setWindowTitle(_translate("Filebrowse", "File List")) - self.download.setText(_translate("Filebrowse", "Download")) + self.download.setText(_translate("Filebrowse", "Save")) diff --git a/QtTinySA.py b/QtTinySA.py index b17ef83..5c5a71b 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -36,6 +36,7 @@ import struct import serial from serial.tools import list_ports +from io import BytesIO # For 3D import pyqtgraph.opengl as pyqtgl @@ -643,29 +644,23 @@ def fileDownload(self): fileName = QFileDialog.getSaveFileName(caption="Save As", directory=selected) filebrowse.downloadBar.setValue(20) # update the fake progress bar to show start of download with open(str(fileName[0]), "wb") as file: - file.write(self.readSD(selected)) - self.clearBuffer() - # pixmap = QPixmap(selected) - pixmap = QPixmap(fileName[0]) - filebrowse.picture.setPixmap(pixmap) + file.write(self.fileShow().getvalue()) filebrowse.downloadBar.setValue(100) # update the fake progress bar to complete 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) + filebrowse.downloadBar.setValue(0) # reset the fake progress bar + filebrowse.picture.clear() fileName = filebrowse.listWidget.currentItem().text() - if self.directory: - filebrowse.downloadInfo.setText(self.directory) - folder = os.path.join(self.directory, fileName) - else: - folder = fileName - if not os.path.isfile(folder): - filebrowse.picture.clear() + self.clearBuffer() # clear the tinySA serial buffer + memF = BytesIO() + memF.write(self.readSD(fileName)) if fileName[-3:] == 'bmp': - if os.path.isfile(folder): - pixmap = QPixmap(folder) - filebrowse.picture.setPixmap(pixmap) + pixmap = QPixmap() + pixmap.loadFromData(memF.getvalue()) + filebrowse.picture.setPixmap(pixmap) + return memF class display: @@ -768,7 +763,6 @@ 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 @@ -777,10 +771,8 @@ 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 @@ -1244,7 +1236,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.5d') +app.setApplicationVersion(' v0.10.5e') window = QtWidgets.QMainWindow() ui = QtTinySpectrum.Ui_MainWindow() ui.setupUi(window) diff --git a/filebrowse.ui b/filebrowse.ui index 8a2fddb..d041750 100644 --- a/filebrowse.ui +++ b/filebrowse.ui @@ -33,7 +33,7 @@ - Download + Save From 29207408b501c3e19bc62579ebf41ff66cfef037 Mon Sep 17 00:00:00 2001 From: G4IXT Date: Tue, 4 Jun 2024 14:27:39 +0100 Subject: [PATCH 16/18] v0.10.5e tweak tweak SD card browse --- QtTinySA.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/QtTinySA.py b/QtTinySA.py index 5c5a71b..37d1152 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -87,6 +87,7 @@ def __init__(self): self.tinySA4 = None self.maxF = 6000 self.directory = None + self.memF = BytesIO() def openPort(self): self.dev = None @@ -637,15 +638,13 @@ def dialogBrowse(self): def fileDownload(self): selected = filebrowse.listWidget.currentItem().text() # the file selected in the list widget - if self.directory: # already downloaded a file so use the same folder path as the default + 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) - filebrowse.downloadBar.setValue(20) # update the fake progress bar to show start of download with open(str(fileName[0]), "wb") as file: - file.write(self.fileShow().getvalue()) - filebrowse.downloadBar.setValue(100) # update the fake progress bar to complete + 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 @@ -654,13 +653,13 @@ def fileShow(self): filebrowse.picture.clear() fileName = filebrowse.listWidget.currentItem().text() self.clearBuffer() # clear the tinySA serial buffer - memF = BytesIO() - memF.write(self.readSD(fileName)) + 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(memF.getvalue()) + pixmap.loadFromData(self.memF.getvalue()) filebrowse.picture.setPixmap(pixmap) - return memF + filebrowse.downloadBar.setValue(100) # update the fake progress bar to complete class display: From 623c76b907a4ac4ea7e9722396f9eb963aa1921b Mon Sep 17 00:00:00 2001 From: G4IXT Date: Fri, 7 Jun 2024 21:41:17 +0100 Subject: [PATCH 17/18] v0.10.5 for testing removed 'ensure exists' from __init__ of database class to stop errors caused by old versions of 'platformdirs' and re-wrote _getPersonalisedPath Fn to suit --- QtTSAprefs.db | Bin 131072 -> 131072 bytes QtTinySA.py | 60 +++++++++++++++++++++++--------------------------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/QtTSAprefs.db b/QtTSAprefs.db index 4a3867980a02d6d6215f100da9d7cfdca90668c3..fda8be8cab1fcdb528aa68e5c298c361d3374bbb 100644 GIT binary patch delta 148 zcmZo@;Am*zm>|vgZla7c|u#Y@&=aM2af|_V0wd*yFz%TTV`TzVo7Rgk)eS> zKx$&PA&4;Y%P-1J%z==cNt}5Oi&@%z=VY@wEavT;{dEdNX8R|3M#ck-oZG+LXXH@; E01Q7d!~g&Q diff --git a/QtTinySA.py b/QtTinySA.py index 37d1152..af20dbd 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -132,7 +132,7 @@ def initialise(self): hardware = '' while hardware[:6] != 'tinySA' and i < 4: # try 3 times to detect TinySA hardware = self.version() - logging.info(f'Try {i}: TinySA version: {hardware[:16]}') + logging.info(f'Hardware detection attempt {i}: TinySA version: {hardware[:16]}') i += 1 time.sleep(0.5) # hardware = 'tinySA' # used for testing @@ -217,6 +217,7 @@ def scan(self): # called by 'run' button self.setRBW() self.sampleRep() self.runButton('Stop') + self.pause() self.usbSend() self.startMeasurement() # runs measurement in separate thread except serial.SerialException: @@ -590,6 +591,10 @@ def setTime(self): 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' @@ -635,6 +640,8 @@ def dialogBrowse(self): 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 @@ -818,40 +825,31 @@ 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.personalDir = platformdirs.user_config_dir(appname=app.applicationName()) self.globalDir = platformdirs.site_config_dir(appname=app.applicationName()) 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): - # 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) + 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 else: - raise FileNotFoundError("Unable to create a personal configuration database") - return returnpath + raise FileNotFoundError("Unable to find the configuration database QtTSAprefs.db") def connect(self): self.db = QSqlDatabase.addDatabase('QSQLITE') @@ -1107,9 +1105,6 @@ def dialogPrefs(): pwindow.show() - - - def about(): message = ('TinySA Ultra GUI programme using Qt5 and PyQt\nAuthor: Ian Jefferson G4IXT\n\nVersion: {} \nConfig: {}' .format(app.applicationVersion(), config.dbpath)) @@ -1137,6 +1132,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') @@ -1235,7 +1231,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.5e') +app.setApplicationVersion(' v0.10.5') window = QtWidgets.QMainWindow() ui = QtTinySpectrum.Ui_MainWindow() ui.setupUi(window) From 48c0d923d7384ce8e9e1293f4efea7901ee6dffe Mon Sep 17 00:00:00 2001 From: G4IXT Date: Fri, 7 Jun 2024 22:29:45 +0100 Subject: [PATCH 18/18] v0.10.5 bugfix line 1024 type="12" changed to type=12 --- QtTinySA.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtTinySA.py b/QtTinySA.py index af20dbd..bfc0e81 100755 --- a/QtTinySA.py +++ b/QtTinySA.py @@ -1021,7 +1021,7 @@ def addBandPressed(): title = "New Spot Frequency" message = "Input the Name for your Spot Frequency" spotName, ok = QInputDialog.getText(None, title, message, QLineEdit.Normal, "") - bands.insertData(name=spotName, type="12", startF=f'{S1.vline.value():.6f}', + bands.insertData(name=spotName, type=12, startF=f'{S1.vline.value():.6f}', stopF='', visible=1, colour=colourID('orange')) # preset 12 is Marker (spot frequency).