diff --git a/NanoVNASaver/Analysis.py b/NanoVNASaver/Analysis.py
index ad895420..b8a8c83d 100644
--- a/NanoVNASaver/Analysis.py
+++ b/NanoVNASaver/Analysis.py
@@ -199,11 +199,12 @@ def runAnalysis(self):
if sixty_db_location > 0:
sixty_db_cutoff_frequency = self.app.data21[sixty_db_location].freq
self.sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_cutoff_frequency))
+ elif ten_db_location != -1 and twenty_db_location != -1:
+ ten = self.app.data21[ten_db_location].freq
+ twenty = self.app.data21[twenty_db_location].freq
+ sixty_db_frequency = ten * 10 ** (5 * (math.log10(twenty) - math.log10(ten)))
+ self.sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_frequency) + " (derived)")
else:
- # # We derive 60 dB instead
- # factor = 10 * (-54 / decade_attenuation)
- # sixty_db_cutoff_frequency = round(six_db_cutoff_frequency + six_db_cutoff_frequency * factor)
- # self.sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_cutoff_frequency) + " (derived)")
self.sixty_db_label.setText("Not calculated")
if ten_db_location > 0 and twenty_db_location > 0 and ten_db_location != twenty_db_location:
@@ -358,14 +359,16 @@ def runAnalysis(self):
break
if sixty_db_location > 0:
- sixty_db_cutoff_frequency = self.app.data21[sixty_db_location].freq
- self.sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_cutoff_frequency))
- else:
- # # We derive 60 dB instead
- # factor = 10 * (-54 / decade_attenuation)
- # sixty_db_cutoff_frequency = round(six_db_cutoff_frequency + six_db_cutoff_frequency * factor)
- # self.sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_cutoff_frequency) + " (derived)")
- self.sixty_db_label.setText("Not calculated")
+ if sixty_db_location > 0:
+ sixty_db_cutoff_frequency = self.app.data21[sixty_db_location].freq
+ self.sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_cutoff_frequency))
+ elif ten_db_location != -1 and twenty_db_location != -1:
+ ten = self.app.data21[ten_db_location].freq
+ twenty = self.app.data21[twenty_db_location].freq
+ sixty_db_frequency = ten * 10 ** (5 * (math.log10(twenty) - math.log10(ten)))
+ self.sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_frequency) + " (derived)")
+ else:
+ self.sixty_db_label.setText("Not calculated")
if ten_db_location > 0 and twenty_db_location > 0 and ten_db_location != twenty_db_location:
octave_attenuation, decade_attenuation = self.calculateRolloff(ten_db_location, twenty_db_location)
@@ -610,14 +613,16 @@ def runAnalysis(self):
break
if sixty_db_location > 0:
- sixty_db_cutoff_frequency = self.app.data21[sixty_db_location].freq
- self.lower_sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_cutoff_frequency))
- else:
- # # We derive 60 dB instead
- # factor = 10 * (-54 / decade_attenuation)
- # sixty_db_cutoff_frequency = round(six_db_cutoff_frequency + six_db_cutoff_frequency * factor)
- # self.sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_cutoff_frequency) + " (derived)")
- self.lower_sixty_db_label.setText("Not calculated")
+ if sixty_db_location > 0:
+ sixty_db_cutoff_frequency = self.app.data21[sixty_db_location].freq
+ self.lower_sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_cutoff_frequency))
+ elif ten_db_location != -1 and twenty_db_location != -1:
+ ten = self.app.data21[ten_db_location].freq
+ twenty = self.app.data21[twenty_db_location].freq
+ sixty_db_frequency = ten * 10 ** (5 * (math.log10(twenty) - math.log10(ten)))
+ self.lower_sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_frequency) + " (derived)")
+ else:
+ self.lower_sixty_db_label.setText("Not calculated")
if ten_db_location > 0 and twenty_db_location > 0 and ten_db_location != twenty_db_location:
octave_attenuation, decade_attenuation = self.calculateRolloff(ten_db_location, twenty_db_location)
@@ -674,11 +679,12 @@ def runAnalysis(self):
if sixty_db_location > 0:
sixty_db_cutoff_frequency = self.app.data21[sixty_db_location].freq
self.upper_sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_cutoff_frequency))
+ elif ten_db_location != -1 and twenty_db_location != -1:
+ ten = self.app.data21[ten_db_location].freq
+ twenty = self.app.data21[twenty_db_location].freq
+ sixty_db_frequency = ten * 10 ** (5 * (math.log10(twenty) - math.log10(ten)))
+ self.upper_sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_frequency) + " (derived)")
else:
- # # We derive 60 dB instead
- # factor = 10 * (-54 / decade_attenuation)
- # sixty_db_cutoff_frequency = round(six_db_cutoff_frequency + six_db_cutoff_frequency * factor)
- # self.sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_cutoff_frequency) + " (derived)")
self.upper_sixty_db_label.setText("Not calculated")
if ten_db_location > 0 and twenty_db_location > 0 and ten_db_location != twenty_db_location:
@@ -885,11 +891,12 @@ def runAnalysis(self):
if sixty_db_location > 0:
sixty_db_cutoff_frequency = self.app.data21[sixty_db_location].freq
self.lower_sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_cutoff_frequency))
+ elif ten_db_location != -1 and twenty_db_location != -1:
+ ten = self.app.data21[ten_db_location].freq
+ twenty = self.app.data21[twenty_db_location].freq
+ sixty_db_frequency = ten * 10 ** (5 * (math.log10(twenty) - math.log10(ten)))
+ self.lower_sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_frequency) + " (derived)")
else:
- # # We derive 60 dB instead
- # factor = 10 * (-54 / decade_attenuation)
- # sixty_db_cutoff_frequency = round(six_db_cutoff_frequency + six_db_cutoff_frequency * factor)
- # self.sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_cutoff_frequency) + " (derived)")
self.lower_sixty_db_label.setText("Not calculated")
if ten_db_location > 0 and twenty_db_location > 0 and ten_db_location != twenty_db_location:
@@ -947,11 +954,12 @@ def runAnalysis(self):
if sixty_db_location > 0:
sixty_db_cutoff_frequency = self.app.data21[sixty_db_location].freq
self.upper_sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_cutoff_frequency))
+ elif ten_db_location != -1 and twenty_db_location != -1:
+ ten = self.app.data21[ten_db_location].freq
+ twenty = self.app.data21[twenty_db_location].freq
+ sixty_db_frequency = ten * 10 ** (5 * (math.log10(twenty) - math.log10(ten)))
+ self.upper_sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_frequency) + " (derived)")
else:
- # # We derive 60 dB instead
- # factor = 10 * (-54 / decade_attenuation)
- # sixty_db_cutoff_frequency = round(six_db_cutoff_frequency + six_db_cutoff_frequency * factor)
- # self.sixty_db_label.setText(NanoVNASaver.formatFrequency(sixty_db_cutoff_frequency) + " (derived)")
self.upper_sixty_db_label.setText("Not calculated")
if ten_db_location > 0 and twenty_db_location > 0 and ten_db_location != twenty_db_location:
diff --git a/NanoVNASaver/Calibration.py b/NanoVNASaver/Calibration.py
index 8574c2cc..83f65b8d 100644
--- a/NanoVNASaver/Calibration.py
+++ b/NanoVNASaver/Calibration.py
@@ -138,10 +138,10 @@ def __init__(self, app):
self.short_l2_input = QtWidgets.QLineEdit("0")
self.short_l3_input = QtWidgets.QLineEdit("0")
self.short_length = QtWidgets.QLineEdit("0")
- cal_short_form.addRow("L0 (F(e-12))", self.short_l0_input)
- cal_short_form.addRow("L1 (F(e-24))", self.short_l1_input)
- cal_short_form.addRow("L2 (F(e-33))", self.short_l2_input)
- cal_short_form.addRow("L3 (F(e-42))", self.short_l3_input)
+ cal_short_form.addRow("L0 (H(e-12))", self.short_l0_input)
+ cal_short_form.addRow("L1 (H(e-24))", self.short_l1_input)
+ cal_short_form.addRow("L2 (H(e-33))", self.short_l2_input)
+ cal_short_form.addRow("L3 (H(e-42))", self.short_l3_input)
cal_short_form.addRow("Offset Delay (ps)", self.short_length)
self.cal_open_box = QtWidgets.QGroupBox("Open")
@@ -152,10 +152,10 @@ def __init__(self, app):
self.open_c2_input = QtWidgets.QLineEdit("0")
self.open_c3_input = QtWidgets.QLineEdit("0")
self.open_length = QtWidgets.QLineEdit("0")
- cal_open_form.addRow("C0 (H(e-15))", self.open_c0_input)
- cal_open_form.addRow("C1 (H(e-27))", self.open_c1_input)
- cal_open_form.addRow("C2 (H(e-36))", self.open_c2_input)
- cal_open_form.addRow("C3 (H(e-45))", self.open_c3_input)
+ cal_open_form.addRow("C0 (F(e-15))", self.open_c0_input)
+ cal_open_form.addRow("C1 (F(e-27))", self.open_c1_input)
+ cal_open_form.addRow("C2 (F(e-36))", self.open_c2_input)
+ cal_open_form.addRow("C3 (F(e-45))", self.open_c3_input)
cal_open_form.addRow("Offset Delay (ps)", self.open_length)
self.cal_load_box = QtWidgets.QGroupBox("Load")
@@ -510,15 +510,15 @@ def automaticCalibration(self):
"Calibration assistant",
"This calibration assistant will help you create a calibration in the " +
"NanoVNASaver application. It will sweep the standards for you, and "+
- "guide you through the process.\n\n" +
+ "guide you through the process.
" +
"Before starting, ensure you have Open, Short and Load standards " +
"available, and the cables you wish to have calibrated with the device " +
- "connected.\n\n" +
+ "connected.
" +
"If you want a 2-port calibration, also have a \"through\" connector " +
- "to hand.\n\n" +
- "The best results are achieved by having the NanoVNA calibrated " +
+ "to hand.
" +
+ "The best results are achieved by having the NanoVNA calibrated " +
"on-device for the full span of interest and saved to save slot 0 " +
- "before starting.\n\n" +
+ "before starting.
" +
"Once you are ready to proceed, press Ok",
QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
response = introduction.exec()
@@ -533,6 +533,12 @@ def automaticCalibration(self):
self.btn_automatic.setDisabled(False)
return
+ if self.app.sweepSettingsWindow.continuous_sweep_radiobutton.isChecked():
+ QtWidgets.QMessageBox(QtWidgets.QMessageBox.Information, "Continuous sweep enabled",
+ "Please disable continuous sweeping before attempting calibration.").exec()
+ self.btn_automatic.setDisabled(False)
+ return
+
short_step = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Information,
"Calibrate short",
"Please connect the \"short\" standard to port 0 of the NanoVNA.\n\n" +
diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py
index 87d42ef8..d40aab40 100644
--- a/NanoVNASaver/Chart.py
+++ b/NanoVNASaver/Chart.py
@@ -15,11 +15,12 @@
# along with this program. If not, see .
import collections
import math
-from typing import List
+from typing import List, Set
import numpy as np
import logging
from PyQt5 import QtWidgets, QtGui, QtCore
+from PyQt5.QtCore import pyqtSignal
from .Marker import Marker
logger = logging.getLogger(__name__)
@@ -36,15 +37,24 @@ class Chart(QtWidgets.QWidget):
backgroundColor: QtGui.QColor = QtGui.QColor(QtCore.Qt.white)
foregroundColor: QtGui.QColor = QtGui.QColor(QtCore.Qt.lightGray)
textColor: QtGui.QColor = QtGui.QColor(QtCore.Qt.black)
+ swrColor: QtGui.QColor = QtGui.QColor(QtCore.Qt.red)
+ swrColor.setAlpha(128)
data: List[Datapoint] = []
reference: List[Datapoint] = []
markers: List[Marker] = []
+ swrMarkers: Set[float] = set()
bands = None
draggedMarker: Marker = None
name = ""
drawLines = False
minChartHeight = 200
minChartWidth = 200
+ lineThickness = 1
+ pointSize = 2
+
+
+ isPopout = False
+ popoutRequested = pyqtSignal(object)
def __init__(self, name):
super().__init__()
@@ -54,6 +64,11 @@ def __init__(self, name):
self.action_save_screenshot = QtWidgets.QAction("Save image")
self.action_save_screenshot.triggered.connect(self.saveScreenshot)
self.addAction(self.action_save_screenshot)
+ self.action_popout = QtWidgets.QAction("Popout chart")
+ self.action_popout.triggered.connect(lambda: self.popoutRequested.emit(self))
+ self.addAction(self.action_popout)
+
+ self.swrMarkers = set()
def setSweepColor(self, color : QtGui.QColor):
self.sweepColor = color
@@ -104,6 +119,14 @@ def setMarkers(self, markers):
def setBands(self, bands):
self.bands = bands
+ def setLineThickness(self, thickness):
+ self.lineThickness = thickness
+ self.update()
+
+ def setPointSize(self, size):
+ self.pointSize = size
+ self.update()
+
def getActiveMarker(self, event: QtGui.QMouseEvent) -> Marker:
if self.draggedMarker is not None:
return self.draggedMarker
@@ -171,6 +194,46 @@ def saveScreenshot(self):
if filename != "":
self.grab().save(filename)
+ def copy(self):
+ new_chart = self.__class__(self.name)
+ new_chart.data = self.data
+ new_chart.reference = self.reference
+ new_chart.sweepColor = self.sweepColor
+ new_chart.secondarySweepColor = self.secondarySweepColor
+ new_chart.referenceColor = self.referenceColor
+ new_chart.secondaryReferenceColor = self.secondaryReferenceColor
+ new_chart.setBackgroundColor(self.backgroundColor)
+ new_chart.textColor = self.textColor
+ new_chart.foregroundColor = self.foregroundColor
+ new_chart.swrColor = self.swrColor
+ new_chart.markers = self.markers
+ new_chart.swrMarkers = self.swrMarkers
+ new_chart.bands = self.bands
+ new_chart.drawLines = self.drawLines
+ new_chart.resize(self.width(), self.height())
+ return new_chart
+
+ def addSWRMarker(self, swr: float):
+ self.swrMarkers.add(swr)
+ self.update()
+
+ def removeSWRMarker(self, swr: float):
+ try:
+ self.swrMarkers.remove(swr)
+ except KeyError:
+ logger.debug("KeyError from %s", self.name)
+ return
+ finally:
+ self.update()
+
+ def clearSWRMarkers(self):
+ self.swrMarkers.clear()
+ self.update()
+
+ def setSWRColor(self, color: QtGui.QColor):
+ self.swrColor = color
+ self.update()
+
class FrequencyChart(Chart):
fstart = 0
@@ -185,8 +248,7 @@ class FrequencyChart(Chart):
fixedSpan = False
fixedValues = False
- linear = True
- logarithmic = False
+ logarithmicX = False
chartWidth = Chart.minChartWidth
chartHeight = Chart.minChartHeight
@@ -229,6 +291,20 @@ def __init__(self, name):
self.x_menu.addAction(self.action_set_fixed_start)
self.x_menu.addAction(self.action_set_fixed_stop)
+
+ self.x_menu.addSeparator()
+ frequency_mode_group = QtWidgets.QActionGroup(self.x_menu)
+ self.action_set_linear_x = QtWidgets.QAction("Linear")
+ self.action_set_linear_x.setCheckable(True)
+ self.action_set_logarithmic_x = QtWidgets.QAction("Logarithmic")
+ self.action_set_logarithmic_x.setCheckable(True)
+ frequency_mode_group.addAction(self.action_set_linear_x)
+ frequency_mode_group.addAction(self.action_set_logarithmic_x)
+ self.action_set_linear_x.triggered.connect(lambda: self.setLogarithmicX(False))
+ self.action_set_logarithmic_x.triggered.connect(lambda: self.setLogarithmicX(True))
+ self.action_set_linear_x.setChecked(True)
+ self.x_menu.addAction(self.action_set_linear_x)
+ self.x_menu.addAction(self.action_set_logarithmic_x)
self.y_menu = QtWidgets.QMenu("Data axis")
self.y_action_automatic = QtWidgets.QAction("Automatic")
@@ -258,6 +334,9 @@ def __init__(self, name):
self.menu.addMenu(self.y_menu)
self.menu.addSeparator()
self.menu.addAction(self.action_save_screenshot)
+ self.action_popout = QtWidgets.QAction("Popout chart")
+ self.action_popout.triggered.connect(lambda: self.popoutRequested.emit(self))
+ self.menu.addAction(self.action_popout)
def contextMenuEvent(self, event):
self.action_set_fixed_start.setText("Start (" + Chart.shortenFrequency(self.minFrequency) + ")")
@@ -283,6 +362,10 @@ def setFixedValues(self, fixed_values: bool):
self.y_action_fixed_span.setChecked(False)
self.update()
+ def setLogarithmicX(self, logarithmic: bool):
+ self.logarithmicX = logarithmic
+ self.update()
+
def setMinimumFrequency(self):
from NanoVNASaver.NanoVNASaver import NanoVNASaver
min_freq_str, selected = QtWidgets.QInputDialog.getText(self, "Start frequency",
@@ -332,12 +415,18 @@ def resetDisplayLimits(self):
self.y_action_automatic.setChecked(True)
self.fixedSpan = False
self.action_automatic.setChecked(True)
+ self.logarithmicX = False
self.update()
def getXPosition(self, d: Datapoint) -> int:
span = self.fstop - self.fstart
if span > 0:
- return self.leftMargin + 1 + round(self.chartWidth * (d.freq - self.fstart) / span)
+ if self.logarithmicX:
+ span = math.log(self.fstop) - math.log(self.fstart)
+ return self.leftMargin + 1 +\
+ round(self.chartWidth * (math.log(d.freq) - math.log(self.fstart)) / span)
+ else:
+ return self.leftMargin + 1 + round(self.chartWidth * (d.freq - self.fstart) / span)
else:
return math.floor(self.width()/2)
@@ -353,9 +442,14 @@ def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None:
a0.accept()
if self.fstop - self.fstart > 0:
m = self.getActiveMarker(a0)
- span = self.fstop - self.fstart
- step = span/self.chartWidth
- f = self.fstart + absx * step
+ if self.logarithmicX:
+ span = math.log(self.fstop) - math.log(self.fstart)
+ step = span/self.chartWidth
+ f = math.exp(math.log(self.fstart) + absx * step)
+ else:
+ span = self.fstop - self.fstart
+ step = span/self.chartWidth
+ f = self.fstart + absx * step
m.setFrequency(str(round(f)))
m.frequencyInput.setText(str(round(f)))
return
@@ -380,6 +474,23 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
"Data outside frequency span")
qp.end()
+ def drawFrequencyTicks(self, qp):
+ fspan = self.fstop - self.fstart
+ qp.setPen(self.textColor)
+ qp.drawText(self.leftMargin - 20, self.topMargin + self.chartHeight + 15, Chart.shortenFrequency(self.fstart))
+ ticks = math.floor(self.chartWidth / 100) # Number of ticks does not include the origin
+ for i in range(ticks):
+ x = self.leftMargin + round((i + 1) * self.chartWidth / ticks)
+ if self.logarithmicX:
+ fspan = math.log(self.fstop) - math.log(self.fstart)
+ freq = round(math.exp(((i + 1) * fspan / ticks) + math.log(self.fstart)))
+ else:
+ freq = round(fspan / ticks * (i + 1) + self.fstart)
+ qp.setPen(QtGui.QPen(self.foregroundColor))
+ qp.drawLine(x, self.topMargin, x, self.topMargin + self.chartHeight + 5)
+ qp.setPen(self.textColor)
+ qp.drawText(x - 20, self.topMargin + self.chartHeight + 15, Chart.shortenFrequency(freq))
+
def drawBands(self, qp, fstart, fstop):
qp.setBrush(self.bands.color)
qp.setPen(QtGui.QColor(128, 128, 128, 0)) # Don't outline the bands
@@ -403,9 +514,9 @@ def drawBands(self, qp, fstart, fstop):
def drawData(self, qp: QtGui.QPainter, data: List[Datapoint], color: QtGui.QColor):
pen = QtGui.QPen(color)
- pen.setWidth(2)
+ pen.setWidth(self.pointSize)
line_pen = QtGui.QPen(color)
- line_pen.setWidth(1)
+ line_pen.setWidth(self.lineThickness)
qp.setPen(pen)
for i in range(len(data)):
x, y = self.getPosition(data[i])
@@ -465,17 +576,47 @@ def getPlotable(self, x, y, distantx, distanty):
else:
return x, y
+ def copy(self):
+ new_chart: FrequencyChart = super().copy()
+ new_chart.fstart = self.fstart
+ new_chart.fstop = self.fstop
+ new_chart.maxFrequency = self.maxFrequency
+ new_chart.minFrequency = self.minFrequency
+ new_chart.minDisplayValue = self.minDisplayValue
+ new_chart.maxDisplayValue = self.maxDisplayValue
+ new_chart.pointSize = self.pointSize
+ new_chart.lineThickness = self.lineThickness
+
+ new_chart.setFixedSpan(self.fixedSpan)
+ new_chart.action_automatic.setChecked(not self.fixedSpan)
+ new_chart.action_fixed_span.setChecked(self.fixedSpan)
+
+ new_chart.setFixedValues(self.fixedValues)
+ new_chart.y_action_automatic.setChecked(not self.fixedValues)
+ new_chart.y_action_fixed_span.setChecked(self.fixedValues)
+
+ new_chart.setLogarithmicX(self.logarithmicX)
+ new_chart.action_set_logarithmic_x.setChecked(self.logarithmicX)
+ new_chart.action_set_linear_x.setChecked(not self.logarithmicX)
+ return new_chart
+
class SquareChart(Chart):
def __init__(self, name):
super().__init__(name)
sizepolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.MinimumExpanding)
self.setSizePolicy(sizepolicy)
+ self.chartWidth = self.width()-40
+ self.chartHeight = self.height()-40
def resizeEvent(self, a0: QtGui.QResizeEvent) -> None:
- self.setFixedWidth(a0.size().height())
- self.chartWidth = a0.size().height()-40
- self.chartHeight = a0.size().height()-40
+ if not self.isPopout:
+ self.setFixedWidth(a0.size().height())
+ self.chartWidth = a0.size().height()-40
+ self.chartHeight = a0.size().height()-40
+ else:
+ min_dimension = min(a0.size().height(), a0.size().width())
+ self.chartWidth = self.chartHeight = min_dimension - 40
self.update()
@@ -511,6 +652,12 @@ def __init__(self, name=""):
self.action_unwrap.triggered.connect(lambda: self.setUnwrap(self.action_unwrap.isChecked()))
self.y_menu.addAction(self.action_unwrap)
+ def copy(self):
+ new_chart: PhaseChart = super().copy()
+ new_chart.setUnwrap(self.unwrap)
+ new_chart.action_unwrap.setChecked(self.unwrap)
+ return new_chart
+
def setUnwrap(self, unwrap: bool):
self.unwrap = unwrap
self.update()
@@ -526,9 +673,9 @@ def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(2)
+ pen.setWidth(self.pointSize)
line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(1)
+ line_pen.setWidth(self.lineThickness)
if self.unwrap:
rawData = []
@@ -601,15 +748,7 @@ def drawValues(self, qp: QtGui.QPainter):
if self.bands.enabled:
self.drawBands(qp, fstart, fstop)
- qp.setPen(self.textColor)
- qp.drawText(self.leftMargin-20, self.topMargin + self.chartHeight + 15, Chart.shortenFrequency(self.fstart))
- ticks = math.floor(self.chartWidth/100) # Number of ticks does not include the origin
- for i in range(ticks):
- x = self.leftMargin + round((i+1)*self.chartWidth/ticks)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(x, 20, x, 20+self.chartHeight+5)
- qp.setPen(self.textColor)
- qp.drawText(x-20, self.topMargin+self.chartHeight+15, Chart.shortenFrequency(round(fspan/ticks*(i+1) + self.fstart)))
+ self.drawFrequencyTicks(qp)
self.drawData(qp, self.data, self.sweepColor)
self.drawData(qp, self.reference, self.referenceColor)
@@ -668,9 +807,9 @@ def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(2)
+ pen.setWidth(self.pointSize)
line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(1)
+ line_pen.setWidth(self.lineThickness)
highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
highlighter.setWidth(1)
if self.fixedSpan:
@@ -718,7 +857,7 @@ def drawValues(self, qp: QtGui.QPainter):
vswrstr = str(round(vswr))
else:
vswrstr = str(round(vswr, digits))
- qp.drawText(3, y+3, vswrstr)
+ qp.drawText(3, y+3, vswrstr)
qp.setPen(QtGui.QPen(self.foregroundColor))
qp.drawLine(self.leftMargin-5, y, self.leftMargin+self.chartWidth, y)
qp.drawLine(self.leftMargin - 5, self.topMargin, self.leftMargin + self.chartWidth, self.topMargin)
@@ -729,17 +868,14 @@ def drawValues(self, qp: QtGui.QPainter):
else:
vswrstr = str(round(maxVSWR, digits))
qp.drawText(3, 35, vswrstr)
- # qp.drawText(3, self.chartHeight + self.topMargin, str(minVSWR))
- # At least 100 px between ticks
- qp.drawText(self.leftMargin-20, self.topMargin + self.chartHeight + 15, Chart.shortenFrequency(fstart))
- ticks = math.floor(self.chartWidth/100) # Number of ticks does not include the origin
- for i in range(ticks):
- x = self.leftMargin + round((i+1)*self.chartWidth/ticks)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(x, self.topMargin, x, self.topMargin + self.chartHeight + 5)
- qp.setPen(self.textColor)
- qp.drawText(x-20, self.topMargin + self.chartHeight + 15, Chart.shortenFrequency(round(fspan/ticks*(i+1) + fstart)))
+ self.drawFrequencyTicks(qp)
+
+ qp.setPen(self.swrColor)
+ for vswr in self.swrMarkers:
+ y = self.topMargin + round((self.maxVSWR - vswr) / self.span * self.chartHeight)
+ qp.drawLine(self.leftMargin, y, self.leftMargin + self.chartWidth, y)
+ qp.drawText(self.leftMargin + 3, y - 1, str(vswr))
self.drawData(qp, self.data, self.sweepColor)
self.drawData(qp, self.reference, self.referenceColor)
@@ -798,9 +934,9 @@ def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(2)
+ pen.setWidth(self.pointSize)
line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(1)
+ line_pen.setWidth(self.lineThickness)
highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
highlighter.setWidth(1)
qp.setPen(pen)
@@ -932,13 +1068,22 @@ def drawSmithChart(self, qp: QtGui.QPainter):
qp.drawArc(centerX - self.chartWidth*2, centerY, self.chartWidth*5, self.chartHeight*5, int(93.85*16), int(18.85*16)) # Im(Z) = -0.2
qp.drawArc(centerX - self.chartWidth*2, centerY, self.chartWidth*5, -self.chartHeight*5, int(-93.85 * 16), int(-18.85 * 16)) # Im(Z) = 0.2
+ qp.setPen(self.swrColor)
+ for swr in self.swrMarkers:
+ if swr <= 1:
+ continue
+ gamma = (swr - 1)/(swr + 1)
+ r = round(gamma * self.chartWidth/2)
+ qp.drawEllipse(QtCore.QPoint(centerX, centerY), r, r)
+ qp.drawText(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(self.sweepColor)
- pen.setWidth(2)
+ pen.setWidth(self.pointSize)
line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(1)
+ line_pen.setWidth(self.lineThickness)
highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
highlighter.setWidth(1)
qp.setPen(pen)
@@ -1058,9 +1203,9 @@ def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(2)
+ pen.setWidth(self.pointSize)
line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(1)
+ line_pen.setWidth(self.lineThickness)
highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
highlighter.setWidth(1)
if not self.fixedSpan:
@@ -1175,15 +1320,18 @@ def drawValues(self, qp: QtGui.QPainter):
qp.setPen(self.textColor)
qp.drawText(3, self.topMargin + 4, str(maxValue))
qp.drawText(3, self.chartHeight+self.topMargin, str(minValue))
- # Frequency ticks
- qp.drawText(self.leftMargin-20, self.topMargin + self.chartHeight + 15, Chart.shortenFrequency(self.fstart))
- ticks = math.floor(self.chartWidth/100) # Number of ticks does not include the origin
- for i in range(ticks):
- x = self.leftMargin + round((i+1)*self.chartWidth/ticks)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(x, 20, x, self.topMargin+self.chartHeight+5)
- qp.setPen(self.textColor)
- qp.drawText(x-20, self.topMargin+self.chartHeight+15, LogMagChart.shortenFrequency(round(fspan/ticks*(i+1) + self.fstart)))
+ self.drawFrequencyTicks(qp)
+
+ qp.setPen(self.swrColor)
+ for vswr in self.swrMarkers:
+ if vswr <= 1:
+ continue
+ logMag = 20 * math.log10((vswr-1)/(vswr+1))
+ if self.isInverted:
+ logMag = logMag * -1
+ y = self.topMargin + round((self.maxValue - logMag) / self.span * self.chartHeight)
+ qp.drawLine(self.leftMargin, y, self.leftMargin + self.chartWidth, y)
+ qp.drawText(self.leftMargin + 3, y - 1, "VSWR: " + str(vswr))
self.drawData(qp, self.data, self.sweepColor)
self.drawData(qp, self.reference, self.referenceColor)
@@ -1200,6 +1348,12 @@ def logMag(self, p: Datapoint) -> float:
else:
return NanoVNASaver.gain(p)
+ def copy(self):
+ new_chart: LogMagChart = super().copy()
+ new_chart.isInverted = self.isInverted
+ new_chart.span = self.span
+ return new_chart
+
class QualityFactorChart(FrequencyChart):
def __init__(self, name=""):
@@ -1285,9 +1439,9 @@ def drawValues(self, qp: QtGui.QPainter):
if self.span == 0:
return
pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(2)
+ pen.setWidth(self.pointSize)
line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(1)
+ line_pen.setWidth(self.lineThickness)
highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
highlighter.setWidth(1)
if self.fixedSpan:
@@ -1308,17 +1462,7 @@ def drawValues(self, qp: QtGui.QPainter):
if self.bands.enabled:
self.drawBands(qp, fstart, fstop)
- qp.setPen(self.textColor)
- qp.drawText(self.leftMargin-20, self.topMargin + self.chartHeight + 15, Chart.shortenFrequency(fstart))
- ticks = math.floor(self.chartWidth/100) # Number of ticks does not include the origin
- for i in range(ticks):
- x = self.leftMargin + round((i+1)*self.chartWidth/ticks)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(x, self.topMargin - 5, x, self.topMargin + self.chartHeight + 5)
- qp.setPen(self.textColor)
- qp.drawText(x - 20, self.topMargin + self.chartHeight + 15,
- Chart.shortenFrequency(round(fspan/ticks*(i+1) + fstart)))
-
+ self.drawFrequencyTicks(qp)
self.drawData(qp, self.data, self.sweepColor)
self.drawData(qp, self.reference, self.referenceColor)
self.drawMarkers(qp)
@@ -1337,12 +1481,19 @@ def __init__(self, name):
self.rightMargin = 20
self.bottomMargin = 35
self.setMinimumSize(250, 250)
- self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding))
+ self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
+ QtWidgets.QSizePolicy.MinimumExpanding))
pal = QtGui.QPalette()
pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
self.setPalette(pal)
self.setAutoFillBackground(True)
+ def copy(self):
+ new_chart = super().copy()
+ new_chart.tdrWindow = self.tdrWindow
+ self.tdrWindow.updated.connect(new_chart.update)
+ return new_chart
+
def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
qp = QtGui.QPainter(self)
qp.setPen(QtGui.QPen(self.textColor))
@@ -1359,7 +1510,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
ticks = math.floor((self.width() - self.leftMargin)/100) # Number of ticks does not include the origin
if len(self.tdrWindow.td) > 0:
- x_step = len(self.tdrWindow.distance_axis) / width
+ x_step = len(self.tdrWindow.distance_axis) / (width * 2)
y_step = np.max(self.tdrWindow.td)*1.1 / height
for i in range(ticks):
@@ -1370,7 +1521,9 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
qp.drawText(x - 20, 20 + height,
str(round(self.tdrWindow.distance_axis[int((x - self.leftMargin) * x_step) - 1]/2, 1)) + "m")
- qp.setPen(self.sweepColor)
+ pen = QtGui.QPen(self.sweepColor)
+ pen.setWidth(self.pointSize)
+ qp.setPen(pen)
for i in range(len(self.tdrWindow.distance_axis)):
qp.drawPoint(self.leftMargin + int(i / x_step), height - int(self.tdrWindow.td[i] / y_step))
id_max = np.argmax(self.tdrWindow.td)
@@ -1451,6 +1604,15 @@ def __init__(self, name=""):
self.setPalette(pal)
self.setAutoFillBackground(True)
+ def copy(self):
+ new_chart: RealImaginaryChart = super().copy()
+
+ new_chart.maxDisplayReal = self.maxDisplayReal
+ new_chart.maxDisplayImag = self.maxDisplayImag
+ new_chart.minDisplayReal = self.minDisplayReal
+ new_chart.minDisplayImag = self.minDisplayImag
+ return new_chart
+
def drawChart(self, qp: QtGui.QPainter):
qp.setPen(QtGui.QPen(self.textColor))
qp.drawText(self.leftMargin + 5, 15, self.name + " (\N{OHM SIGN})")
@@ -1466,9 +1628,9 @@ def drawValues(self, qp: QtGui.QPainter):
if len(self.data) == 0 and len(self.reference) == 0:
return
pen = QtGui.QPen(self.sweepColor)
- pen.setWidth(2)
+ pen.setWidth(self.pointSize)
line_pen = QtGui.QPen(self.sweepColor)
- line_pen.setWidth(1)
+ line_pen.setWidth(self.lineThickness)
highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255))
highlighter.setWidth(1)
if self.fixedSpan:
@@ -1579,18 +1741,10 @@ def drawValues(self, qp: QtGui.QPainter):
qp.drawText(3, self.chartHeight + self.topMargin, str(round(min_real, 1)))
qp.drawText(self.leftMargin + self.chartWidth + 8, self.chartHeight + self.topMargin, str(round(min_imag, 1)))
- qp.drawText(self.leftMargin-20, self.topMargin + self.chartHeight + 15, Chart.shortenFrequency(fstart))
- ticks = math.floor(self.chartWidth/100) # Number of ticks does not include the origin
- for i in range(ticks):
- x = self.leftMargin + round((i+1)*self.chartWidth/ticks)
- qp.setPen(QtGui.QPen(self.foregroundColor))
- qp.drawLine(x, self.topMargin - 5, x, self.topMargin + self.chartHeight + 5)
- qp.setPen(self.textColor)
- qp.drawText(x-20, self.topMargin + self.chartHeight + 15, Chart.shortenFrequency(round(fspan/ticks*(i+1) + fstart)))
+ self.drawFrequencyTicks(qp)
primary_pen = pen
secondary_pen = QtGui.QPen(self.secondarySweepColor)
- secondary_pen.setWidth(2)
if len(self.data) > 0:
c = QtGui.QColor(self.sweepColor)
c.setAlpha(255)
@@ -1604,6 +1758,10 @@ def drawValues(self, qp: QtGui.QPainter):
qp.setPen(pen)
qp.drawLine(self.leftMargin + self.chartWidth, 9, self.leftMargin + self.chartWidth + 5, 9)
+ primary_pen.setWidth(self.pointSize)
+ secondary_pen.setWidth(self.pointSize)
+ line_pen.setWidth(self.lineThickness)
+
for i in range(len(self.data)):
x = self.getXPosition(self.data[i])
y_re = self.getReYPosition(self.data[i])
@@ -1678,7 +1836,7 @@ def drawValues(self, qp: QtGui.QPainter):
prev_y_re = self.getReYPosition(self.reference[i-1])
prev_y_im = self.getImYPosition(self.reference[i-1])
- line_pen.setColor(self.secondaryReferenceColor)
+ line_pen.setColor(self.referenceColor)
qp.setPen(line_pen)
# Real part first
if self.isPlotable(x, y_re) and self.isPlotable(prev_x, prev_y_re):
diff --git a/NanoVNASaver/Hardware.py b/NanoVNASaver/Hardware.py
index c03d974d..26281910 100644
--- a/NanoVNASaver/Hardware.py
+++ b/NanoVNASaver/Hardware.py
@@ -47,7 +47,9 @@ def getVNA(app, serialPort: serial.Serial) -> 'VNA':
return NanoVNA_F(app, serialPort)
elif firmware.find("NanoVNA") > 0:
return NanoVNA(app, serialPort)
- return InvalidVNA(app, serialPort)
+ else:
+ logger.warning("Did not recognize NanoVNA type from firmware.")
+ return NanoVNA(app, serialPort)
def readFrequencies(self) -> List[str]:
pass
@@ -259,13 +261,13 @@ class Version:
def __init__(self, version_string):
self.version_string = version_string
- results = re.match(r"(.*\D+)?(\d+)\.(\d+)\.(\d+)(.*)", version_string)
+ results = re.match(r"(.*\s+)?(\d+)\.(\d+)\.(\d+)(.*)", version_string)
if results:
self.major = int(results.group(2))
self.minor = int(results.group(3))
self.revision = int(results.group(4))
self.note = results.group(5)
- logger.debug("Parsed version as %d.%d.%d%s", self.major, self.minor, self.revision, self.note)
+ logger.debug("Parsed version as \"%d.%d.%d%s\"", self.major, self.minor, self.revision, self.note)
@staticmethod
def getVersion(major: int, minor: int, revision: int, note=""):
diff --git a/NanoVNASaver/NanoVNASaver.py b/NanoVNASaver/NanoVNASaver.py
index 76c49a96..f5d7f079 100644
--- a/NanoVNASaver/NanoVNASaver.py
+++ b/NanoVNASaver/NanoVNASaver.py
@@ -57,7 +57,6 @@ def __init__(self):
else:
self.icon = QtGui.QIcon("icon_48x48.png")
self.setWindowIcon(self.icon)
-
self.settings = QtCore.QSettings(QtCore.QSettings.IniFormat,
QtCore.QSettings.UserScope,
"NanoVNASaver", "NanoVNASaver")
@@ -111,6 +110,8 @@ def __init__(self):
widget.setLayout(layout)
scrollarea.setWidget(widget)
+ # outer.setContentsMargins(2, 2, 2, 2) # Small screen mode, reduce margins?
+
self.s11SmithChart = SmithChart("S11 Smith Chart")
self.s21PolarChart = PolarChart("S21 Polar Plot")
self.s11LogMag = LogMagChart("S11 Return Loss")
@@ -120,7 +121,10 @@ def __init__(self):
self.s11VSWR = VSWRChart("S11 VSWR")
self.s11QualityFactor = QualityFactorChart("S11 Quality Factor")
self.s11RealImaginary = RealImaginaryChart("S11 R+jX")
+ self.tdr_chart = TDRChart("TDR")
+ self.tdr_mainwindow_chart = TDRChart("TDR")
+ # List of all the S11 charts, for selecting
self.s11charts: List[Chart] = []
self.s11charts.append(self.s11SmithChart)
self.s11charts.append(self.s11LogMag)
@@ -129,15 +133,23 @@ def __init__(self):
self.s11charts.append(self.s11RealImaginary)
self.s11charts.append(self.s11QualityFactor)
+ # List of all the S21 charts, for selecting
self.s21charts: List[Chart] = []
self.s21charts.append(self.s21PolarChart)
self.s21charts.append(self.s21LogMag)
self.s21charts.append(self.s21Phase)
- self.charts = self.s11charts + self.s21charts
+ # List of all charts that can be selected for display
+ self.selectable_charts = self.s11charts + self.s21charts
+ self.selectable_charts.append(self.tdr_mainwindow_chart)
- self.tdr_chart = TDRChart("TDR")
- self.charts.append(self.tdr_chart)
+ # List of all charts that subscribe to updates (including duplicates!)
+ self.subscribing_charts = []
+ self.subscribing_charts.extend(self.selectable_charts)
+ self.subscribing_charts.append(self.tdr_chart)
+
+ for c in self.subscribing_charts:
+ c.popoutRequested.connect(self.popoutChart)
self.charts_layout = QtWidgets.QGridLayout()
@@ -178,13 +190,13 @@ def __init__(self):
self.sweepStartInput.setMinimumWidth(60)
self.sweepStartInput.setAlignment(QtCore.Qt.AlignRight)
self.sweepStartInput.textEdited.connect(self.updateCenterSpan)
-
+ self.sweepStartInput.textChanged.connect(self.updateStepSize)
sweep_input_left_layout.addRow(QtWidgets.QLabel("Start"), self.sweepStartInput)
self.sweepEndInput = QtWidgets.QLineEdit("")
self.sweepEndInput.setAlignment(QtCore.Qt.AlignRight)
self.sweepEndInput.textEdited.connect(self.updateCenterSpan)
-
+ self.sweepEndInput.textChanged.connect(self.updateStepSize)
sweep_input_left_layout.addRow(QtWidgets.QLabel("Stop"), self.sweepEndInput)
self.sweepCenterInput = QtWidgets.QLineEdit("")
@@ -226,8 +238,10 @@ def __init__(self):
self.btnSweep = QtWidgets.QPushButton("Sweep")
self.btnSweep.clicked.connect(self.sweep)
+ self.btnSweep.setShortcut(QtCore.Qt.Key_W | QtCore.Qt.CTRL)
self.btnStopSweep = QtWidgets.QPushButton("Stop")
self.btnStopSweep.clicked.connect(self.stopSweep)
+ self.btnStopSweep.setShortcut(QtCore.Qt.Key_Escape)
self.btnStopSweep.setDisabled(True)
btn_layout = QtWidgets.QHBoxLayout()
btn_layout.addWidget(self.btnSweep)
@@ -279,7 +293,7 @@ def __init__(self):
self.showMarkerButton.clicked.connect(self.toggleMarkerFrame)
marker_control_layout.addRow(self.showMarkerButton)
- for c in self.charts:
+ for c in self.subscribing_charts:
c.setMarkers(self.markers)
c.setBands(self.bands)
left_column.addWidget(marker_control_box)
@@ -331,6 +345,9 @@ def __init__(self):
self.tdr_window = TDRWindow(self)
self.tdr_chart.tdrWindow = self.tdr_window
+ self.tdr_mainwindow_chart.tdrWindow = self.tdr_window
+ self.tdr_window.updated.connect(self.tdr_chart.update)
+ self.tdr_window.updated.connect(self.tdr_mainwindow_chart.update)
tdr_control_box = QtWidgets.QGroupBox()
tdr_control_box.setTitle("TDR")
@@ -799,7 +816,6 @@ def updateCenterSpan(self):
return
self.sweepSpanInput.setText(str(fspan))
self.sweepCenterInput.setText(str(fcenter))
- self.updateStepSize()
def updateStartEnd(self):
fcenter = self.parseFrequency(self.sweepCenterInput.text())
@@ -812,7 +828,6 @@ def updateStartEnd(self):
return
self.sweepStartInput.setText(str(fstart))
self.sweepEndInput.setText(str(fstop))
- self.updateStepSize()
def updateStepSize(self):
fspan = self.parseFrequency(self.sweepSpanInput.text())
@@ -931,7 +946,7 @@ def resetReference(self):
self.referenceS21data = []
self.referenceSource = ""
self.updateTitle()
- for c in self.charts:
+ for c in self.subscribing_charts:
c.resetReference()
self.btnResetReference.setDisabled(True)
@@ -991,6 +1006,23 @@ def showSweepError(self):
self.showError(self.worker.error_message)
self.stopSerial()
+ def popoutChart(self, chart: Chart):
+ logger.debug("Requested popout for chart: %s", chart.name)
+ new_chart = self.copyChart(chart)
+ new_chart.isPopout = True
+ new_chart.show()
+ new_chart.setWindowTitle(new_chart.name)
+
+ def copyChart(self, chart: Chart):
+ new_chart = chart.copy()
+ self.subscribing_charts.append(new_chart)
+ if chart in self.s11charts:
+ self.s11charts.append(new_chart)
+ if chart in self.s21charts:
+ self.s21charts.append(new_chart)
+ new_chart.popoutRequested.connect(self.popoutChart)
+ return new_chart
+
def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
self.worker.stopped = True
self.settings.setValue("Marker1Color", self.markers[0].color)
@@ -1016,9 +1048,12 @@ def __init__(self, app: NanoVNASaver):
shortcut = QtWidgets.QShortcut(QtCore.Qt.Key_Escape, self, self.hide)
- layout = QtWidgets.QVBoxLayout()
+ layout = QtWidgets.QHBoxLayout()
self.setLayout(layout)
+ left_layout = QtWidgets.QVBoxLayout()
+ layout.addLayout(left_layout)
+
display_options_box = QtWidgets.QGroupBox("Options")
display_options_layout = QtWidgets.QFormLayout(display_options_box)
@@ -1092,7 +1127,23 @@ def __init__(self, app: NanoVNASaver):
display_options_layout.addRow("Second reference color", self.btnSecondaryReferenceColorPicker)
- layout.addWidget(display_options_box)
+ self.pointSizeInput = QtWidgets.QSpinBox()
+ self.pointSizeInput.setValue(self.app.settings.value("PointSize", 2, int))
+ self.pointSizeInput.setMinimum(1)
+ self.pointSizeInput.setMaximum(10)
+ self.pointSizeInput.setSuffix(" px")
+ self.pointSizeInput.setAlignment(QtCore.Qt.AlignRight)
+ self.pointSizeInput.valueChanged.connect(self.changePointSize)
+ display_options_layout.addRow("Point size", self.pointSizeInput)
+
+ self.lineThicknessInput = QtWidgets.QSpinBox()
+ self.lineThicknessInput.setValue(self.app.settings.value("LineThickness", 1, int))
+ self.lineThicknessInput.setMinimum(1)
+ self.lineThicknessInput.setMaximum(10)
+ self.lineThicknessInput.setSuffix(" px")
+ self.lineThicknessInput.setAlignment(QtCore.Qt.AlignRight)
+ self.lineThicknessInput.valueChanged.connect(self.changeLineThickness)
+ display_options_layout.addRow("Line thickness", self.lineThicknessInput)
color_options_box = QtWidgets.QGroupBox("Chart colors")
color_options_layout = QtWidgets.QFormLayout(color_options_box)
@@ -1112,14 +1163,15 @@ def __init__(self, app: NanoVNASaver):
self.btn_foreground_picker.clicked.connect(lambda: self.setColor("foreground", QtWidgets.QColorDialog.getColor(self.foregroundColor, options=QtWidgets.QColorDialog.ShowAlphaChannel)))
color_options_layout.addRow("Chart foreground", self.btn_foreground_picker)
-
+
self.btn_text_picker = QtWidgets.QPushButton("█")
self.btn_text_picker.setFixedWidth(20)
self.btn_text_picker.clicked.connect(lambda: self.setColor("text", QtWidgets.QColorDialog.getColor(self.textColor, options=QtWidgets.QColorDialog.ShowAlphaChannel)))
color_options_layout.addRow("Chart text", self.btn_text_picker)
- layout.addWidget(color_options_box)
+ right_layout = QtWidgets.QVBoxLayout()
+ layout.addLayout(right_layout)
font_options_box = QtWidgets.QGroupBox("Font")
font_options_layout = QtWidgets.QFormLayout(font_options_box)
@@ -1134,8 +1186,6 @@ def __init__(self, app: NanoVNASaver):
self.font_dropdown.currentTextChanged.connect(self.changeFont)
font_options_layout.addRow("Font size", self.font_dropdown)
- layout.addWidget(font_options_box)
-
bands_box = QtWidgets.QGroupBox("Bands")
bands_layout = QtWidgets.QFormLayout(bands_box)
@@ -1157,7 +1207,45 @@ def __init__(self, app: NanoVNASaver):
bands_layout.addRow(self.btn_manage_bands)
- layout.addWidget(bands_box)
+ vswr_marker_box = QtWidgets.QGroupBox("VSWR Markers")
+ vswr_marker_layout = QtWidgets.QFormLayout(vswr_marker_box)
+
+ self.vswrMarkers: List[float] = self.app.settings.value("VSWRMarkers", [], float)
+
+ if isinstance(self.vswrMarkers, float):
+ if self.vswrMarkers == 0:
+ self.vswrMarkers = []
+ else:
+ # Single values from the .ini become floats rather than lists. Convert them.
+ self.vswrMarkers = [self.vswrMarkers]
+
+ self.btn_vswr_picker = QtWidgets.QPushButton("█")
+ self.btn_vswr_picker.setFixedWidth(20)
+ self.btn_vswr_picker.clicked.connect(lambda: self.setColor("vswr", QtWidgets.QColorDialog.getColor(self.vswrColor, options=QtWidgets.QColorDialog.ShowAlphaChannel)))
+
+ vswr_marker_layout.addRow("VSWR Markers", self.btn_vswr_picker)
+
+ self.vswr_marker_dropdown = QtWidgets.QComboBox()
+ vswr_marker_layout.addRow(self.vswr_marker_dropdown)
+
+ if len(self.vswrMarkers) == 0:
+ self.vswr_marker_dropdown.addItem("None")
+ else:
+ for m in self.vswrMarkers:
+ self.vswr_marker_dropdown.addItem(str(m))
+ for c in self.app.s11charts:
+ c.addSWRMarker(m)
+
+ self.vswr_marker_dropdown.setCurrentIndex(0)
+ btn_add_marker = QtWidgets.QPushButton("Add ...")
+ btn_remove_marker = QtWidgets.QPushButton("Remove")
+ vswr_marker_btn_layout = QtWidgets.QHBoxLayout()
+ vswr_marker_btn_layout.addWidget(btn_add_marker)
+ vswr_marker_btn_layout.addWidget(btn_remove_marker)
+ vswr_marker_layout.addRow(vswr_marker_btn_layout)
+
+ btn_add_marker.clicked.connect(self.addVSWRMarker)
+ btn_remove_marker.clicked.connect(self.removeVSWRMarker)
charts_box = QtWidgets.QGroupBox("Displayed charts")
charts_layout = QtWidgets.QGridLayout(charts_box)
@@ -1173,7 +1261,7 @@ def __init__(self, app: NanoVNASaver):
selections = []
- for c in self.app.charts:
+ for c in self.app.selectable_charts:
selections.append(c.name)
selections.append("None")
@@ -1189,7 +1277,7 @@ def __init__(self, app: NanoVNASaver):
chart01_selection.setCurrentIndex(selections.index(self.app.settings.value("Chart01", "S11 Return Loss")))
chart01_selection.currentTextChanged.connect(lambda: self.changeChart(0, 1, chart01_selection.currentText()))
charts_layout.addWidget(chart01_selection, 0, 1)
-
+
chart02_selection = QtWidgets.QComboBox()
chart02_selection.addItems(selections)
chart02_selection.setCurrentIndex(selections.index(self.app.settings.value("Chart02", "None")))
@@ -1221,10 +1309,6 @@ def __init__(self, app: NanoVNASaver):
self.changeChart(1, 1, chart11_selection.currentText())
self.changeChart(1, 2, chart12_selection.currentText())
- layout.addWidget(charts_box)
- self.dark_mode_option.setChecked(self.app.settings.value("DarkMode", False, bool))
- self.show_lines_option.setChecked(self.app.settings.value("ShowLines", False, bool))
-
self.backgroundColor = self.app.settings.value("BackgroundColor", defaultValue=QtGui.QColor("white"),
type=QtGui.QColor)
self.foregroundColor = self.app.settings.value("ForegroundColor", defaultValue=QtGui.QColor("lightgray"),
@@ -1234,6 +1318,11 @@ def __init__(self, app: NanoVNASaver):
self.bandsColor = self.app.settings.value("BandsColor", defaultValue=QtGui.QColor(128, 128, 128, 48),
type=QtGui.QColor)
self.app.bands.color = self.bandsColor
+ self.vswrColor = self.app.settings.value("VSWRColor", defaultValue=QtGui.QColor(192, 0, 0, 128),
+ type=QtGui.QColor)
+
+ self.dark_mode_option.setChecked(self.app.settings.value("DarkMode", False, bool))
+ self.show_lines_option.setChecked(self.app.settings.value("ShowLines", False, bool))
if self.app.settings.value("UseCustomColors", defaultValue=False, type=bool):
self.dark_mode_option.setDisabled(True)
@@ -1260,9 +1349,22 @@ def __init__(self, app: NanoVNASaver):
p.setColor(QtGui.QPalette.ButtonText, self.bandsColor)
self.btn_bands_picker.setPalette(p)
+ p = self.btn_vswr_picker.palette()
+ p.setColor(QtGui.QPalette.ButtonText, self.vswrColor)
+ self.btn_vswr_picker.setPalette(p)
+
+ left_layout.addWidget(display_options_box)
+ left_layout.addWidget(charts_box)
+ left_layout.addStretch(1)
+
+ right_layout.addWidget(color_options_box)
+ right_layout.addWidget(font_options_box)
+ right_layout.addWidget(bands_box)
+ right_layout.addWidget(vswr_marker_box)
+
def changeChart(self, x, y, chart):
found = None
- for c in self.app.charts:
+ for c in self.app.selectable_charts:
if c.name == chart:
found = c
@@ -1274,6 +1376,10 @@ def changeChart(self, x, y, chart):
self.app.charts_layout.removeWidget(w)
w.hide()
if found is not None:
+ if self.app.charts_layout.indexOf(found) > -1:
+ logger.debug("%s is already shown, duplicating.", found.name)
+ found = self.app.copyChart(found)
+
self.app.charts_layout.addWidget(found, x, y)
if found.isHidden():
found.show()
@@ -1291,22 +1397,34 @@ def changeReturnLoss(self):
def changeShowLines(self):
state = self.show_lines_option.isChecked()
self.app.settings.setValue("ShowLines", state)
- for c in self.app.charts:
+ for c in self.app.subscribing_charts:
c.setDrawLines(state)
+ def changePointSize(self, size: int):
+ self.app.settings.setValue("PointSize", size)
+ for c in self.app.subscribing_charts:
+ c.setPointSize(size)
+
+ def changeLineThickness(self, size: int):
+ self.app.settings.setValue("LineThickness", size)
+ for c in self.app.subscribing_charts:
+ c.setLineThickness(size)
+
def changeDarkMode(self):
state = self.dark_mode_option.isChecked()
self.app.settings.setValue("DarkMode", state)
if state:
- for c in self.app.charts:
+ for c in self.app.subscribing_charts:
c.setBackgroundColor(QtGui.QColor(QtCore.Qt.black))
c.setForegroundColor(QtGui.QColor(QtCore.Qt.lightGray))
c.setTextColor(QtGui.QColor(QtCore.Qt.white))
+ c.setSWRColor(self.vswrColor)
else:
- for c in self.app.charts:
+ for c in self.app.subscribing_charts:
c.setBackgroundColor(QtGui.QColor(QtCore.Qt.white))
c.setForegroundColor(QtGui.QColor(QtCore.Qt.lightGray))
c.setTextColor(QtGui.QColor(QtCore.Qt.black))
+ c.setSWRColor(self.vswrColor)
def changeCustomColors(self):
self.app.settings.setValue("UseCustomColors", self.use_custom_colors.isChecked())
@@ -1316,10 +1434,11 @@ def changeCustomColors(self):
self.btn_background_picker.setDisabled(False)
self.btn_foreground_picker.setDisabled(False)
self.btn_text_picker.setDisabled(False)
- for c in self.app.charts:
+ for c in self.app.subscribing_charts:
c.setBackgroundColor(self.backgroundColor)
c.setForegroundColor(self.foregroundColor)
c.setTextColor(self.textColor)
+ c.setSWRColor(self.vswrColor)
else:
self.dark_mode_option.setDisabled(False)
self.btn_background_picker.setDisabled(True)
@@ -1353,6 +1472,12 @@ def setColor(self, name: str, color: QtGui.QColor):
self.bandsColor = color
self.app.settings.setValue("BandsColor", color)
self.app.bands.setColor(color)
+ elif name == "vswr":
+ p = self.btn_vswr_picker.palette()
+ p.setColor(QtGui.QPalette.ButtonText, color)
+ self.btn_vswr_picker.setPalette(p)
+ self.vswrColor = color
+ self.app.settings.setValue("VSWRColor", color)
self.changeCustomColors()
def setSweepColor(self, color: QtGui.QColor):
@@ -1363,7 +1488,7 @@ def setSweepColor(self, color: QtGui.QColor):
self.btnColorPicker.setPalette(p)
self.app.settings.setValue("SweepColor", color)
self.app.settings.sync()
- for c in self.app.charts:
+ for c in self.app.subscribing_charts:
c.setSweepColor(color)
def setSecondarySweepColor(self, color: QtGui.QColor):
@@ -1374,7 +1499,7 @@ def setSecondarySweepColor(self, color: QtGui.QColor):
self.btnSecondaryColorPicker.setPalette(p)
self.app.settings.setValue("SecondarySweepColor", color)
self.app.settings.sync()
- for c in self.app.charts:
+ for c in self.app.subscribing_charts:
c.setSecondarySweepColor(color)
def setReferenceColor(self, color):
@@ -1386,7 +1511,7 @@ def setReferenceColor(self, color):
self.app.settings.setValue("ReferenceColor", color)
self.app.settings.sync()
- for c in self.app.charts:
+ for c in self.app.subscribing_charts:
c.setReferenceColor(color)
def setSecondaryReferenceColor(self, color):
@@ -1398,14 +1523,14 @@ def setSecondaryReferenceColor(self, color):
self.app.settings.setValue("SecondaryReferenceColor", color)
self.app.settings.sync()
- for c in self.app.charts:
+ for c in self.app.subscribing_charts:
c.setSecondaryReferenceColor(color)
def setShowBands(self, show_bands):
self.app.bands.enabled = show_bands
self.app.bands.settings.setValue("ShowBands", show_bands)
self.app.bands.settings.sync()
- for c in self.app.charts:
+ for c in self.app.subscribing_charts:
c.update()
def changeFont(self):
@@ -1420,6 +1545,33 @@ def displayBandsWindow(self):
self.bandsWindow.show()
QtWidgets.QApplication.setActiveWindow(self.bandsWindow)
+ def addVSWRMarker(self):
+ value, selected = QtWidgets.QInputDialog.getDouble(self, "Add VSWR Marker",
+ "VSWR value to show:", min=1.001, decimals=3)
+ if selected:
+ self.vswrMarkers.append(value)
+ if self.vswr_marker_dropdown.itemText(0) == "None":
+ self.vswr_marker_dropdown.removeItem(0)
+ self.vswr_marker_dropdown.addItem(str(value))
+ self.vswr_marker_dropdown.setCurrentText(str(value))
+ for c in self.app.s11charts:
+ c.addSWRMarker(value)
+ self.app.settings.setValue("VSWRMarkers", self.vswrMarkers)
+
+ def removeVSWRMarker(self):
+ value_str = self.vswr_marker_dropdown.currentText()
+ if value_str != "None":
+ value = float(value_str)
+ self.vswrMarkers.remove(value)
+ self.vswr_marker_dropdown.removeItem(self.vswr_marker_dropdown.currentIndex())
+ if self.vswr_marker_dropdown.count() == 0:
+ self.vswr_marker_dropdown.addItem("None")
+ self.app.settings.remove("VSWRMarkers")
+ else:
+ self.app.settings.setValue("VSWRMarkers", self.vswrMarkers)
+ for c in self.app.s11charts:
+ c.removeSWRMarker(value)
+
class AboutWindow(QtWidgets.QWidget):
def __init__(self, app: NanoVNASaver):
@@ -1475,7 +1627,7 @@ def __init__(self, app: NanoVNASaver):
check_for_updates = self.app.settings.value("CheckForUpdates", "Ask")
if check_for_updates == "Yes":
self.updateCheckBox.setChecked(True)
- self.findUpdates(automatic = True)
+ self.findUpdates(automatic=True)
elif check_for_updates == "No":
self.updateCheckBox.setChecked(False)
else:
@@ -1535,7 +1687,9 @@ def findUpdates(self, automatic=False):
update_url = "http://mihtjel.dk/nanovna-saver/latest.json"
try:
- updates = json.load(request.urlopen(update_url, timeout=3))
+ req = request.Request(update_url)
+ req.add_header('User-Agent', "NanoVNA-Saver/" + self.app.version)
+ updates = json.load(request.urlopen(req, timeout=3))
latest_version = Version(updates['version'])
latest_url = updates['url']
except error.HTTPError as e:
@@ -1571,6 +1725,8 @@ def findUpdates(self, automatic=False):
class TDRWindow(QtWidgets.QWidget):
+ updated = QtCore.pyqtSignal()
+
def __init__(self, app: NanoVNASaver):
super().__init__()
self.app = app
@@ -1682,7 +1838,7 @@ def updateTDR(self):
self.tdr_result_label.setText(str(cable_len) + " m (" + str(feet) + "ft " + str(inches) + "in)")
self.app.tdr_result_label.setText(str(cable_len) + " m")
- self.app.tdr_chart.update()
+ self.updated.emit()
class SweepSettingsWindow(QtWidgets.QWidget):
diff --git a/NanoVNASaver/about.py b/NanoVNASaver/about.py
index c7da89dd..10ea0f83 100644
--- a/NanoVNASaver/about.py
+++ b/NanoVNASaver/about.py
@@ -14,5 +14,5 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-version = '0.1.2'
+version = '0.1.3'
debug = False