kopia lustrzana https://github.com/NanoVNA-Saver/nanovna-saver
Migrated sweeps to a separate worker thread.
Updated Smith Chart to use tuple data format.pull/1/head
rodzic
9a19310585
commit
fa6e4c32e0
|
@ -2,6 +2,7 @@
|
|||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
|
|
181
NanoVNASaver.py
181
NanoVNASaver.py
|
@ -20,12 +20,14 @@ class NanoVNASaver(QtWidgets.QWidget):
|
|||
|
||||
self.threadpool = QtCore.QThreadPool()
|
||||
print("Max thread count " + str(self.threadpool.maxThreadCount()))
|
||||
self.worker = SweepWorker(self)
|
||||
|
||||
self.noSweeps = 1 # Number of sweeps to run
|
||||
|
||||
self.serialLock = threading.Lock()
|
||||
self.serial = serial.Serial()
|
||||
|
||||
self.dataLock = threading.Lock()
|
||||
self.values = []
|
||||
self.frequencies = []
|
||||
self.data : List[Datapoint] = []
|
||||
|
@ -71,7 +73,8 @@ class NanoVNASaver(QtWidgets.QWidget):
|
|||
sweep_control_layout.addRow(QtWidgets.QLabel("Sweep count"), self.sweepCountInput)
|
||||
|
||||
self.sweepProgressBar = QtWidgets.QProgressBar()
|
||||
|
||||
self.sweepProgressBar.setMaximum(100)
|
||||
self.sweepProgressBar.setValue(0)
|
||||
sweep_control_layout.addRow(self.sweepProgressBar)
|
||||
|
||||
self.btnSweep = QtWidgets.QPushButton("Sweep")
|
||||
|
@ -183,6 +186,8 @@ class NanoVNASaver(QtWidgets.QWidget):
|
|||
right_column.addWidget(self.lister)
|
||||
right_column.addWidget(self.smithChart)
|
||||
|
||||
self.worker.signals.updated.connect(self.dataUpdated)
|
||||
|
||||
def exportFile(self):
|
||||
print("Save file to " + self.fileNameInput.text())
|
||||
filename = self.fileNameInput.text()
|
||||
|
@ -252,137 +257,34 @@ class NanoVNASaver(QtWidgets.QWidget):
|
|||
return
|
||||
|
||||
self.sweepProgressBar.setValue(0)
|
||||
|
||||
worker = SweepWorker(self)
|
||||
self.threadpool.start(worker)
|
||||
|
||||
self.btnSweep.setDisabled(True)
|
||||
|
||||
if int(self.sweepCountInput.text()) > 0:
|
||||
self.noSweeps = int(self.sweepCountInput.text())
|
||||
self.threadpool.start(self.worker)
|
||||
|
||||
print("### Updating... ### ")
|
||||
|
||||
if len(self.frequencies) > 1:
|
||||
# We've already run at least once
|
||||
print("### Run at least once ###")
|
||||
if (self.sweepStartInput.text() != self.frequencies[0]
|
||||
or self.sweepEndInput.text() != self.frequencies[100]):
|
||||
# Need to set frequency span
|
||||
print("### Setting span ###")
|
||||
# TODO: Set up for multiple sweeps
|
||||
print("Setting sweep to run " + self.sweepStartInput.text() + " to " + self.sweepEndInput.text())
|
||||
self.setSweep(self.sweepStartInput.text(), self.sweepEndInput.text())
|
||||
sleep(0.5)
|
||||
|
||||
if self.noSweeps > 1 and self.sweepStartInput.text() != "" and self.sweepEndInput.text() != "":
|
||||
# We're going to run multiple sweeps
|
||||
print("### Multisweep ###")
|
||||
span = int(self.sweepEndInput.text()) - int(self.sweepStartInput.text())
|
||||
start = int(self.sweepStartInput.text())
|
||||
end = int(self.sweepEndInput.text())
|
||||
stepsize = int(span / (100 + (self.noSweeps-1)*101))
|
||||
print("Doing " + str(100 + (self.noSweeps-1)*101) + " steps of size " + str(stepsize))
|
||||
values = []
|
||||
frequencies = []
|
||||
for i in range(self.noSweeps):
|
||||
self.setSweep(start + i*101*stepsize, start+(100+i*101)*stepsize)
|
||||
QtWidgets.QApplication.processEvents() # TODO: Make this multithreaded using the QT threads instead
|
||||
sleep(0.2)
|
||||
QtWidgets.QApplication.processEvents() # This is a really stupid way to limit UI sleeps to 0.2 seconds
|
||||
sleep(0.2)
|
||||
QtWidgets.QApplication.processEvents()
|
||||
sleep(0.2)
|
||||
QtWidgets.QApplication.processEvents()
|
||||
sleep(0.2)
|
||||
tmpdata = []
|
||||
done = False
|
||||
while not done:
|
||||
done = True
|
||||
tmpdata = self.readValues("data 0")
|
||||
for d in tmpdata:
|
||||
a, b = d.split(" ")
|
||||
try:
|
||||
if float(a) < -1.5 or float(a) > 1.5:
|
||||
print("Warning: Got a non-float data value: " + d + " (" + a + ")")
|
||||
done = False
|
||||
if float(b) < -1.5 or float(b) > 1.5:
|
||||
print("Warning: Got a non-float data value: " + d + " (" + b + ")")
|
||||
done = False
|
||||
except Exception:
|
||||
done = False
|
||||
|
||||
values += tmpdata
|
||||
|
||||
# TODO: Figure out why frequencies sometimes arrive as non-numbers
|
||||
tmpfreq = []
|
||||
done = False
|
||||
while not done:
|
||||
done = True
|
||||
tmpfreq = self.readValues("frequencies")
|
||||
for f in tmpfreq:
|
||||
if not f.isdigit():
|
||||
print("Warning: Got a non-digit frequency: " + f)
|
||||
done = False
|
||||
|
||||
frequencies += tmpfreq
|
||||
self.smithChart.setValues(values, frequencies)
|
||||
self.sweepProgressBar.setValue(round(100*(i+1)/self.noSweeps))
|
||||
|
||||
self.values = values
|
||||
self.frequencies = frequencies
|
||||
# Test code which sets up an array of tuples of parsed values
|
||||
self.data = []
|
||||
for i in range(len(values)):
|
||||
reStr, imStr = values[i].split(" ")
|
||||
re = float(reStr)
|
||||
im = float(imStr)
|
||||
freq = int(frequencies[i])
|
||||
self.data += [Datapoint(freq, re, im)]
|
||||
|
||||
# Reset the device to show the full range
|
||||
self.setSweep(self.sweepStartInput.text(), self.sweepEndInput.text())
|
||||
else:
|
||||
print("### Reading values ###")
|
||||
self.values = self.readValues("data 0")
|
||||
print("### Reading frequencies ###")
|
||||
self.frequencies = self.readValues("frequencies")
|
||||
if self.sweepStartInput.text() == "":
|
||||
self.sweepStartInput.setText(self.frequencies[0])
|
||||
if self.sweepEndInput.text() == "":
|
||||
self.sweepEndInput.setText(self.frequencies[100])
|
||||
self.sweepProgressBar.setValue(100)
|
||||
|
||||
print("### Outputting values in textbox ###")
|
||||
for line in self.values:
|
||||
self.lister.appendPlainText(line)
|
||||
print("### Displaying Smith chart ###")
|
||||
self.smithChart.setValues(self.values, self.frequencies)
|
||||
if self.smithChart.marker1Location != -1:
|
||||
reStr, imStr = self.values[self.smithChart.marker1Location].split(" ")
|
||||
re = float(reStr)
|
||||
im = float(imStr)
|
||||
|
||||
re50 = 50*(1-re*re-im*im)/(1+re*re+im*im-2*re)
|
||||
im50 = 50*(2*im)/(1+re*re+im*im-2*re)
|
||||
|
||||
mag = math.sqrt(re*re+im*im)
|
||||
vswr = (1+mag)/(1-mag)
|
||||
self.marker1label.setText(str(round(re50, 3)) + " + j" + str(round(im50, 3)) + " VSWR: 1:" + str(round(vswr, 3)))
|
||||
|
||||
if self.smithChart.marker2Location != -1:
|
||||
reStr, imStr = self.values[self.smithChart.marker2Location].split(" ")
|
||||
re = float(reStr)
|
||||
im = float(imStr)
|
||||
|
||||
re50 = 50*(1-re*re-im*im)/(1+re*re+im*im-2*re)
|
||||
im50 = 50*(2*im)/(1+re*re+im*im-2*re)
|
||||
|
||||
mag = math.sqrt(re*re+im*im)
|
||||
vswr = (1+mag)/(1-mag)
|
||||
self.marker2label.setText(str(round(re50, 3)) + " + j" + str(round(im50, 3)) + " VSWR: 1:" + str(round(vswr, 3)))
|
||||
|
||||
self.btnSweep.setDisabled(False)
|
||||
# TODO: Make markers into separate objects, and integrate updating them.
|
||||
# if self.smithChart.marker1Location != -1:
|
||||
# reStr, imStr = self.values[self.smithChart.marker1Location].split(" ")
|
||||
# re = float(reStr)
|
||||
# im = float(imStr)
|
||||
#
|
||||
# re50 = 50*(1-re*re-im*im)/(1+re*re+im*im-2*re)
|
||||
# im50 = 50*(2*im)/(1+re*re+im*im-2*re)
|
||||
#
|
||||
# mag = math.sqrt(re*re+im*im)
|
||||
# vswr = (1+mag)/(1-mag)
|
||||
# self.marker1label.setText(str(round(re50, 3)) + " + j" + str(round(im50, 3)) + " VSWR: 1:" + str(round(vswr, 3)))
|
||||
#
|
||||
# if self.smithChart.marker2Location != -1:
|
||||
# reStr, imStr = self.values[self.smithChart.marker2Location].split(" ")
|
||||
# re = float(reStr)
|
||||
# im = float(imStr)
|
||||
#
|
||||
# re50 = 50*(1-re*re-im*im)/(1+re*re+im*im-2*re)
|
||||
# im50 = 50*(2*im)/(1+re*re+im*im-2*re)
|
||||
#
|
||||
# mag = math.sqrt(re*re+im*im)
|
||||
# vswr = (1+mag)/(1-mag)
|
||||
# self.marker2label.setText(str(round(re50, 3)) + " + j" + str(round(im50, 3)) + " VSWR: 1:" + str(round(vswr, 3)))
|
||||
return
|
||||
|
||||
def readValues(self, value):
|
||||
|
@ -419,4 +321,23 @@ class NanoVNASaver(QtWidgets.QWidget):
|
|||
self.smithChart.marker2Color = color
|
||||
p = self.btnMarker2ColorPicker.palette()
|
||||
p.setColor(QtGui.QPalette.ButtonText, color)
|
||||
self.btnMarker2ColorPicker.setPalette(p)
|
||||
self.btnMarker2ColorPicker.setPalette(p)
|
||||
|
||||
def saveData(self, data):
|
||||
if self.dataLock.acquire(blocking=True):
|
||||
self.data = data
|
||||
else:
|
||||
print("ERROR: Failed acquiring data lock while saving.")
|
||||
self.dataLock.release()
|
||||
|
||||
def dataUpdated(self):
|
||||
if self.dataLock.acquire(blocking=True):
|
||||
self.smithChart.setData(self.data)
|
||||
self.sweepProgressBar.setValue(self.worker.percentage)
|
||||
else:
|
||||
print("ERROR: Failed acquiring data lock while updating")
|
||||
self.dataLock.release()
|
||||
|
||||
def sweepFinished(self):
|
||||
self.sweepProgressBar.setValue(100)
|
||||
self.btnSweep.setDisabled(False)
|
|
@ -1,7 +1,10 @@
|
|||
# Copyright 2019 Rune B. Broberg
|
||||
import collections
|
||||
from typing import List
|
||||
|
||||
from PyQt5 import QtWidgets, QtGui, QtCore
|
||||
|
||||
Datapoint = collections.namedtuple('Datapoint', 'freq re im')
|
||||
|
||||
class SmithChart(QtWidgets.QWidget):
|
||||
def __init__(self):
|
||||
|
@ -18,6 +21,7 @@ class SmithChart(QtWidgets.QWidget):
|
|||
|
||||
self.values = []
|
||||
self.frequencies = []
|
||||
self.data : List[Datapoint] = []
|
||||
self.marker1 = -1
|
||||
self.marker2 = -1
|
||||
self.marker1Location = -1
|
||||
|
@ -72,44 +76,41 @@ class SmithChart(QtWidgets.QWidget):
|
|||
qp.setPen(pen)
|
||||
marker1 = -1
|
||||
marker2 = -1
|
||||
for i in range(len(self.values)):
|
||||
for i in range(len(self.data)):
|
||||
# TODO: Make this check for being "nearest" neater
|
||||
if self.marker1 != -1 and abs(int(self.frequencies[i]) - self.marker1) < (int(self.frequencies[2]) - int(self.frequencies[1])):
|
||||
if self.marker1 != -1 and abs(int(self.data[i].freq) - self.marker1) < (int(self.data[2].freq) - int(self.data[1].freq)):
|
||||
if marker1 != -1:
|
||||
# Are we closer than the other spot?
|
||||
if abs(int(self.frequencies[i]) - self.marker1) < abs(int(self.frequencies[marker1]) - self.marker1):
|
||||
if abs(int(self.data[i].freq) - self.marker1) < abs(int(self.data[marker1].freq) - self.marker1):
|
||||
marker1 = i
|
||||
else:
|
||||
marker1 = i
|
||||
|
||||
if self.marker2 != -1 and abs(int(self.frequencies[i]) - self.marker2) < (int(self.frequencies[2]) - int(self.frequencies[1])):
|
||||
if self.marker2 != -1 and abs(int(self.data[i].freq) - self.marker2) < (int(self.data[2].freq) - int(self.data[1].freq)):
|
||||
if marker2 != -1:
|
||||
# Are we closer than the other spot?
|
||||
if abs(int(self.frequencies[i]) - self.marker2) < abs(int(self.frequencies[marker2]) - self.marker2):
|
||||
if abs(int(self.data[i].freq) - self.marker2) < abs(int(self.data[marker2].freq) - self.marker2):
|
||||
marker2 = i
|
||||
else:
|
||||
marker2 = i
|
||||
|
||||
rawx, rawy = self.values[i].split(" ")
|
||||
x = self.width()/2 + float(rawx) * self.chartWidth/2
|
||||
y = self.height()/2 + float(rawy) * -1 * self.chartHeight/2
|
||||
x = self.width()/2 + self.data[i].re * self.chartWidth/2
|
||||
y = self.height()/2 + self.data[i].im * -1 * self.chartHeight/2
|
||||
qp.drawPoint(int(x), int(y))
|
||||
# Now draw the markers
|
||||
if marker1 != -1:
|
||||
highlighter.setColor(self.marker1Color)
|
||||
qp.setPen(highlighter)
|
||||
rawx, rawy = self.values[marker1].split(" ")
|
||||
x = self.width() / 2 + float(rawx) * self.chartWidth / 2
|
||||
y = self.height() / 2 + float(rawy) * -1 * self.chartHeight / 2
|
||||
x = self.width() / 2 + self.data[marker1].re * self.chartWidth / 2
|
||||
y = self.height() / 2 + self.data[marker1].im * -1 * self.chartHeight / 2
|
||||
qp.drawPoint(int(x), int(y))
|
||||
self.marker1Location = marker1
|
||||
|
||||
if marker2 != -1:
|
||||
highlighter.setColor(self.marker2Color)
|
||||
qp.setPen(highlighter)
|
||||
rawx, rawy = self.values[marker2].split(" ")
|
||||
x = self.width() / 2 + float(rawx) * self.chartWidth / 2
|
||||
y = self.height() / 2 + float(rawy) * -1 * self.chartHeight / 2
|
||||
x = self.width() / 2 + self.data[marker2].re * self.chartWidth / 2
|
||||
y = self.height() / 2 + self.data[marker2].im * -1 * self.chartHeight / 2
|
||||
qp.drawPoint(int(x), int(y))
|
||||
self.marker2Location = marker2
|
||||
|
||||
|
@ -119,6 +120,11 @@ class SmithChart(QtWidgets.QWidget):
|
|||
self.frequencies = frequencies
|
||||
self.update()
|
||||
|
||||
def setData(self, data):
|
||||
print("### Updating data ###")
|
||||
self.data = data
|
||||
self.update()
|
||||
|
||||
def setMarker1(self, value):
|
||||
self.marker1Location = -1
|
||||
if value.isnumeric():
|
||||
|
|
114
SweepWorker.py
114
SweepWorker.py
|
@ -1,27 +1,115 @@
|
|||
# Copyright (c) Rune B. Broberg
|
||||
import collections
|
||||
from time import sleep
|
||||
|
||||
from PyQt5 import QtCore
|
||||
from PyQt5.QtCore import pyqtSlot
|
||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal
|
||||
|
||||
from NanoVNASaver import NanoVNASaver
|
||||
import NanoVNASaver
|
||||
|
||||
Datapoint = collections.namedtuple('Datapoint', 'freq re im')
|
||||
|
||||
|
||||
class WorkerSignals(QtCore.QObject):
|
||||
updated = pyqtSignal()
|
||||
finished = pyqtSignal()
|
||||
|
||||
|
||||
class SweepWorker(QtCore.QRunnable):
|
||||
def __init__(self, app: NanoVNASaver):
|
||||
super().__init__()
|
||||
self.signals = WorkerSignals()
|
||||
self.app = app
|
||||
self.serial = app.serial
|
||||
self.serialLock = app.serialLock
|
||||
self.noSweeps = 1
|
||||
self.setAutoDelete(False)
|
||||
self.percentage = 0
|
||||
|
||||
@pyqtSlot()
|
||||
def run(self):
|
||||
print("I am thread")
|
||||
# TODO: Set up multithreading contents
|
||||
# Fetch all the parameters before starting
|
||||
# Optionally split the sweep into subsweeps
|
||||
# For each sweep:
|
||||
# - Fetch data
|
||||
# - Process data
|
||||
# - Store data
|
||||
# - Signal update
|
||||
# Signal finishing
|
||||
self.percentage = 0
|
||||
if not self.app.serial.is_open:
|
||||
return
|
||||
|
||||
if int(self.app.sweepCountInput.text()) > 0:
|
||||
self.noSweeps = int(self.app.sweepCountInput.text())
|
||||
|
||||
print("### Updating... ### ")
|
||||
if not self.app.sweepStartInput.text().isnumeric() or not self.app.sweepEndInput.text().isnumeric():
|
||||
# We should handle the first startup by reading frequencies?
|
||||
sweepFrom = 1000000
|
||||
sweepTo = 800000000
|
||||
else:
|
||||
sweepFrom = int(self.app.sweepStartInput.text())
|
||||
sweepTo = int(self.app.sweepEndInput.text())
|
||||
|
||||
if self.noSweeps > 1:
|
||||
# We're going to run multiple sweeps
|
||||
print("### Multisweep ###")
|
||||
span = sweepTo - sweepFrom
|
||||
stepsize = int(span / (100 + (self.noSweeps-1)*101))
|
||||
print("Doing " + str(100 + (self.noSweeps-1)*101) + " steps of size " + str(stepsize))
|
||||
values = []
|
||||
frequencies = []
|
||||
for i in range(self.noSweeps):
|
||||
self.app.setSweep(sweepFrom + i*101*stepsize, sweepFrom+(100+i*101)*stepsize)
|
||||
sleep(0.8)
|
||||
tmpdata = []
|
||||
done = False
|
||||
while not done:
|
||||
done = True
|
||||
tmpdata = self.app.readValues("data 0")
|
||||
for d in tmpdata:
|
||||
a, b = d.split(" ")
|
||||
try:
|
||||
if float(a) < -1.5 or float(a) > 1.5:
|
||||
print("Warning: Got a non-float data value: " + d + " (" + a + ")")
|
||||
done = False
|
||||
if float(b) < -1.5 or float(b) > 1.5:
|
||||
print("Warning: Got a non-float data value: " + d + " (" + b + ")")
|
||||
done = False
|
||||
except Exception:
|
||||
done = False
|
||||
|
||||
values += tmpdata
|
||||
|
||||
# TODO: Figure out why frequencies sometimes arrive as non-numbers
|
||||
tmpfreq = []
|
||||
done = False
|
||||
while not done:
|
||||
done = True
|
||||
tmpfreq = self.app.readValues("frequencies")
|
||||
for f in tmpfreq:
|
||||
if not f.isdigit():
|
||||
print("Warning: Got a non-digit frequency: " + f)
|
||||
done = False
|
||||
|
||||
frequencies += tmpfreq
|
||||
self.percentage = (i+1)*100/self.noSweeps
|
||||
self.saveData(frequencies, values)
|
||||
|
||||
# Reset the device to show the full range
|
||||
self.app.setSweep(self.app.sweepStartInput.text(), self.app.sweepEndInput.text())
|
||||
else:
|
||||
print("### Reading values ###")
|
||||
self.values = self.app.readValues("data 0")
|
||||
print("### Reading frequencies ###")
|
||||
self.frequencies = self.app.readValues("frequencies")
|
||||
print("Read data, saving")
|
||||
self.saveData(self.frequencies, self.values)
|
||||
|
||||
self.percentage = 100
|
||||
self.signals.finished.emit()
|
||||
return
|
||||
|
||||
def saveData(self, frequencies, values):
|
||||
data = []
|
||||
for i in range(len(values)):
|
||||
reStr, imStr = values[i].split(" ")
|
||||
re = float(reStr)
|
||||
im = float(imStr)
|
||||
freq = int(frequencies[i])
|
||||
data += [Datapoint(freq, re, im)]
|
||||
self.app.saveData(data)
|
||||
print("Saved data, emitting signal")
|
||||
self.signals.updated.emit()
|
||||
|
|
Ładowanie…
Reference in New Issue