Migrated sweeps to a separate worker thread.

Updated Smith Chart to use tuple data format.
pull/1/head
Rune B. Broberg 2019-08-29 00:56:54 +02:00
rodzic 9a19310585
commit fa6e4c32e0
4 zmienionych plików z 173 dodań i 157 usunięć

Wyświetl plik

@ -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" />

Wyświetl plik

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

Wyświetl plik

@ -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():

Wyświetl plik

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