kopia lustrzana https://github.com/NanoVNA-Saver/nanovna-saver
Feature/linting 220402 (#499)
* added .flatpak-builder to .gitingnore * GroupDelay Chart simplified * allow numpy > 1.21 fixes #456 * Added flatpak manifestpull/504/head
rodzic
140ce4906c
commit
6aa7aaa051
|
@ -11,4 +11,5 @@
|
||||||
settings.json
|
settings.json
|
||||||
.gitignore
|
.gitignore
|
||||||
.coverage
|
.coverage
|
||||||
/nanovna-saver.exe.spec
|
.flatpak-builder
|
||||||
|
/nanovna-saver.exe.spec
|
||||||
|
|
|
@ -70,23 +70,21 @@ class MagLoopAnalysis(VSWRAnalysis):
|
||||||
if len(self.minimums) == 1:
|
if len(self.minimums) == 1:
|
||||||
m = self.minimums[0]
|
m = self.minimums[0]
|
||||||
start, lowest, end = m
|
start, lowest, end = m
|
||||||
if start != end:
|
if start == end:
|
||||||
if self.vswr_limit_value == self.vswr_bandwith_value:
|
|
||||||
Q = self.app.data.s11[lowest].freq / \
|
|
||||||
(self.app.data.s11[end].freq -
|
|
||||||
self.app.data.s11[start].freq)
|
|
||||||
self.layout.addRow(
|
|
||||||
"Q", QtWidgets.QLabel("{}".format(int(Q))))
|
|
||||||
new_start = self.app.data.s11[start].freq - self.bandwith
|
|
||||||
new_end = self.app.data.s11[end].freq + self.bandwith
|
|
||||||
logger.debug("Single Spot, new scan on %s-%s",
|
|
||||||
new_start, new_end)
|
|
||||||
|
|
||||||
else:
|
|
||||||
new_start = self.app.data.s11[start].freq - 2 * self.bandwith
|
new_start = self.app.data.s11[start].freq - 2 * self.bandwith
|
||||||
new_end = self.app.data.s11[end].freq + 2 * self.bandwith
|
new_end = self.app.data.s11[end].freq + 2 * self.bandwith
|
||||||
logger.debug(" Zoom to %s-%s", new_start, new_end)
|
logger.debug(" Zoom to %s-%s", new_start, new_end)
|
||||||
|
|
||||||
|
elif self.vswr_limit_value == self.vswr_bandwith_value:
|
||||||
|
Q = self.app.data.s11[lowest].freq / \
|
||||||
|
(self.app.data.s11[end].freq -
|
||||||
|
self.app.data.s11[start].freq)
|
||||||
|
self.layout.addRow("Q", QtWidgets.QLabel(f"{int(Q)}"))
|
||||||
|
new_start = self.app.data.s11[start].freq - self.bandwith
|
||||||
|
new_end = self.app.data.s11[end].freq + self.bandwith
|
||||||
|
logger.debug("Single Spot, new scan on %s-%s",
|
||||||
|
new_start, new_end)
|
||||||
|
|
||||||
if self.vswr_limit_value > self.vswr_bandwith_value:
|
if self.vswr_limit_value > self.vswr_bandwith_value:
|
||||||
self.vswr_limit_value = max(
|
self.vswr_limit_value = max(
|
||||||
self.vswr_bandwith_value, self.vswr_limit_value - 1)
|
self.vswr_bandwith_value, self.vswr_limit_value - 1)
|
||||||
|
@ -96,13 +94,14 @@ class MagLoopAnalysis(VSWRAnalysis):
|
||||||
else:
|
else:
|
||||||
new_start = new_start - 5 * self.bandwith
|
new_start = new_start - 5 * self.bandwith
|
||||||
new_end = new_end + 5 * self.bandwith
|
new_end = new_end + 5 * self.bandwith
|
||||||
if all((new_start <= self.min_freq,
|
if (
|
||||||
new_end >= self.max_freq)):
|
all((new_start <= self.min_freq, new_end >= self.max_freq))
|
||||||
if self.vswr_limit_value < 10:
|
and self.vswr_limit_value < 10
|
||||||
self.vswr_limit_value += 2
|
):
|
||||||
self.input_vswr_limit.setValue(self.vswr_limit_value)
|
self.vswr_limit_value += 2
|
||||||
logger.debug(
|
self.input_vswr_limit.setValue(self.vswr_limit_value)
|
||||||
"no minimum found, looking for higher value %s", self.vswr_limit_value)
|
logger.debug(
|
||||||
|
"no minimum found, looking for higher value %s", self.vswr_limit_value)
|
||||||
new_start = max(self.min_freq, new_start)
|
new_start = max(self.min_freq, new_start)
|
||||||
new_end = min(self.max_freq, new_end)
|
new_end = min(self.max_freq, new_end)
|
||||||
logger.debug("next search will be %s - %s for vswr %s",
|
logger.debug("next search will be %s - %s for vswr %s",
|
||||||
|
|
|
@ -109,7 +109,7 @@ class Chart(QtWidgets.QWidget):
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.name = name
|
self.name = name
|
||||||
self.sweepTitle = ""
|
self.sweepTitle = ''
|
||||||
|
|
||||||
self.dim = ChartDimensions()
|
self.dim = ChartDimensions()
|
||||||
self.dragbox = ChartDragBox()
|
self.dragbox = ChartDragBox()
|
||||||
|
@ -170,10 +170,14 @@ class Chart(QtWidgets.QWidget):
|
||||||
def getActiveMarker(self) -> Marker:
|
def getActiveMarker(self) -> Marker:
|
||||||
if self.draggedMarker is not None:
|
if self.draggedMarker is not None:
|
||||||
return self.draggedMarker
|
return self.draggedMarker
|
||||||
for m in self.markers:
|
return next(
|
||||||
if m.isMouseControlledRadioButton.isChecked():
|
(
|
||||||
return m
|
m
|
||||||
return None
|
for m in self.markers
|
||||||
|
if m.isMouseControlledRadioButton.isChecked()
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
def getNearestMarker(self, x, y) -> Marker:
|
def getNearestMarker(self, x, y) -> Marker:
|
||||||
if len(self.data) == 0:
|
if len(self.data) == 0:
|
||||||
|
@ -264,7 +268,6 @@ class Chart(QtWidgets.QWidget):
|
||||||
self.swrMarkers.remove(swr)
|
self.swrMarkers.remove(swr)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
logger.debug("KeyError from %s", self.name)
|
logger.debug("KeyError from %s", self.name)
|
||||||
return
|
|
||||||
finally:
|
finally:
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
@ -277,8 +280,6 @@ class Chart(QtWidgets.QWidget):
|
||||||
cmarker.draw(x, y, color, str(number))
|
cmarker.draw(x, y, color, str(number))
|
||||||
|
|
||||||
def drawTitle(self, qp: QtGui.QPainter, position: QtCore.QPoint = None):
|
def drawTitle(self, qp: QtGui.QPainter, position: QtCore.QPoint = None):
|
||||||
if not self.sweepTitle:
|
|
||||||
return
|
|
||||||
qp.setPen(Chart.color.text)
|
qp.setPen(Chart.color.text)
|
||||||
if position is None:
|
if position is None:
|
||||||
qf = QtGui.QFontMetricsF(self.font())
|
qf = QtGui.QFontMetricsF(self.font())
|
||||||
|
|
|
@ -96,11 +96,11 @@ class FrequencyChart(Chart):
|
||||||
self.x_menu.addSeparator()
|
self.x_menu.addSeparator()
|
||||||
|
|
||||||
self.action_set_fixed_start = QtWidgets.QAction(
|
self.action_set_fixed_start = QtWidgets.QAction(
|
||||||
"Start (" + format_frequency_chart(self.minFrequency) + ")")
|
f"Start ({format_frequency_chart(self.minFrequency)})")
|
||||||
self.action_set_fixed_start.triggered.connect(self.setMinimumFrequency)
|
self.action_set_fixed_start.triggered.connect(self.setMinimumFrequency)
|
||||||
|
|
||||||
self.action_set_fixed_stop = QtWidgets.QAction(
|
self.action_set_fixed_stop = QtWidgets.QAction(
|
||||||
"Stop (" + format_frequency_chart(self.maxFrequency) + ")")
|
f"Stop ({format_frequency_chart(self.maxFrequency)})")
|
||||||
self.action_set_fixed_stop.triggered.connect(self.setMaximumFrequency)
|
self.action_set_fixed_stop.triggered.connect(self.setMaximumFrequency)
|
||||||
|
|
||||||
self.x_menu.addAction(self.action_set_fixed_start)
|
self.x_menu.addAction(self.action_set_fixed_start)
|
||||||
|
|
|
@ -62,64 +62,40 @@ class GroupDelayChart(FrequencyChart):
|
||||||
|
|
||||||
def setReference(self, data):
|
def setReference(self, data):
|
||||||
self.reference = data
|
self.reference = data
|
||||||
|
|
||||||
self.calculateGroupDelay()
|
self.calculateGroupDelay()
|
||||||
|
|
||||||
def setData(self, data):
|
def setData(self, data):
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
self.calculateGroupDelay()
|
self.calculateGroupDelay()
|
||||||
|
|
||||||
def calculateGroupDelay(self):
|
def calculateGroupDelay(self):
|
||||||
rawData = []
|
self.groupDelay = self.calc_data(self.data)
|
||||||
for d in self.data:
|
self.groupDelayReference = self.calc_data(self.reference)
|
||||||
rawData.append(d.phase)
|
|
||||||
|
|
||||||
rawReference = []
|
|
||||||
for d in self.reference:
|
|
||||||
rawReference.append(d.phase)
|
|
||||||
|
|
||||||
if len(self.data) > 1:
|
|
||||||
unwrappedData = np.degrees(np.unwrap(rawData))
|
|
||||||
self.groupDelay = []
|
|
||||||
for i in range(len(self.data)):
|
|
||||||
# TODO: Replace with call to RFTools.groupDelay
|
|
||||||
if i == 0:
|
|
||||||
phase_change = unwrappedData[1] - unwrappedData[0]
|
|
||||||
freq_change = self.data[1].freq - self.data[0].freq
|
|
||||||
elif i == len(self.data)-1:
|
|
||||||
idx = len(self.data)-1
|
|
||||||
phase_change = unwrappedData[idx] - unwrappedData[idx-1]
|
|
||||||
freq_change = self.data[idx].freq - self.data[idx-1].freq
|
|
||||||
else:
|
|
||||||
phase_change = unwrappedData[i+1] - unwrappedData[i-1]
|
|
||||||
freq_change = self.data[i+1].freq - self.data[i-1].freq
|
|
||||||
delay = (-phase_change / (freq_change * 360)) * 10e8
|
|
||||||
if not self.reflective:
|
|
||||||
delay /= 2
|
|
||||||
self.groupDelay.append(delay)
|
|
||||||
|
|
||||||
if len(self.reference) > 1:
|
|
||||||
unwrappedReference = np.degrees(np.unwrap(rawReference))
|
|
||||||
self.groupDelayReference = []
|
|
||||||
for i in range(len(self.reference)):
|
|
||||||
if i == 0:
|
|
||||||
phase_change = unwrappedReference[1] - unwrappedReference[0]
|
|
||||||
freq_change = self.reference[1].freq - self.reference[0].freq
|
|
||||||
elif i == len(self.reference)-1:
|
|
||||||
idx = len(self.reference)-1
|
|
||||||
phase_change = unwrappedReference[idx] - unwrappedReference[idx-1]
|
|
||||||
freq_change = self.reference[idx].freq - self.reference[idx-1].freq
|
|
||||||
else:
|
|
||||||
phase_change = unwrappedReference[i+1] - unwrappedReference[i-1]
|
|
||||||
freq_change = self.reference[i+1].freq - self.reference[i-1].freq
|
|
||||||
delay = (-phase_change / (freq_change * 360)) * 10e8
|
|
||||||
if not self.reflective:
|
|
||||||
delay /= 2
|
|
||||||
self.groupDelayReference.append(delay)
|
|
||||||
|
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
def calc_data(self, data: List[Datapoint]):
|
||||||
|
data_len = len(data)
|
||||||
|
if data_len <= 1:
|
||||||
|
return []
|
||||||
|
unwrapped = np.degrees(np.unwrap([d.phase for d in data]))
|
||||||
|
delay_data = []
|
||||||
|
for i, d in enumerate(data):
|
||||||
|
# TODO: Replace with call to RFTools.groupDelay
|
||||||
|
if i == 0:
|
||||||
|
phase_change = unwrapped[1] - unwrapped[i]
|
||||||
|
freq_change = data[1].freq - d.freq
|
||||||
|
elif i == data_len - 1:
|
||||||
|
phase_change = unwrapped[-1] - unwrapped[-2]
|
||||||
|
freq_change = d.freq - data[-2].freq
|
||||||
|
else:
|
||||||
|
phase_change = unwrapped[i+1] - unwrapped[i-1]
|
||||||
|
freq_change = data[i+1].freq - data[i-1].freq
|
||||||
|
delay = (-phase_change / (freq_change * 360)) * 10e8
|
||||||
|
if not self.reflective:
|
||||||
|
delay /= 2
|
||||||
|
delay_data.append(delay)
|
||||||
|
return delay_data
|
||||||
|
|
||||||
def drawValues(self, qp: QtGui.QPainter):
|
def drawValues(self, qp: QtGui.QPainter):
|
||||||
if len(self.data) == 0 and len(self.reference) == 0:
|
if len(self.data) == 0 and len(self.reference) == 0:
|
||||||
return
|
return
|
||||||
|
@ -146,23 +122,19 @@ class GroupDelayChart(FrequencyChart):
|
||||||
self.span = span
|
self.span = span
|
||||||
|
|
||||||
tickcount = math.floor(self.dim.height / 60)
|
tickcount = math.floor(self.dim.height / 60)
|
||||||
|
|
||||||
for i in range(tickcount):
|
for i in range(tickcount):
|
||||||
delay = min_delay + span * i / tickcount
|
delay = min_delay + span * i / tickcount
|
||||||
y = self.topMargin + round((self.maxDelay - delay) / self.span * self.dim.height)
|
y = self.topMargin + round((self.maxDelay - delay) / self.span * self.dim.height)
|
||||||
if delay != min_delay and delay != max_delay:
|
if delay not in {min_delay, max_delay}:
|
||||||
qp.setPen(QtGui.QPen(Chart.color.text))
|
qp.setPen(QtGui.QPen(Chart.color.text))
|
||||||
if delay != 0:
|
# TODO use format class
|
||||||
digits = max(0, min(2, math.floor(3 - math.log10(abs(delay)))))
|
digits = 0 if delay == 0 else max(
|
||||||
if digits == 0:
|
0, min(2, math.floor(3 - math.log10(abs(delay)))))
|
||||||
delaystr = str(round(delay))
|
delaystr = str(round(delay, digits if digits != 0 else None))
|
||||||
else:
|
|
||||||
delaystr = str(round(delay, digits))
|
|
||||||
else:
|
|
||||||
delaystr = "0"
|
|
||||||
qp.drawText(3, y + 3, delaystr)
|
qp.drawText(3, y + 3, delaystr)
|
||||||
qp.setPen(QtGui.QPen(Chart.color.foreground))
|
qp.setPen(QtGui.QPen(Chart.color.foreground))
|
||||||
qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.dim.width, y)
|
qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.dim.width, y)
|
||||||
|
|
||||||
qp.drawLine(self.leftMargin - 5,
|
qp.drawLine(self.leftMargin - 5,
|
||||||
self.topMargin,
|
self.topMargin,
|
||||||
self.leftMargin + self.dim.width,
|
self.leftMargin + self.dim.width,
|
||||||
|
@ -179,66 +151,48 @@ class GroupDelayChart(FrequencyChart):
|
||||||
|
|
||||||
self.drawFrequencyTicks(qp)
|
self.drawFrequencyTicks(qp)
|
||||||
|
|
||||||
color = Chart.color.sweep
|
self.draw_data(qp, Chart.color.sweep,
|
||||||
pen = QtGui.QPen(color)
|
self.data, self.groupDelay)
|
||||||
pen.setWidth(self.dim.point)
|
self.draw_data(qp, Chart.color.reference,
|
||||||
line_pen = QtGui.QPen(color)
|
self.reference, self.groupDelayReference)
|
||||||
line_pen.setWidth(self.dim.line)
|
|
||||||
qp.setPen(pen)
|
|
||||||
for i in range(len(self.data)):
|
|
||||||
x = self.getXPosition(self.data[i])
|
|
||||||
y = self.getYPositionFromDelay(self.groupDelay[i])
|
|
||||||
if self.isPlotable(x, y):
|
|
||||||
qp.drawPoint(int(x), int(y))
|
|
||||||
if self.flag.draw_lines and i > 0:
|
|
||||||
prevx = self.getXPosition(self.data[i - 1])
|
|
||||||
prevy = self.getYPositionFromDelay(self.groupDelay[i - 1])
|
|
||||||
qp.setPen(line_pen)
|
|
||||||
if self.isPlotable(x, y) and self.isPlotable(prevx, prevy):
|
|
||||||
qp.drawLine(x, y, prevx, prevy)
|
|
||||||
elif self.isPlotable(x, y) and not self.isPlotable(prevx, prevy):
|
|
||||||
new_x, new_y = self.getPlotable(x, y, prevx, prevy)
|
|
||||||
qp.drawLine(x, y, new_x, new_y)
|
|
||||||
elif not self.isPlotable(x, y) and self.isPlotable(prevx, prevy):
|
|
||||||
new_x, new_y = self.getPlotable(prevx, prevy, x, y)
|
|
||||||
qp.drawLine(prevx, prevy, new_x, new_y)
|
|
||||||
qp.setPen(pen)
|
|
||||||
|
|
||||||
color = Chart.color.reference
|
|
||||||
pen = QtGui.QPen(color)
|
|
||||||
pen.setWidth(self.dim.point)
|
|
||||||
line_pen = QtGui.QPen(color)
|
|
||||||
line_pen.setWidth(self.dim.line)
|
|
||||||
qp.setPen(pen)
|
|
||||||
for i in range(len(self.reference)):
|
|
||||||
x = self.getXPosition(self.reference[i])
|
|
||||||
y = self.getYPositionFromDelay(self.groupDelayReference[i])
|
|
||||||
if self.isPlotable(x, y):
|
|
||||||
qp.drawPoint(int(x), int(y))
|
|
||||||
if self.flag.draw_lines and i > 0:
|
|
||||||
prevx = self.getXPosition(self.reference[i - 1])
|
|
||||||
prevy = self.getYPositionFromDelay(self.groupDelayReference[i - 1])
|
|
||||||
qp.setPen(line_pen)
|
|
||||||
if self.isPlotable(x, y) and self.isPlotable(prevx, prevy):
|
|
||||||
qp.drawLine(x, y, prevx, prevy)
|
|
||||||
elif self.isPlotable(x, y) and not self.isPlotable(prevx, prevy):
|
|
||||||
new_x, new_y = self.getPlotable(x, y, prevx, prevy)
|
|
||||||
qp.drawLine(x, y, new_x, new_y)
|
|
||||||
elif not self.isPlotable(x, y) and self.isPlotable(prevx, prevy):
|
|
||||||
new_x, new_y = self.getPlotable(prevx, prevy, x, y)
|
|
||||||
qp.drawLine(prevx, prevy, new_x, new_y)
|
|
||||||
qp.setPen(pen)
|
|
||||||
|
|
||||||
self.drawMarkers(qp)
|
self.drawMarkers(qp)
|
||||||
|
|
||||||
|
def draw_data(self, qp: QtGui.QPainter, color: QtGui.QColor,
|
||||||
|
data: List[Datapoint], delay: List[Datapoint]):
|
||||||
|
pen = QtGui.QPen(color)
|
||||||
|
pen.setWidth(self.dim.point)
|
||||||
|
line_pen = QtGui.QPen(color)
|
||||||
|
line_pen.setWidth(self.dim.line)
|
||||||
|
qp.setPen(pen)
|
||||||
|
for i, d in enumerate(data):
|
||||||
|
x = self.getXPosition(d)
|
||||||
|
y = self.getYPositionFromDelay(delay[i])
|
||||||
|
if self.isPlotable(x, y):
|
||||||
|
qp.drawPoint(int(x), int(y))
|
||||||
|
if self.flag.draw_lines and i > 0:
|
||||||
|
prevx = self.getXPosition(data[i - 1])
|
||||||
|
prevy = self.getYPositionFromDelay(delay[i - 1])
|
||||||
|
qp.setPen(line_pen)
|
||||||
|
if self.isPlotable(x, y) and self.isPlotable(prevx, prevy):
|
||||||
|
qp.drawLine(x, y, prevx, prevy)
|
||||||
|
elif self.isPlotable(x, y) and not self.isPlotable(prevx, prevy):
|
||||||
|
new_x, new_y = self.getPlotable(x, y, prevx, prevy)
|
||||||
|
qp.drawLine(x, y, new_x, new_y)
|
||||||
|
elif not self.isPlotable(x, y) and self.isPlotable(prevx, prevy):
|
||||||
|
new_x, new_y = self.getPlotable(prevx, prevy, x, y)
|
||||||
|
qp.drawLine(prevx, prevy, new_x, new_y)
|
||||||
|
qp.setPen(pen)
|
||||||
|
|
||||||
def getYPosition(self, d: Datapoint) -> int:
|
def getYPosition(self, d: Datapoint) -> int:
|
||||||
# TODO: Find a faster way than these expensive "d in self.data" lookups
|
# TODO: Find a faster way than these expensive "d in data" lookups
|
||||||
if d in self.data:
|
try:
|
||||||
delay = self.groupDelay[self.data.index(d)]
|
delay = self.groupDelay[self.data.index(d)]
|
||||||
elif d in self.reference:
|
except ValueError:
|
||||||
delay = self.groupDelayReference[self.reference.index(d)]
|
try:
|
||||||
else:
|
delay = self.groupDelayReference[self.reference.index(d)]
|
||||||
delay = 0
|
except ValueError:
|
||||||
|
delay = 0
|
||||||
return self.getYPositionFromDelay(delay)
|
return self.getYPositionFromDelay(delay)
|
||||||
|
|
||||||
def getYPositionFromDelay(self, delay: float):
|
def getYPositionFromDelay(self, delay: float):
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
# NanoVNASaver
|
# NanoVNASaver
|
||||||
#
|
#
|
||||||
# A python program to view and export Touchstone data from a NanoVNA
|
# A python program to view and export Touchstone data from a NanoVNA
|
||||||
# Copyright (C) 2019, 2020 Rune B. Broberg
|
# Copyright (C) 2019, 2020 Rune B. Broberg
|
||||||
# Copyright (C) 2020,2021 NanoVNA-Saver Authors
|
# Copyright (C) 2020ff NanoVNA-Saver Authors
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,12 +16,9 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import math
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from PyQt5 import QtGui, QtCore
|
from PyQt5 import QtGui, QtCore
|
||||||
|
|
||||||
from NanoVNASaver.RFTools import Datapoint
|
|
||||||
from NanoVNASaver.Charts.Chart import Chart
|
from NanoVNASaver.Charts.Chart import Chart
|
||||||
from NanoVNASaver.Charts.Square import SquareChart
|
from NanoVNASaver.Charts.Square import SquareChart
|
||||||
|
|
||||||
|
@ -29,129 +26,32 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class PolarChart(SquareChart):
|
class PolarChart(SquareChart):
|
||||||
def __init__(self, name=""):
|
|
||||||
super().__init__(name)
|
|
||||||
self.dim.width = 250
|
|
||||||
self.dim.height = 250
|
|
||||||
|
|
||||||
self.setMinimumSize(self.dim.width + 40, self.dim.height + 40)
|
|
||||||
pal = QtGui.QPalette()
|
|
||||||
pal.setColor(QtGui.QPalette.Background, Chart.color.background)
|
|
||||||
self.setPalette(pal)
|
|
||||||
self.setAutoFillBackground(True)
|
|
||||||
|
|
||||||
def paintEvent(self, _: QtGui.QPaintEvent) -> None:
|
|
||||||
qp = QtGui.QPainter(self)
|
|
||||||
self.drawChart(qp)
|
|
||||||
self.drawValues(qp)
|
|
||||||
qp.end()
|
|
||||||
|
|
||||||
def drawChart(self, qp: QtGui.QPainter):
|
def drawChart(self, qp: QtGui.QPainter):
|
||||||
centerX = int(self.width()/2)
|
center_x = int(self.width()/2)
|
||||||
centerY = int(self.height()/2)
|
center_y = int(self.height()/2)
|
||||||
|
width_2 = int(self.dim.width / 2)
|
||||||
|
height_2 = int(self.dim.height / 2)
|
||||||
|
width_45 = width_2 * 0.7071
|
||||||
|
height_45 = height_2 * 0.7071
|
||||||
|
|
||||||
qp.setPen(QtGui.QPen(Chart.color.text))
|
qp.setPen(QtGui.QPen(Chart.color.text))
|
||||||
qp.drawText(3, 15, self.name)
|
qp.drawText(3, 15, self.name)
|
||||||
qp.setPen(QtGui.QPen(Chart.color.foreground))
|
qp.setPen(QtGui.QPen(Chart.color.foreground))
|
||||||
qp.drawEllipse(QtCore.QPoint(centerX, centerY),
|
|
||||||
int(self.dim.width / 2),
|
qp.drawEllipse(QtCore.QPoint(center_x, center_y), width_2, height_2)
|
||||||
int(self.dim.height / 2))
|
qp.drawEllipse(QtCore.QPoint(center_x, center_y),
|
||||||
qp.drawEllipse(QtCore.QPoint(centerX, centerY),
|
width_2 // 2, height_2 // 2)
|
||||||
int(self.dim.width / 4),
|
|
||||||
int(self.dim.height / 4))
|
qp.drawLine(center_x - width_2, center_y, center_x + width_2, center_y)
|
||||||
qp.drawLine(centerX - int(self.dim.width / 2), centerY,
|
qp.drawLine(center_x, center_y - height_2,
|
||||||
centerX + int(self.dim.width / 2), centerY)
|
center_x, center_y + height_2)
|
||||||
qp.drawLine(centerX, centerY - int(self.dim.height / 2),
|
|
||||||
centerX, centerY + int(self.dim.height / 2))
|
qp.drawLine(center_x + width_45, center_y + height_45,
|
||||||
qp.drawLine(centerX + int(self.dim.height / 2 * math.sin(math.pi / 4)),
|
center_x - width_45, center_y - height_45)
|
||||||
centerY + int(self.dim.height / 2 * math.sin(math.pi / 4)),
|
qp.drawLine(center_x + width_45, center_y - height_45,
|
||||||
centerX - int(self.dim.height / 2 * math.sin(math.pi / 4)),
|
center_x - width_45, center_y + height_45)
|
||||||
centerY - int(self.dim.height / 2 * math.sin(math.pi / 4)))
|
|
||||||
qp.drawLine(centerX + int(self.dim.height / 2 * math.sin(math.pi / 4)),
|
|
||||||
centerY - int(self.dim.height / 2 * math.sin(math.pi / 4)),
|
|
||||||
centerX - int(self.dim.height / 2 * math.sin(math.pi / 4)),
|
|
||||||
centerY + int(self.dim.height / 2 * math.sin(math.pi / 4)))
|
|
||||||
self.drawTitle(qp)
|
self.drawTitle(qp)
|
||||||
|
|
||||||
def drawValues(self, qp: QtGui.QPainter):
|
def zoomTo(self, x1, y1, x2, y2):
|
||||||
if len(self.data) == 0 and len(self.reference) == 0:
|
raise NotImplementedError()
|
||||||
return
|
|
||||||
pen = QtGui.QPen(Chart.color.sweep)
|
|
||||||
pen.setWidth(self.dim.point)
|
|
||||||
line_pen = QtGui.QPen(Chart.color.sweep)
|
|
||||||
line_pen.setWidth(self.dim.line)
|
|
||||||
qp.setPen(pen)
|
|
||||||
for i in range(len(self.data)):
|
|
||||||
x = self.getXPosition(self.data[i])
|
|
||||||
y = self.height()/2 + self.data[i].im * -1 * self.dim.height/2
|
|
||||||
qp.drawPoint(int(x), int(y))
|
|
||||||
if self.flag.draw_lines and i > 0:
|
|
||||||
prevx = self.getXPosition(self.data[i-1])
|
|
||||||
prevy = self.height() / 2 + self.data[i-1].im * -1 * self.dim.height / 2
|
|
||||||
qp.setPen(line_pen)
|
|
||||||
qp.drawLine(x, y, prevx, prevy)
|
|
||||||
qp.setPen(pen)
|
|
||||||
pen.setColor(Chart.color.reference)
|
|
||||||
line_pen.setColor(Chart.color.reference)
|
|
||||||
qp.setPen(pen)
|
|
||||||
if len(self.data) > 0:
|
|
||||||
fstart = self.data[0].freq
|
|
||||||
fstop = self.data[len(self.data) - 1].freq
|
|
||||||
else:
|
|
||||||
fstart = self.reference[0].freq
|
|
||||||
fstop = self.reference[len(self.reference) - 1].freq
|
|
||||||
for i in range(len(self.reference)):
|
|
||||||
data = self.reference[i]
|
|
||||||
if data.freq < fstart or data.freq > fstop:
|
|
||||||
continue
|
|
||||||
x = self.getXPosition(self.reference[i])
|
|
||||||
y = self.height()/2 + data.im * -1 * self.dim.height/2
|
|
||||||
qp.drawPoint(int(x), int(y))
|
|
||||||
if self.flag.draw_lines and i > 0:
|
|
||||||
prevx = self.getXPosition(self.reference[i-1])
|
|
||||||
prevy = self.height() / 2 + self.reference[i-1].im * -1 * self.dim.height / 2
|
|
||||||
qp.setPen(line_pen)
|
|
||||||
qp.drawLine(x, y, prevx, prevy)
|
|
||||||
qp.setPen(pen)
|
|
||||||
# Now draw the markers
|
|
||||||
for m in self.markers:
|
|
||||||
if m.location != -1 and m.location < len(self.data):
|
|
||||||
x = self.getXPosition(self.data[m.location])
|
|
||||||
y = self.height() / 2 + self.data[m.location].im * -1 * self.dim.height / 2
|
|
||||||
self.drawMarker(x, y, qp, m.color, self.markers.index(m)+1)
|
|
||||||
|
|
||||||
def getXPosition(self, d: Datapoint) -> int:
|
|
||||||
return self.width()/2 + d.re * self.dim.width/2
|
|
||||||
|
|
||||||
def getYPosition(self, d: Datapoint) -> int:
|
|
||||||
return self.height()/2 + d.im * -1 * self.dim.height/2
|
|
||||||
|
|
||||||
def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None:
|
|
||||||
if a0.buttons() == QtCore.Qt.RightButton:
|
|
||||||
a0.ignore()
|
|
||||||
return
|
|
||||||
x = a0.x()
|
|
||||||
y = a0.y()
|
|
||||||
absx = x - (self.width() - self.dim.width) / 2
|
|
||||||
absy = y - (self.height() - self.dim.height) / 2
|
|
||||||
if absx < 0 or absx > self.dim.width or absy < 0 or absy > self.dim.height \
|
|
||||||
or len(self.data) == len(self.reference) == 0:
|
|
||||||
a0.ignore()
|
|
||||||
return
|
|
||||||
a0.accept()
|
|
||||||
|
|
||||||
if len(self.data) > 0:
|
|
||||||
target = self.data
|
|
||||||
else:
|
|
||||||
target = self.reference
|
|
||||||
positions = []
|
|
||||||
for d in target:
|
|
||||||
thisx = self.width() / 2 + d.re * self.dim.width / 2
|
|
||||||
thisy = self.height() / 2 + d.im * -1 * self.dim.height / 2
|
|
||||||
positions.append(math.sqrt((x - thisx)**2 + (y - thisy)**2))
|
|
||||||
|
|
||||||
minimum_position = positions.index(min(positions))
|
|
||||||
m = self.getActiveMarker()
|
|
||||||
if m is not None:
|
|
||||||
m.setFrequency(str(round(target[minimum_position].freq)))
|
|
||||||
m.frequencyInput.setText(str(round(target[minimum_position].freq)))
|
|
||||||
return
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
# NanoVNASaver
|
# NanoVNASaver
|
||||||
#
|
#
|
||||||
# A python program to view and export Touchstone data from a NanoVNA
|
# A python program to view and export Touchstone data from a NanoVNA
|
||||||
# Copyright (C) 2019, 2020 Rune B. Broberg
|
# Copyright (C) 2019, 2020 Rune B. Broberg
|
||||||
# Copyright (C) 2020,2021 NanoVNA-Saver Authors
|
# Copyright (C) 2020ff NanoVNA-Saver Authors
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,12 +16,9 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import math
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from PyQt5 import QtGui, QtCore
|
from PyQt5 import QtGui, QtCore
|
||||||
|
|
||||||
from NanoVNASaver.RFTools import Datapoint
|
|
||||||
from NanoVNASaver.Charts.Chart import Chart
|
from NanoVNASaver.Charts.Chart import Chart
|
||||||
from NanoVNASaver.Charts.Square import SquareChart
|
from NanoVNASaver.Charts.Square import SquareChart
|
||||||
|
|
||||||
|
@ -29,25 +26,7 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class SmithChart(SquareChart):
|
class SmithChart(SquareChart):
|
||||||
def __init__(self, name=""):
|
def drawChart(self, qp: QtGui.QPainter) -> None:
|
||||||
super().__init__(name)
|
|
||||||
self.dim.width = 250
|
|
||||||
self.dim.height = 250
|
|
||||||
|
|
||||||
self.setMinimumSize(self.dim.width + 40, self.dim.height + 40)
|
|
||||||
pal = QtGui.QPalette()
|
|
||||||
pal.setColor(QtGui.QPalette.Background, Chart.color.background)
|
|
||||||
self.setPalette(pal)
|
|
||||||
self.setAutoFillBackground(True)
|
|
||||||
|
|
||||||
def paintEvent(self, _: QtGui.QPaintEvent) -> None:
|
|
||||||
qp = QtGui.QPainter(self)
|
|
||||||
# qp.begin(self) # Apparently not needed?
|
|
||||||
self.drawSmithChart(qp)
|
|
||||||
self.drawValues(qp)
|
|
||||||
qp.end()
|
|
||||||
|
|
||||||
def drawSmithChart(self, qp: QtGui.QPainter):
|
|
||||||
centerX = int(self.width()/2)
|
centerX = int(self.width()/2)
|
||||||
centerY = int(self.height()/2)
|
centerY = int(self.height()/2)
|
||||||
qp.setPen(QtGui.QPen(Chart.color.text))
|
qp.setPen(QtGui.QPen(Chart.color.text))
|
||||||
|
@ -116,91 +95,5 @@ class SmithChart(SquareChart):
|
||||||
QtCore.QRect(centerX - 50, centerY - 4 + r, 100, 20),
|
QtCore.QRect(centerX - 50, centerY - 4 + r, 100, 20),
|
||||||
QtCore.Qt.AlignCenter, str(swr))
|
QtCore.Qt.AlignCenter, str(swr))
|
||||||
|
|
||||||
def drawValues(self, qp: QtGui.QPainter):
|
def zoomTo(self, x1, y1, x2, y2):
|
||||||
if len(self.data) == 0 and len(self.reference) == 0:
|
raise NotImplementedError()
|
||||||
return
|
|
||||||
pen = QtGui.QPen(Chart.color.sweep)
|
|
||||||
pen.setWidth(self.dim.point)
|
|
||||||
line_pen = QtGui.QPen(Chart.color.sweep)
|
|
||||||
line_pen.setWidth(self.dim.line)
|
|
||||||
highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
|
|
||||||
highlighter.setWidth(1)
|
|
||||||
qp.setPen(pen)
|
|
||||||
for i in range(len(self.data)):
|
|
||||||
x = self.getXPosition(self.data[i])
|
|
||||||
y = int(self.height()/2 + self.data[i].im * -1 * self.dim.height/2)
|
|
||||||
qp.drawPoint(x, y)
|
|
||||||
if self.flag.draw_lines and i > 0:
|
|
||||||
prevx = self.getXPosition(self.data[i-1])
|
|
||||||
prevy = int(self.height() / 2 + self.data[i-1].im * -1 * self.dim.height / 2)
|
|
||||||
qp.setPen(line_pen)
|
|
||||||
qp.drawLine(x, y, prevx, prevy)
|
|
||||||
qp.setPen(pen)
|
|
||||||
pen.setColor(Chart.color.reference)
|
|
||||||
line_pen.setColor(Chart.color.reference)
|
|
||||||
qp.setPen(pen)
|
|
||||||
if len(self.data) > 0:
|
|
||||||
fstart = self.data[0].freq
|
|
||||||
fstop = self.data[len(self.data)-1].freq
|
|
||||||
else:
|
|
||||||
fstart = self.reference[0].freq
|
|
||||||
fstop = self.reference[len(self.reference)-1].freq
|
|
||||||
for i in range(len(self.reference)):
|
|
||||||
data = self.reference[i]
|
|
||||||
if data.freq < fstart or data.freq > fstop:
|
|
||||||
continue
|
|
||||||
x = self.getXPosition(data)
|
|
||||||
y = int(self.height()/2 + data.im * -1 * self.dim.height/2)
|
|
||||||
qp.drawPoint(x, y)
|
|
||||||
if self.flag.draw_lines and i > 0:
|
|
||||||
prevx = self.getXPosition(self.reference[i-1])
|
|
||||||
prevy = int(self.height() / 2 + self.reference[i-1].im * -1 * self.dim.height / 2)
|
|
||||||
qp.setPen(line_pen)
|
|
||||||
qp.drawLine(x, y, prevx, prevy)
|
|
||||||
qp.setPen(pen)
|
|
||||||
# Now draw the markers
|
|
||||||
for m in self.markers:
|
|
||||||
if m.location != -1:
|
|
||||||
x = self.getXPosition(self.data[m.location])
|
|
||||||
y = self.height() / 2 + self.data[m.location].im * -1 * self.dim.height / 2
|
|
||||||
self.drawMarker(x, y, qp, m.color, self.markers.index(m)+1)
|
|
||||||
|
|
||||||
def getXPosition(self, d: Datapoint) -> int:
|
|
||||||
return int(self.width()/2 + d.re * self.dim.width/2)
|
|
||||||
|
|
||||||
def getYPosition(self, d: Datapoint) -> int:
|
|
||||||
return int(self.height()/2 + d.im * -1 * self.dim.height/2)
|
|
||||||
|
|
||||||
def heightForWidth(self, a0: int) -> int:
|
|
||||||
return a0
|
|
||||||
|
|
||||||
def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None:
|
|
||||||
if a0.buttons() == QtCore.Qt.RightButton:
|
|
||||||
a0.ignore()
|
|
||||||
return
|
|
||||||
x = a0.x()
|
|
||||||
y = a0.y()
|
|
||||||
absx = x - (self.width() - self.dim.width) / 2
|
|
||||||
absy = y - (self.height() - self.dim.height) / 2
|
|
||||||
if absx < 0 or absx > self.dim.width or absy < 0 or absy > self.dim.height \
|
|
||||||
or len(self.data) == len(self.reference) == 0:
|
|
||||||
a0.ignore()
|
|
||||||
return
|
|
||||||
a0.accept()
|
|
||||||
|
|
||||||
if len(self.data) > 0:
|
|
||||||
target = self.data
|
|
||||||
else:
|
|
||||||
target = self.reference
|
|
||||||
positions = []
|
|
||||||
for d in target:
|
|
||||||
thisx = self.width() / 2 + d.re * self.dim.width / 2
|
|
||||||
thisy = self.height() / 2 + d.im * -1 * self.dim.height / 2
|
|
||||||
positions.append(math.sqrt((x - thisx)**2 + (y - thisy)**2))
|
|
||||||
|
|
||||||
minimum_position = positions.index(min(positions))
|
|
||||||
m = self.getActiveMarker()
|
|
||||||
if m is not None:
|
|
||||||
m.setFrequency(str(round(target[minimum_position].freq)))
|
|
||||||
m.frequencyInput.setText(str(round(target[minimum_position].freq)))
|
|
||||||
return
|
|
||||||
|
|
|
@ -17,23 +17,81 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
|
import math
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from PyQt5 import QtGui, QtCore
|
||||||
from PyQt5 import QtWidgets, QtGui
|
from PyQt5 import QtWidgets, QtGui
|
||||||
|
|
||||||
from NanoVNASaver.Charts.Chart import Chart
|
from NanoVNASaver.Charts.Chart import Chart
|
||||||
|
from NanoVNASaver.RFTools import Datapoint
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class SquareChart(Chart):
|
class SquareChart(Chart):
|
||||||
def __init__(self, name):
|
def __init__(self, name=''):
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
sizepolicy = QtWidgets.QSizePolicy(
|
sizepolicy = QtWidgets.QSizePolicy(
|
||||||
QtWidgets.QSizePolicy.Fixed,
|
QtWidgets.QSizePolicy.Fixed,
|
||||||
QtWidgets.QSizePolicy.MinimumExpanding)
|
QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
self.setSizePolicy(sizepolicy)
|
self.setSizePolicy(sizepolicy)
|
||||||
self.dim.width = self.width()-40
|
self.dim.width = 250
|
||||||
self.dim.height = self.height()-40
|
self.dim.height = 250
|
||||||
|
self.setMinimumSize(self.dim.width + 40, self.dim.height + 40)
|
||||||
|
|
||||||
|
pal = QtGui.QPalette()
|
||||||
|
pal.setColor(QtGui.QPalette.Background, Chart.color.background)
|
||||||
|
self.setPalette(pal)
|
||||||
|
self.setAutoFillBackground(True)
|
||||||
|
|
||||||
|
def paintEvent(self, _: QtGui.QPaintEvent) -> None:
|
||||||
|
qp = QtGui.QPainter(self)
|
||||||
|
self.drawChart(qp)
|
||||||
|
self.drawValues(qp)
|
||||||
|
qp.end()
|
||||||
|
|
||||||
|
def drawChart(self, qp: QtGui.QPainter) -> None:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def draw_data(self, qp: QtGui.QPainter, color: QtGui.QColor,
|
||||||
|
data: List[Datapoint], fstart: int=0, fstop: int=0):
|
||||||
|
if not data:
|
||||||
|
return
|
||||||
|
fstop = fstop or data[-1].freq
|
||||||
|
pen = QtGui.QPen(color)
|
||||||
|
pen.setWidth(self.dim.point)
|
||||||
|
line_pen = QtGui.QPen(color)
|
||||||
|
line_pen.setWidth(self.dim.line)
|
||||||
|
|
||||||
|
qp.setPen(pen)
|
||||||
|
prev_x = self.getXPosition(data[0])
|
||||||
|
prev_y = int(self.height() / 2 + data[0].im * -1 * self.dim.height / 2)
|
||||||
|
for i, d in enumerate(data):
|
||||||
|
x = self.getXPosition(d)
|
||||||
|
y = int(self.height()/2 + d.im * -1 * self.dim.height/2)
|
||||||
|
if d.freq > fstart and d.freq < fstop:
|
||||||
|
qp.drawPoint(x, y)
|
||||||
|
if self.flag.draw_lines and i > 0:
|
||||||
|
qp.setPen(line_pen)
|
||||||
|
qp.drawLine(x, y, prev_x, prev_y)
|
||||||
|
qp.setPen(pen)
|
||||||
|
prev_x, prev_y = x, y
|
||||||
|
|
||||||
|
def drawValues(self, qp: QtGui.QPainter):
|
||||||
|
if not (self.data or self.reference):
|
||||||
|
return
|
||||||
|
self.draw_data(qp, Chart.color.sweep, self.data)
|
||||||
|
|
||||||
|
fstart = self.data[0].freq if self.data else 0
|
||||||
|
fstop = self.data[-1].freq if self.data else 0
|
||||||
|
self.draw_data(qp, Chart.color.reference, self.reference, fstart, fstop)
|
||||||
|
|
||||||
|
for m in self.markers:
|
||||||
|
if m.location != -1 and m.location < len(self.data):
|
||||||
|
x = self.getXPosition(self.data[m.location])
|
||||||
|
y = self.height() / 2 + self.data[m.location].im * -1 * self.dim.height / 2
|
||||||
|
self.drawMarker(x, y, qp, m.color, self.markers.index(m)+1)
|
||||||
|
|
||||||
def resizeEvent(self, a0: QtGui.QResizeEvent) -> None:
|
def resizeEvent(self, a0: QtGui.QResizeEvent) -> None:
|
||||||
if not self.flag.is_popout:
|
if not self.flag.is_popout:
|
||||||
|
@ -44,3 +102,44 @@ class SquareChart(Chart):
|
||||||
min_dimension = min(a0.size().height(), a0.size().width())
|
min_dimension = min(a0.size().height(), a0.size().width())
|
||||||
self.dim.width = self.dim.height = min_dimension - 40
|
self.dim.width = self.dim.height = min_dimension - 40
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
def mouseMoveEvent(self, a0: QtGui.QMouseEvent):
|
||||||
|
if a0.buttons() == QtCore.Qt.RightButton:
|
||||||
|
a0.ignore()
|
||||||
|
return
|
||||||
|
|
||||||
|
x = a0.x()
|
||||||
|
y = a0.y()
|
||||||
|
absx = x - (self.width() - self.dim.width) / 2
|
||||||
|
absy = y - (self.height() - self.dim.height) / 2
|
||||||
|
if absx < 0 or absx > self.dim.width or absy < 0 or absy > self.dim.height \
|
||||||
|
or len(self.data) == len(self.reference) == 0:
|
||||||
|
a0.ignore()
|
||||||
|
return
|
||||||
|
a0.accept()
|
||||||
|
|
||||||
|
target = self.data or self.reference
|
||||||
|
positions = []
|
||||||
|
|
||||||
|
dim_x_2 = self.dim.width / 2
|
||||||
|
dim_y_2 = self.dim.height / 2
|
||||||
|
width_2 = self.width() / 2
|
||||||
|
height_2 = self.height() / 2
|
||||||
|
|
||||||
|
positions = [
|
||||||
|
math.sqrt(
|
||||||
|
(x - (width_2 + d.re * dim_x_2))**2 +
|
||||||
|
(y - (height_2 - d.im * dim_y_2))**2)
|
||||||
|
for d in target
|
||||||
|
]
|
||||||
|
|
||||||
|
minimum_position = positions.index(min(positions))
|
||||||
|
if m := self.getActiveMarker():
|
||||||
|
m.setFrequency(str(round(target[minimum_position].freq)))
|
||||||
|
m.frequencyInput.setText(str(round(target[minimum_position].freq)))
|
||||||
|
|
||||||
|
def getXPosition(self, d: Datapoint) -> int:
|
||||||
|
return int(self.width()/2 + d.re * self.dim.width/2)
|
||||||
|
|
||||||
|
def getYPosition(self, d: Datapoint) -> int:
|
||||||
|
return int(self.height()/2 + d.im * -1 * self.dim.height/2)
|
||||||
|
|
|
@ -34,6 +34,7 @@ from NanoVNASaver.Hardware.NanoVNA_H4 import NanoVNA_H4
|
||||||
from NanoVNASaver.Hardware.NanoVNA_V2 import NanoVNA_V2
|
from NanoVNASaver.Hardware.NanoVNA_V2 import NanoVNA_V2
|
||||||
from NanoVNASaver.Hardware.TinySA import TinySA
|
from NanoVNASaver.Hardware.TinySA import TinySA
|
||||||
from NanoVNASaver.Hardware.Serial import drain_serial, Interface
|
from NanoVNASaver.Hardware.Serial import drain_serial, Interface
|
||||||
|
from NanoVNASaver.Hardware.VNA import VNA
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
|
@ -41,7 +41,7 @@ def drain_serial(serial_port: serial.Serial):
|
||||||
class Interface(serial.Serial):
|
class Interface(serial.Serial):
|
||||||
def __init__(self, interface_type: str, comment, *args, **kwargs):
|
def __init__(self, interface_type: str, comment, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
assert interface_type in ('serial', 'usb', 'bt', 'network')
|
assert interface_type in {'serial', 'usb', 'bt', 'network'}
|
||||||
self.type = interface_type
|
self.type = interface_type
|
||||||
self.comment = comment
|
self.comment = comment
|
||||||
self.port = None
|
self.port = None
|
||||||
|
|
|
@ -38,7 +38,7 @@ class TinySA(VNA):
|
||||||
|
|
||||||
def __init__(self, iface: Interface):
|
def __init__(self, iface: Interface):
|
||||||
super().__init__(iface)
|
super().__init__(iface)
|
||||||
self.features = set(('Screenshots',))
|
self.features = {'Screenshots'}
|
||||||
logger.debug("Setting initial start,stop")
|
logger.debug("Setting initial start,stop")
|
||||||
self.start, self.stop = self._get_running_frequencies()
|
self.start, self.stop = self._get_running_frequencies()
|
||||||
self.sweep_max_freq_Hz = 950e6
|
self.sweep_max_freq_Hz = 950e6
|
||||||
|
|
|
@ -77,15 +77,8 @@ class DeltaMarker(Marker):
|
||||||
RFTools.impedance_to_inductance(imp_p_b, s11_b.freq)-
|
RFTools.impedance_to_inductance(imp_p_b, s11_b.freq)-
|
||||||
RFTools.impedance_to_inductance(imp_p_a, s11_a.freq))
|
RFTools.impedance_to_inductance(imp_p_a, s11_a.freq))
|
||||||
|
|
||||||
if imp.imag < 0:
|
x_str = cap_str if imp.imag < 0 else ind_str
|
||||||
x_str = cap_str
|
x_p_str = cap_p_str if imp_p.imag < 0 else ind_p_str
|
||||||
else:
|
|
||||||
x_str = ind_str
|
|
||||||
|
|
||||||
if imp_p.imag < 0:
|
|
||||||
x_p_str = cap_p_str
|
|
||||||
else:
|
|
||||||
x_p_str = ind_p_str
|
|
||||||
|
|
||||||
self.label['actualfreq'].setText(
|
self.label['actualfreq'].setText(
|
||||||
format_frequency_space(s11_b.freq - s11_a.freq))
|
format_frequency_space(s11_b.freq - s11_a.freq))
|
||||||
|
|
|
@ -84,9 +84,9 @@ class Value():
|
||||||
if s21:
|
if s21:
|
||||||
s21 = [s21[0], ] + s21
|
s21 = [s21[0], ] + s21
|
||||||
if index == len(s11):
|
if index == len(s11):
|
||||||
s11 = s11 + [s11[-1], ]
|
s11 += [s11[-1], ]
|
||||||
if s21:
|
if s21:
|
||||||
s21 = s21 + [s21[-1], ]
|
s21 += [s21[-1], ]
|
||||||
self.freq = s11[1].freq
|
self.freq = s11[1].freq
|
||||||
self.s11 = s11[index-1:index+2]
|
self.s11 = s11[index-1:index+2]
|
||||||
if s21:
|
if s21:
|
||||||
|
|
|
@ -271,7 +271,7 @@ class Marker(QtCore.QObject, Value):
|
||||||
self.location = i-1
|
self.location = i-1
|
||||||
if i < datasize:
|
if i < datasize:
|
||||||
self.frequencyInput.nextFrequency = item.freq
|
self.frequencyInput.nextFrequency = item.freq
|
||||||
if (i-2) >= 0:
|
if i >= 2:
|
||||||
self.frequencyInput.previousFrequency = data[i-2].freq
|
self.frequencyInput.previousFrequency = data[i-2].freq
|
||||||
return
|
return
|
||||||
# If we still didn't find a best spot, it was the last value
|
# If we still didn't find a best spot, it was the last value
|
||||||
|
@ -317,15 +317,8 @@ class Marker(QtCore.QObject, Value):
|
||||||
ind_p_str = format_inductance(
|
ind_p_str = format_inductance(
|
||||||
RFTools.impedance_to_inductance(imp_p, _s11.freq))
|
RFTools.impedance_to_inductance(imp_p, _s11.freq))
|
||||||
|
|
||||||
if imp.imag < 0:
|
x_str = cap_str if imp.imag < 0 else ind_str
|
||||||
x_str = cap_str
|
x_p_str = cap_p_str if imp_p.imag < 0 else ind_p_str
|
||||||
else:
|
|
||||||
x_str = ind_str
|
|
||||||
|
|
||||||
if imp_p.imag < 0:
|
|
||||||
x_p_str = cap_p_str
|
|
||||||
else:
|
|
||||||
x_p_str = ind_p_str
|
|
||||||
|
|
||||||
self.label['actualfreq'].setText(format_frequency_space(_s11.freq))
|
self.label['actualfreq'].setText(format_frequency_space(_s11.freq))
|
||||||
self.label['lambda'].setText(format_wavelength(_s11.wavelength))
|
self.label['lambda'].setText(format_wavelength(_s11.wavelength))
|
||||||
|
@ -342,7 +335,9 @@ class Marker(QtCore.QObject, Value):
|
||||||
self.label['s11mag'].setText(format_magnitude(abs(_s11.z)))
|
self.label['s11mag'].setText(format_magnitude(abs(_s11.z)))
|
||||||
self.label['s11phase'].setText(format_phase(_s11.phase))
|
self.label['s11phase'].setText(format_phase(_s11.phase))
|
||||||
self.label['s11polar'].setText(
|
self.label['s11polar'].setText(
|
||||||
str(round(abs(_s11.z), 2)) + "∠" + format_phase(_s11.phase))
|
f'{str(round(abs(_s11.z), 2))}∠{format_phase(_s11.phase)}'
|
||||||
|
)
|
||||||
|
|
||||||
self.label['s11q'].setText(format_q_factor(_s11.qFactor()))
|
self.label['s11q'].setText(format_q_factor(_s11.qFactor()))
|
||||||
self.label['s11z'].setText(format_resistance(abs(imp)))
|
self.label['s11z'].setText(format_resistance(abs(imp)))
|
||||||
self.label['serc'].setText(cap_str)
|
self.label['serc'].setText(cap_str)
|
||||||
|
@ -359,7 +354,9 @@ class Marker(QtCore.QObject, Value):
|
||||||
self.label['s21mag'].setText(format_magnitude(abs(_s21.z)))
|
self.label['s21mag'].setText(format_magnitude(abs(_s21.z)))
|
||||||
self.label['s21phase'].setText(format_phase(_s21.phase))
|
self.label['s21phase'].setText(format_phase(_s21.phase))
|
||||||
self.label['s21polar'].setText(
|
self.label['s21polar'].setText(
|
||||||
str(round(abs(_s21.z), 2)) + "∠" + format_phase(_s21.phase))
|
f'{str(round(abs(_s21.z), 2))}∠{format_phase(_s21.phase)}'
|
||||||
|
)
|
||||||
|
|
||||||
self.label['s21magshunt'].setText(
|
self.label['s21magshunt'].setText(
|
||||||
format_magnitude(abs(_s21.shuntImpedance())))
|
format_magnitude(abs(_s21.shuntImpedance())))
|
||||||
self.label['s21magseries'].setText(
|
self.label['s21magseries'].setText(
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
import contextlib
|
||||||
import logging
|
import logging
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
|
@ -132,10 +133,8 @@ class BandsModel(QtCore.QAbstractTableModel):
|
||||||
orientation: QtCore.Qt.Orientation, role: int = ...):
|
orientation: QtCore.Qt.Orientation, role: int = ...):
|
||||||
if (role == QtCore.Qt.DisplayRole and
|
if (role == QtCore.Qt.DisplayRole and
|
||||||
orientation == QtCore.Qt.Horizontal):
|
orientation == QtCore.Qt.Horizontal):
|
||||||
try:
|
with contextlib.suppress(IndexError):
|
||||||
return _HEADER_DATA[section]
|
return _HEADER_DATA[section]
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def flags(self, index: QModelIndex) -> QtCore.Qt.ItemFlags:
|
def flags(self, index: QModelIndex) -> QtCore.Qt.ItemFlags:
|
||||||
|
|
|
@ -192,12 +192,10 @@ class SweepWorker(QtCore.QRunnable):
|
||||||
if not self.app.calibration.isCalculated:
|
if not self.app.calibration.isCalculated:
|
||||||
data11 = raw_data11.copy()
|
data11 = raw_data11.copy()
|
||||||
data21 = raw_data21.copy()
|
data21 = raw_data21.copy()
|
||||||
|
elif self.app.calibration.isValid1Port():
|
||||||
|
data11.extend(self.app.calibration.correct11(dp) for dp in raw_data11)
|
||||||
else:
|
else:
|
||||||
if self.app.calibration.isValid1Port():
|
data11 = raw_data11.copy()
|
||||||
for dp in raw_data11:
|
|
||||||
data11.append(self.app.calibration.correct11(dp))
|
|
||||||
else:
|
|
||||||
data11 = raw_data11.copy()
|
|
||||||
|
|
||||||
if self.app.calibration.isValid2Port():
|
if self.app.calibration.isValid2Port():
|
||||||
for counter, dp in enumerate(raw_data21):
|
for counter, dp in enumerate(raw_data21):
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
app-id: io.github.zarath.nanovna-saver
|
||||||
|
runtime: org.kde.Platform
|
||||||
|
runtime-version: '5.15-21.08'
|
||||||
|
sdk: org.kde.Sdk
|
||||||
|
command: /app/bin/NanoVNASaver
|
||||||
|
build-options:
|
||||||
|
build-args:
|
||||||
|
- --share=network
|
||||||
|
modules:
|
||||||
|
- name: nanonva-saver
|
||||||
|
buildsystem: simple
|
||||||
|
build-commands:
|
||||||
|
- pip3 install --prefix=/app wheel
|
||||||
|
- pip3 install --prefix=/app git+https://github.com/NanoVNA-Saver/nanovna-saver.git
|
||||||
|
finish-args:
|
||||||
|
# X11 + XShm access
|
||||||
|
- --share=ipc
|
||||||
|
- --socket=x11
|
||||||
|
# Wayland access
|
||||||
|
- --socket=wayland
|
||||||
|
# Needs access to NanoVNAs
|
||||||
|
- --device=all
|
||||||
|
# Needs to save files locally
|
||||||
|
- --filesystem=xdg-documents
|
|
@ -13,7 +13,7 @@ packages = find_namespace:
|
||||||
install_requires=
|
install_requires=
|
||||||
pyserial>=3.5
|
pyserial>=3.5
|
||||||
PyQt5>=5.15.0
|
PyQt5>=5.15.0
|
||||||
numpy>=1.21.1,<1.22.0
|
numpy>=1.21.1
|
||||||
scipy>=1.7.1
|
scipy>=1.7.1
|
||||||
Cython>=0.29.24
|
Cython>=0.29.24
|
||||||
python_requires = >=3.8, <4
|
python_requires = >=3.8, <4
|
||||||
|
|
Ładowanie…
Reference in New Issue