Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Live View for viewing monitor screen of NanoVNA and TinySA in real time #762

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/NanoVNASaver/Hardware/Hardware.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ def get_VNA(iface: Interface) -> VNA:
# serial_port.timeout = TIMEOUT
return NAME2DEVICE[iface.comment](iface)


def get_comment(iface: Interface) -> str:
logger.info("Finding correct VNA type...")
with iface.lock:
Expand Down
40 changes: 39 additions & 1 deletion src/NanoVNASaver/Hardware/NanoVNA_F_V2.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

from NanoVNASaver.Hardware.NanoVNA import NanoVNA
from NanoVNASaver.Hardware.Serial import Interface
from NanoVNASaver.Version import Version

logger = logging.getLogger(__name__)

Expand All @@ -31,10 +32,25 @@ class NanoVNA_F_V2(NanoVNA):
name = "NanoVNA-F_V2"
screenwidth = 800
screenheight = 480

valid_datapoints = (101, 11, 51, 201, 301)
sweep_points_min = 11
sweep_points_max = 301

def __init__(self, iface: Interface):
super().__init__(iface)
self.sweep_max_freq_Hz = 3e9
self.version = self.read_firmware_version()
# max datapoints reach up to 301 since version 0.5.0
if self.version >= Version("0.5.0"):
pass
# max datapoints reach up to 201 since version 0.2.0
elif self.version >= Version("0.2.0"):
self.valid_datapoints = (101, 11, 51, 201)
self.sweep_points_max = 201
# max datapoints reach up to 101 before version 0.2.0
else:
self.valid_datapoints = (101, 11, 51)
self.sweep_points_max = 101

def getScreenshot(self) -> QPixmap:
logger.debug("Capturing screenshot...")
Expand All @@ -53,3 +69,25 @@ def getScreenshot(self) -> QPixmap:
except serial.SerialException as exc:
logger.exception("Exception while capturing screenshot: %s", exc)
return QPixmap()

def read_firmware_version(self) -> "Version":
"""For example, command version in NanoVNA_F_V2 and NanoVNA_F_V3 returns as this
0.5.8
"""
result = list(self.exec_command("version"))
logger.debug("firmware version result:\n%s", result[0])
return Version(result[0])

def read_features(self):
super().read_features()
result = " ".join(self.exec_command("help")).split()
if "sn:" or "SN:" in result:
self.features.add("SN")
self.SN = self.getSerialNumber()

def getSerialNumber(self) -> str:
return (
" ".join(list(self.exec_command("SN")))
if "SN:" in " ".join(self.exec_command("help")).split()
else " ".join(list(self.exec_command("sn")))
)
1 change: 0 additions & 1 deletion src/NanoVNASaver/Hardware/TinySA.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ class TinySA_Ultra(TinySA): # noqa: N801
screenwidth = 480
screenheight = 320
valid_datapoints = (450, 51, 101, 145, 290)
hardware_revision = None

def __init__(self, iface: Interface):
super().__init__(iface)
Expand Down
1 change: 1 addition & 0 deletions src/NanoVNASaver/Hardware/VNA.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class VNA:
valid_datapoints = (101, 51, 11)
wait = 0.05
SN = "NOT SUPPORTED"
hardware_revision = "NOT SUPPORTED"
sweep_points_max = 101
sweep_points_min = 11

Expand Down
23 changes: 21 additions & 2 deletions src/NanoVNASaver/Windows/DeviceSettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

from NanoVNASaver.SweepWorker import SweepState
from NanoVNASaver.Windows.Defaults import make_scrollable
from NanoVNASaver.Windows.Screenshot import ScreenshotWindow
from NanoVNASaver.Windows.Screenshot import ScreenshotWindow, LiveViewWindow

logger = logging.getLogger(__name__)

Expand All @@ -44,6 +44,7 @@ def __init__(self, app: QtWidgets.QWidget) -> None: # noqa: PLR0915
self.label = {
"status": QtWidgets.QLabel("Not connected."),
"firmware": QtWidgets.QLabel("Not connected."),
"hardware": QtWidgets.QLabel("Not connected."),
"calibration": QtWidgets.QLabel("Not connected."),
"SN": QtWidgets.QLabel("Not connected."),
}
Expand All @@ -60,6 +61,7 @@ def __init__(self, app: QtWidgets.QWidget) -> None: # noqa: PLR0915

