diff --git a/.gitignore b/.gitignore
index 77b21b3..998ca08 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,4 +11,5 @@
settings.json
.gitignore
.coverage
-/nanovna-saver.exe.spec
\ No newline at end of file
+.flatpak-builder
+/nanovna-saver.exe.spec
diff --git a/NanoVNASaver/Analysis/AntennaAnalysis.py b/NanoVNASaver/Analysis/AntennaAnalysis.py
index c69078d..bbc97de 100644
--- a/NanoVNASaver/Analysis/AntennaAnalysis.py
+++ b/NanoVNASaver/Analysis/AntennaAnalysis.py
@@ -70,23 +70,21 @@ class MagLoopAnalysis(VSWRAnalysis):
if len(self.minimums) == 1:
m = self.minimums[0]
start, lowest, end = m
- 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:
+ if start == end:
new_start = self.app.data.s11[start].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)
+ 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:
self.vswr_limit_value = max(
self.vswr_bandwith_value, self.vswr_limit_value - 1)
@@ -96,13 +94,14 @@ class MagLoopAnalysis(VSWRAnalysis):
else:
new_start = new_start - 5 * self.bandwith
new_end = new_end + 5 * self.bandwith
- if all((new_start <= self.min_freq,
- new_end >= self.max_freq)):
- if self.vswr_limit_value < 10:
- self.vswr_limit_value += 2
- self.input_vswr_limit.setValue(self.vswr_limit_value)
- logger.debug(
- "no minimum found, looking for higher value %s", self.vswr_limit_value)
+ if (
+ all((new_start <= self.min_freq, new_end >= self.max_freq))
+ and self.vswr_limit_value < 10
+ ):
+ self.vswr_limit_value += 2
+ self.input_vswr_limit.setValue(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_end = min(self.max_freq, new_end)
logger.debug("next search will be %s - %s for vswr %s",
diff --git a/NanoVNASaver/Charts/Chart.py b/NanoVNASaver/Charts/Chart.py
index abb5758..a5cfb33 100644
--- a/NanoVNASaver/Charts/Chart.py
+++ b/NanoVNASaver/Charts/Chart.py
@@ -109,7 +109,7 @@ class Chart(QtWidgets.QWidget):
def __init__(self, name):
super().__init__()
self.name = name
- self.sweepTitle = ""
+ self.sweepTitle = ''
self.dim = ChartDimensions()
self.dragbox = ChartDragBox()
@@ -170,10 +170,14 @@ class Chart(QtWidgets.QWidget):
def getActiveMarker(self) -> Marker:
if self.draggedMarker is not None:
return self.draggedMarker
- for m in self.markers:
- if m.isMouseControlledRadioButton.isChecked():
- return m
- return None
+ return next(
+ (
+ m
+ for m in self.markers
+ if m.isMouseControlledRadioButton.isChecked()
+ ),
+ None,
+ )
def getNearestMarker(self, x, y) -> Marker:
if len(self.data) == 0:
@@ -264,7 +268,6 @@ class Chart(QtWidgets.QWidget):
self.swrMarkers.remove(swr)
except KeyError:
logger.debug("KeyError from %s", self.name)
- return
finally:
self.update()
@@ -277,8 +280,6 @@ class Chart(QtWidgets.QWidget):
cmarker.draw(x, y, color, str(number))
def drawTitle(self, qp: QtGui.QPainter, position: QtCore.QPoint = None):
- if not self.sweepTitle:
- return
qp.setPen(Chart.color.text)
if position is None:
qf = QtGui.QFontMetricsF(self.font())
diff --git a/NanoVNASaver/Charts/Frequency.py b/NanoVNASaver/Charts/Frequency.py
index f961a2f..e9ae60b 100644
--- a/NanoVNASaver/Charts/Frequency.py
+++ b/NanoVNASaver/Charts/Frequency.py
@@ -96,11 +96,11 @@ class FrequencyChart(Chart):
self.x_menu.addSeparator()
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_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.x_menu.addAction(self.action_set_fixed_start)
diff --git a/NanoVNASaver/Charts/GroupDelay.py b/NanoVNASaver/Charts/GroupDelay.py
index ac44a79..1db1a91 100644
--- a/NanoVNASaver/Charts/GroupDelay.py
+++ b/NanoVNASaver/Charts/GroupDelay.py
@@ -62,64 +62,40 @@ class GroupDelayChart(FrequencyChart):
def setReference(self, data):
self.reference = data
-
self.calculateGroupDelay()
def setData(self, data):
self.data = data
-
self.calculateGroupDelay()
def calculateGroupDelay(self):
- rawData = []
- for d in self.data:
- 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.groupDelay = self.calc_data(self.data)
+ self.groupDelayReference = self.calc_data(self.reference)
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):
if len(self.data) == 0 and len(self.reference) == 0:
return
@@ -146,23 +122,19 @@ class GroupDelayChart(FrequencyChart):
self.span = span
tickcount = math.floor(self.dim.height / 60)
-
for i in range(tickcount):
delay = min_delay + span * i / tickcount
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))
- if delay != 0:
- digits = max(0, min(2, math.floor(3 - math.log10(abs(delay)))))
- if digits == 0:
- delaystr = str(round(delay))
- else:
- delaystr = str(round(delay, digits))
- else:
- delaystr = "0"
+ # TODO use format class
+ digits = 0 if delay == 0 else max(
+ 0, min(2, math.floor(3 - math.log10(abs(delay)))))
+ delaystr = str(round(delay, digits if digits != 0 else None))
qp.drawText(3, y + 3, delaystr)
qp.setPen(QtGui.QPen(Chart.color.foreground))
qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.dim.width, y)
+
qp.drawLine(self.leftMargin - 5,
self.topMargin,
self.leftMargin + self.dim.width,
@@ -179,66 +151,48 @@ class GroupDelayChart(FrequencyChart):
self.drawFrequencyTicks(qp)
- color = Chart.color.sweep
- 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.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.draw_data(qp, Chart.color.sweep,
+ self.data, self.groupDelay)
+ self.draw_data(qp, Chart.color.reference,
+ self.reference, self.groupDelayReference)
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:
- # TODO: Find a faster way than these expensive "d in self.data" lookups
- if d in self.data:
+ # TODO: Find a faster way than these expensive "d in data" lookups
+ try:
delay = self.groupDelay[self.data.index(d)]
- elif d in self.reference:
- delay = self.groupDelayReference[self.reference.index(d)]
- else:
- delay = 0
+ except ValueError:
+ try:
+ delay = self.groupDelayReference[self.reference.index(d)]
+ except ValueError:
+ delay = 0
return self.getYPositionFromDelay(delay)
def getYPositionFromDelay(self, delay: float):
diff --git a/NanoVNASaver/Charts/Polar.py b/NanoVNASaver/Charts/Polar.py
index 8db1319..d2fec6e 100644
--- a/NanoVNASaver/Charts/Polar.py
+++ b/NanoVNASaver/Charts/Polar.py
@@ -1,8 +1,8 @@
# NanoVNASaver
#
# A python program to view and export Touchstone data from a NanoVNA
-# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020,2021 NanoVNA-Saver Authors
+# Copyright (C) 2019, 2020 Rune B. Broberg
+# Copyright (C) 2020ff NanoVNA-Saver Authors
#
# 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
@@ -16,12 +16,9 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-import math
import logging
-
from PyQt5 import QtGui, QtCore
-from NanoVNASaver.RFTools import Datapoint
from NanoVNASaver.Charts.Chart import Chart
from NanoVNASaver.Charts.Square import SquareChart
@@ -29,129 +26,32 @@ logger = logging.getLogger(__name__)
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):
- centerX = int(self.width()/2)
- centerY = int(self.height()/2)
+ center_x = int(self.width()/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.drawText(3, 15, self.name)
qp.setPen(QtGui.QPen(Chart.color.foreground))
- qp.drawEllipse(QtCore.QPoint(centerX, centerY),
- int(self.dim.width / 2),
- int(self.dim.height / 2))
- qp.drawEllipse(QtCore.QPoint(centerX, centerY),
- int(self.dim.width / 4),
- int(self.dim.height / 4))
- qp.drawLine(centerX - int(self.dim.width / 2), centerY,
- centerX + int(self.dim.width / 2), centerY)
- qp.drawLine(centerX, centerY - int(self.dim.height / 2),
- centerX, centerY + int(self.dim.height / 2))
- 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)))
- 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)))
+
+ qp.drawEllipse(QtCore.QPoint(center_x, center_y), width_2, height_2)
+ qp.drawEllipse(QtCore.QPoint(center_x, center_y),
+ width_2 // 2, height_2 // 2)
+
+ qp.drawLine(center_x - width_2, center_y, center_x + width_2, center_y)
+ qp.drawLine(center_x, center_y - height_2,
+ center_x, center_y + height_2)
+
+ qp.drawLine(center_x + width_45, center_y + height_45,
+ center_x - width_45, center_y - height_45)
+ qp.drawLine(center_x + width_45, center_y - height_45,
+ center_x - width_45, center_y + height_45)
+
self.drawTitle(qp)
- def drawValues(self, qp: QtGui.QPainter):
- if len(self.data) == 0 and len(self.reference) == 0:
- 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
+ def zoomTo(self, x1, y1, x2, y2):
+ raise NotImplementedError()
diff --git a/NanoVNASaver/Charts/Smith.py b/NanoVNASaver/Charts/Smith.py
index 2309963..ff18c7d 100644
--- a/NanoVNASaver/Charts/Smith.py
+++ b/NanoVNASaver/Charts/Smith.py
@@ -1,8 +1,8 @@
# NanoVNASaver
#
# A python program to view and export Touchstone data from a NanoVNA
-# Copyright (C) 2019, 2020 Rune B. Broberg
-# Copyright (C) 2020,2021 NanoVNA-Saver Authors
+# Copyright (C) 2019, 2020 Rune B. Broberg
+# Copyright (C) 2020ff NanoVNA-Saver Authors
#
# 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
@@ -16,12 +16,9 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-import math
import logging
-
from PyQt5 import QtGui, QtCore
-from NanoVNASaver.RFTools import Datapoint
from NanoVNASaver.Charts.Chart import Chart
from NanoVNASaver.Charts.Square import SquareChart
@@ -29,25 +26,7 @@ logger = logging.getLogger(__name__)
class SmithChart(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)
- # qp.begin(self) # Apparently not needed?
- self.drawSmithChart(qp)
- self.drawValues(qp)
- qp.end()
-
- def drawSmithChart(self, qp: QtGui.QPainter):
+ def drawChart(self, qp: QtGui.QPainter) -> None:
centerX = int(self.width()/2)
centerY = int(self.height()/2)
qp.setPen(QtGui.QPen(Chart.color.text))
@@ -116,91 +95,5 @@ class SmithChart(SquareChart):
QtCore.QRect(centerX - 50, centerY - 4 + r, 100, 20),
QtCore.Qt.AlignCenter, str(swr))
- def drawValues(self, qp: QtGui.QPainter):
- if len(self.data) == 0 and len(self.reference) == 0:
- 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
+ def zoomTo(self, x1, y1, x2, y2):
+ raise NotImplementedError()
diff --git a/NanoVNASaver/Charts/Square.py b/NanoVNASaver/Charts/Square.py
index 83b96d1..acc7b0f 100644
--- a/NanoVNASaver/Charts/Square.py
+++ b/NanoVNASaver/Charts/Square.py
@@ -17,23 +17,81 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
import logging
+import math
+from typing import List
+from PyQt5 import QtGui, QtCore
from PyQt5 import QtWidgets, QtGui
from NanoVNASaver.Charts.Chart import Chart
+from NanoVNASaver.RFTools import Datapoint
logger = logging.getLogger(__name__)
class SquareChart(Chart):
- def __init__(self, name):
+ def __init__(self, name=''):
super().__init__(name)
sizepolicy = QtWidgets.QSizePolicy(
QtWidgets.QSizePolicy.Fixed,
QtWidgets.QSizePolicy.MinimumExpanding)
self.setSizePolicy(sizepolicy)
- self.dim.width = self.width()-40
- self.dim.height = self.height()-40
+ 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) -> 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:
if not self.flag.is_popout:
@@ -44,3 +102,44 @@ class SquareChart(Chart):
min_dimension = min(a0.size().height(), a0.size().width())
self.dim.width = self.dim.height = min_dimension - 40
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)
diff --git a/NanoVNASaver/Hardware/Hardware.py b/NanoVNASaver/Hardware/Hardware.py
index 68a5d7b..4e4047e 100644
--- a/NanoVNASaver/Hardware/Hardware.py
+++ b/NanoVNASaver/Hardware/Hardware.py
@@ -34,6 +34,7 @@ from NanoVNASaver.Hardware.NanoVNA_H4 import NanoVNA_H4
from NanoVNASaver.Hardware.NanoVNA_V2 import NanoVNA_V2
from NanoVNASaver.Hardware.TinySA import TinySA
from NanoVNASaver.Hardware.Serial import drain_serial, Interface
+from NanoVNASaver.Hardware.VNA import VNA
logger = logging.getLogger(__name__)
diff --git a/NanoVNASaver/Hardware/Serial.py b/NanoVNASaver/Hardware/Serial.py
index 1600637..f38b1ef 100644
--- a/NanoVNASaver/Hardware/Serial.py
+++ b/NanoVNASaver/Hardware/Serial.py
@@ -41,7 +41,7 @@ def drain_serial(serial_port: serial.Serial):
class Interface(serial.Serial):
def __init__(self, interface_type: str, comment, *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.comment = comment
self.port = None
diff --git a/NanoVNASaver/Hardware/TinySA.py b/NanoVNASaver/Hardware/TinySA.py
index 19af3bb..5208ebf 100644
--- a/NanoVNASaver/Hardware/TinySA.py
+++ b/NanoVNASaver/Hardware/TinySA.py
@@ -38,7 +38,7 @@ class TinySA(VNA):
def __init__(self, iface: Interface):
super().__init__(iface)
- self.features = set(('Screenshots',))
+ self.features = {'Screenshots'}
logger.debug("Setting initial start,stop")
self.start, self.stop = self._get_running_frequencies()
self.sweep_max_freq_Hz = 950e6
diff --git a/NanoVNASaver/Marker/Delta.py b/NanoVNASaver/Marker/Delta.py
index 1f06876..f13f9b1 100644
--- a/NanoVNASaver/Marker/Delta.py
+++ b/NanoVNASaver/Marker/Delta.py
@@ -77,15 +77,8 @@ class DeltaMarker(Marker):
RFTools.impedance_to_inductance(imp_p_b, s11_b.freq)-
RFTools.impedance_to_inductance(imp_p_a, s11_a.freq))
- if imp.imag < 0:
- x_str = cap_str
- else:
- x_str = ind_str
-
- if imp_p.imag < 0:
- x_p_str = cap_p_str
- else:
- x_p_str = ind_p_str
+ x_str = cap_str if imp.imag < 0 else ind_str
+ x_p_str = cap_p_str if imp_p.imag < 0 else ind_p_str
self.label['actualfreq'].setText(
format_frequency_space(s11_b.freq - s11_a.freq))
diff --git a/NanoVNASaver/Marker/Values.py b/NanoVNASaver/Marker/Values.py
index ee58d07..34ef586 100644
--- a/NanoVNASaver/Marker/Values.py
+++ b/NanoVNASaver/Marker/Values.py
@@ -84,9 +84,9 @@ class Value():
if s21:
s21 = [s21[0], ] + s21
if index == len(s11):
- s11 = s11 + [s11[-1], ]
+ s11 += [s11[-1], ]
if s21:
- s21 = s21 + [s21[-1], ]
+ s21 += [s21[-1], ]
self.freq = s11[1].freq
self.s11 = s11[index-1:index+2]
if s21:
diff --git a/NanoVNASaver/Marker/Widget.py b/NanoVNASaver/Marker/Widget.py
index cf19aa1..9d47f32 100644
--- a/NanoVNASaver/Marker/Widget.py
+++ b/NanoVNASaver/Marker/Widget.py
@@ -271,7 +271,7 @@ class Marker(QtCore.QObject, Value):
self.location = i-1
if i < datasize:
self.frequencyInput.nextFrequency = item.freq
- if (i-2) >= 0:
+ if i >= 2:
self.frequencyInput.previousFrequency = data[i-2].freq
return
# 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(
RFTools.impedance_to_inductance(imp_p, _s11.freq))
- if imp.imag < 0:
- x_str = cap_str
- else:
- x_str = ind_str
-
- if imp_p.imag < 0:
- x_p_str = cap_p_str
- else:
- x_p_str = ind_p_str
+ x_str = cap_str if imp.imag < 0 else ind_str
+ x_p_str = cap_p_str if imp_p.imag < 0 else ind_p_str
self.label['actualfreq'].setText(format_frequency_space(_s11.freq))
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['s11phase'].setText(format_phase(_s11.phase))
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['s11z'].setText(format_resistance(abs(imp)))
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['s21phase'].setText(format_phase(_s21.phase))
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(
format_magnitude(abs(_s21.shuntImpedance())))
self.label['s21magseries'].setText(
diff --git a/NanoVNASaver/Settings/Bands.py b/NanoVNASaver/Settings/Bands.py
index 3c6844d..87fb292 100644
--- a/NanoVNASaver/Settings/Bands.py
+++ b/NanoVNASaver/Settings/Bands.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+import contextlib
import logging
import typing
@@ -132,10 +133,8 @@ class BandsModel(QtCore.QAbstractTableModel):
orientation: QtCore.Qt.Orientation, role: int = ...):
if (role == QtCore.Qt.DisplayRole and
orientation == QtCore.Qt.Horizontal):
- try:
+ with contextlib.suppress(IndexError):
return _HEADER_DATA[section]
- except IndexError:
- pass
return None
def flags(self, index: QModelIndex) -> QtCore.Qt.ItemFlags:
diff --git a/NanoVNASaver/SweepWorker.py b/NanoVNASaver/SweepWorker.py
index f01f7ce..c4da6d7 100644
--- a/NanoVNASaver/SweepWorker.py
+++ b/NanoVNASaver/SweepWorker.py
@@ -192,12 +192,10 @@ class SweepWorker(QtCore.QRunnable):
if not self.app.calibration.isCalculated:
data11 = raw_data11.copy()
data21 = raw_data21.copy()
+ elif self.app.calibration.isValid1Port():
+ data11.extend(self.app.calibration.correct11(dp) for dp in raw_data11)
else:
- if self.app.calibration.isValid1Port():
- for dp in raw_data11:
- data11.append(self.app.calibration.correct11(dp))
- else:
- data11 = raw_data11.copy()
+ data11 = raw_data11.copy()
if self.app.calibration.isValid2Port():
for counter, dp in enumerate(raw_data21):
diff --git a/flatpak.manifest.yml b/flatpak.manifest.yml
new file mode 100644
index 0000000..280fcfa
--- /dev/null
+++ b/flatpak.manifest.yml
@@ -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
diff --git a/setup.cfg b/setup.cfg
index dbeb32d..6ab3140 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -13,7 +13,7 @@ packages = find_namespace:
install_requires=
pyserial>=3.5
PyQt5>=5.15.0
- numpy>=1.21.1,<1.22.0
+ numpy>=1.21.1
scipy>=1.7.1
Cython>=0.29.24
python_requires = >=3.8, <4