status_layout.addRow("Status:", self.label["status"])
status_layout.addRow("Firmware:", self.label["firmware"])
status_layout.addRow("Hardware:", self.label["hardware"])
status_layout.addRow("Calibration:", self.label["calibration"])
status_layout.addRow("SN:", self.label["SN"])

Expand Down Expand Up @@ -90,7 +92,13 @@ def __init__(self, app: QtWidgets.QWidget) -> None: # noqa: PLR0915
self.btnCaptureScreenshot = QtWidgets.QPushButton("Screenshot")
self.btnCaptureScreenshot.clicked.connect(self.captureScreenshot)
control_layout.addWidget(self.btnCaptureScreenshot)


self.liveViewWindow = LiveViewWindow(self)
self.btnLiveView = QtWidgets.QPushButton("Live view")
self.btnLiveView.clicked.connect(self.liveView)
self.liveViewWindow.setAttribute(QtCore.Qt.WidgetAttribute.WA_DeleteOnClose)
control_layout.addWidget(self.btnLiveView)

left_layout.addWidget(status_box)
left_layout.addLayout(control_layout)

Expand Down Expand Up @@ -134,16 +142,21 @@ def updateFields(self):
if not self.app.vna.connected():
self.label["status"].setText("Not connected.")
self.label["firmware"].setText("Not connected.")
self.label["hardware"].setText("Not connected.")
self.label["calibration"].setText("Not connected.")
self.label["SN"].setText("Not connected.")
self.featureList.clear()
self.btnCaptureScreenshot.setDisabled(True)
self.btnLiveView.setDisabled(True)
return

self.label["status"].setText(f"Connected to {self.app.vna.name}.")
self.label["firmware"].setText(
f"{self.app.vna.name} v{self.app.vna.version}"
)
self.label["hardware"].setText(
f"{self.app.vna.hardware_revision}"
)
if self.app.worker.state == SweepState.RUNNING:
self.label["calibration"].setText("(Sweep running)")
else:
Expand All @@ -155,6 +168,7 @@ def updateFields(self):
self.featureList.addItem(item)

self.btnCaptureScreenshot.setDisabled("Screenshots" not in features)
self.btnLiveView.setDisabled("Screenshots" not in features)

if "Customizable data points" in features:
self.datapoints.clear()
Expand Down Expand Up @@ -194,6 +208,11 @@ def captureScreenshot(self) -> None:
# TODO: Consider having a list of widgets that want to be
# disabled when a sweep is running?


def liveView(self) -> None:
if self.app.worker.state != SweepState.RUNNING:
self.liveViewWindow.start()

def updateNrDatapoints(self, i) -> None:
if i < 0 or self.app.worker.state == SweepState.RUNNING:
return
Expand Down
20 changes: 20 additions & 0 deletions src/NanoVNASaver/Windows/Screenshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import logging

from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6.QtCore import QTimer,QDateTime
from NanoVNASaver.SweepWorker import SweepState
from PyQt6.QtCore import Qt

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -116,3 +119,20 @@ def setScale(self, scale):
self.pix.size().height() * scale,
)
self.resize(width, height)

class LiveViewWindow(ScreenshotWindow):
def __init__(self, qtwidgets: QtWidgets.QTableWidget):
super().__init__()
self.setWindowTitle("Live View")
self.qtwidgets = qtwidgets
self.timer = QTimer(self)
self.timer.timeout.connect(self.update_screenshot)

def start(self):
self.timer.start(2000) # Update every 2000ms (this will not burn the little chip too much on nanovna & tinysa)

def update_screenshot(self):
if self.qtwidgets.app.worker.state != SweepState.RUNNING: # Check if worker is not running
pixmap = self.qtwidgets.app.vna.getScreenshot()
self.qtwidgets.liveViewWindow.setScreenshot(pixmap)
self.qtwidgets.liveViewWindow.show()
Loading