kopia lustrzana https://github.com/NanoVNA-Saver/nanovna-saver
rodzic
b322d3dc09
commit
dd2f5b8a5d
|
@ -52,3 +52,4 @@ MANIFEST
|
||||||
.venv*/
|
.venv*/
|
||||||
.conda*/
|
.conda*/
|
||||||
.python-version
|
.python-version
|
||||||
|
**/_version.py
|
||||||
|
|
|
@ -1,33 +1,26 @@
|
||||||
.. role:: raw-html-m2r(raw)
|
.. role:: raw-html-m2r(raw)
|
||||||
:format: html
|
:format: html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. image:: https://img.shields.io/github/v/release/NanoVNA-Saver/nanovna-saver.svg
|
.. image:: https://img.shields.io/github/v/release/NanoVNA-Saver/nanovna-saver.svg
|
||||||
:target: https://github.com/NanoVNA-Saver/nanovna-saver/releases/latest
|
:target: https://github.com/NanoVNA-Saver/nanovna-saver/releases/latest
|
||||||
:alt: Latest Release
|
:alt: Latest Release
|
||||||
|
|
||||||
|
|
||||||
.. image:: https://img.shields.io/github/license/NanoVNA-Saver/nanovna-saver.svg
|
.. image:: https://img.shields.io/github/license/NanoVNA-Saver/nanovna-saver.svg
|
||||||
:target: https://github.com/NanoVNA-Saver/nanovna-saver/blob/master/LICENSE.txt
|
:target: https://github.com/NanoVNA-Saver/nanovna-saver/blob/master/LICENSE.txt
|
||||||
:alt: License
|
:alt: License
|
||||||
|
|
||||||
|
|
||||||
.. image:: https://img.shields.io/github/downloads/NanoVNA-Saver/nanovna-saver/total.svg
|
.. image:: https://img.shields.io/github/downloads/NanoVNA-Saver/nanovna-saver/total.svg
|
||||||
:target: https://github.com/NanoVNA-Saver/nanovna-saver/releases/
|
:target: https://github.com/NanoVNA-Saver/nanovna-saver/releases/
|
||||||
:alt: Downloads
|
:alt: Downloads
|
||||||
|
|
||||||
|
|
||||||
.. image:: https://img.shields.io/github/downloads/NanoVNA-Saver/nanovna-saver/latest/total
|
.. image:: https://img.shields.io/github/downloads/NanoVNA-Saver/nanovna-saver/latest/total
|
||||||
:target: https://github.com/NanoVNA-Saver/nanovna-saver/releases/latest
|
:target: https://github.com/NanoVNA-Saver/nanovna-saver/releases/latest
|
||||||
:alt: GitHub Releases
|
:alt: GitHub Releases
|
||||||
|
|
||||||
|
|
||||||
.. image:: https://img.shields.io/badge/paypal-donate-yellow.svg
|
.. image:: https://img.shields.io/badge/paypal-donate-yellow.svg
|
||||||
:target: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=T8KTGVDQF5K6E&item_name=NanoVNASaver+Development¤cy_code=EUR&source=url
|
:target: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=T8KTGVDQF5K6E&item_name=NanoVNASaver+Development¤cy_code=EUR&source=url
|
||||||
:alt: Donate
|
:alt: Donate
|
||||||
|
|
||||||
|
|
||||||
NanoVNASaver
|
NanoVNASaver
|
||||||
============
|
============
|
||||||
|
|
||||||
|
@ -57,6 +50,7 @@ Current features
|
||||||
|
|
||||||
* Reading data from a NanoVNA -- Compatible devices: NanoVNA, NanoVNA-H,
|
* Reading data from a NanoVNA -- Compatible devices: NanoVNA, NanoVNA-H,
|
||||||
NanoVNA-H4, NanoVNA-F, AVNA via Teensy
|
NanoVNA-H4, NanoVNA-F, AVNA via Teensy
|
||||||
|
* Reading data from a TinySA
|
||||||
* Splitting a frequency range into multiple segments to increase resolution
|
* Splitting a frequency range into multiple segments to increase resolution
|
||||||
(tried up to >10k points)
|
(tried up to >10k points)
|
||||||
* Averaging data for better results particularly at higher frequencies
|
* Averaging data for better results particularly at higher frequencies
|
||||||
|
@ -189,6 +183,7 @@ Latest Changes
|
||||||
* Using PyQt6
|
* Using PyQt6
|
||||||
* Moved to PyScaffold project structure
|
* Moved to PyScaffold project structure
|
||||||
* Fixed crash in resonance analysis
|
* Fixed crash in resonance analysis
|
||||||
|
* Added TinySA readout and screenshot
|
||||||
|
|
||||||
|
|
||||||
Changes in 0.5.5
|
Changes in 0.5.5
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
[build-system]
|
[build-system]
|
||||||
# AVOID CHANGING REQUIRES: IT WILL BE UPDATED BY PYSCAFFOLD!
|
# AVOID CHANGING REQUIRES: IT WILL BE UPDATED BY PYSCAFFOLD!
|
||||||
requires = ["setuptools>=46.1.0", "setuptools_scm[toml]>=5"]
|
requires = ["setuptools>=46.1.0", "setuptools_scm[toml]>=6.2"]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[NanoVNASaver]
|
||||||
|
dynamic = ["version"]
|
||||||
|
|
||||||
[tool.setuptools_scm]
|
[tool.setuptools_scm]
|
||||||
# For smarter version schemes and other configuration options,
|
# For smarter version schemes and other configuration options,
|
||||||
# check out https://github.com/pypa/setuptools_scm
|
# check out https://github.com/pypa/setuptools_scm
|
||||||
version_scheme = "no-guess-dev"
|
version_scheme = "no-guess-dev"
|
||||||
|
write_to = "src/NanoVNASaver/_version.py"
|
||||||
|
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
pythonpath = [
|
pythonpath = [
|
||||||
|
|
|
@ -3,3 +3,5 @@ PyQt6==5.15.9
|
||||||
numpy==1.24.2
|
numpy==1.24.2
|
||||||
scipy==1.10.1
|
scipy==1.10.1
|
||||||
Cython==0.29.33
|
Cython==0.29.33
|
||||||
|
setuptools==65.3.0
|
||||||
|
setuptools-scm==7.1.0
|
||||||
|
|
|
@ -17,14 +17,16 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
VERSION = "0.6.0-pre"
|
from setuptools_scm import get_version
|
||||||
|
version = get_version() # root='.', relative_to=__file__)
|
||||||
|
|
||||||
VERSION_URL = (
|
VERSION_URL = (
|
||||||
"https://raw.githubusercontent.com/"
|
"https://raw.githubusercontent.com/"
|
||||||
"NanoVNA-Saver/nanovna-saver/master/NanoVNASaver/About.py"
|
"NanoVNA-Saver/nanovna-saver/master/NanoVNASaver/About.py"
|
||||||
)
|
)
|
||||||
|
|
||||||
INFO_URL = "https://github.com/NanoVNA-Saver/nanovna-saver"
|
INFO_URL = "https://github.com/NanoVNA-Saver/nanovna-saver"
|
||||||
INFO = f"""NanoVNASaver {VERSION}
|
INFO = f"""NanoVNASaver {version}
|
||||||
|
|
||||||
Copyright (C) 2019, 2020 Rune B. Broberg
|
Copyright (C) 2019, 2020 Rune B. Broberg
|
||||||
Copyright (C) 2020ff NanoVNA-Saver Authors
|
Copyright (C) 2020ff NanoVNA-Saver Authors
|
||||||
|
|
|
@ -71,7 +71,7 @@ class PeakSearchAnalysis(SimplePeakSearchAnalysis):
|
||||||
|
|
||||||
# Having found the peaks, get the prominence data
|
# Having found the peaks, get the prominence data
|
||||||
for i, p in np.ndenumerate(peaks):
|
for i, p in np.ndenumerate(peaks):
|
||||||
logger.debug("Peak %i at %d", i, p)
|
logger.debug("Peak %s at %s", i, p)
|
||||||
prominences = peak_prominences(data, peaks)[0]
|
prominences = peak_prominences(data, peaks)[0]
|
||||||
logger.debug("%d prominences", len(prominences))
|
logger.debug("%d prominences", len(prominences))
|
||||||
|
|
||||||
|
|
|
@ -219,16 +219,21 @@ class Chart(QtWidgets.QWidget):
|
||||||
if event.buttons() == Qt.MouseButton.MiddleButton:
|
if event.buttons() == Qt.MouseButton.MiddleButton:
|
||||||
# Drag event
|
# Drag event
|
||||||
event.accept()
|
event.accept()
|
||||||
self.dragbox.move_x = event.x()
|
self.dragbox.move_x = event.position().x()
|
||||||
self.dragbox.move_y = event.y()
|
self.dragbox.move_y = event.position().y()
|
||||||
return
|
return
|
||||||
if event.modifiers() == Qt.KeyboardModifier.ControlModifier:
|
if event.modifiers() == Qt.KeyboardModifier.ControlModifier:
|
||||||
event.accept()
|
event.accept()
|
||||||
self.dragbox.state = True
|
self.dragbox.state = True
|
||||||
self.dragbox.pos_start = (event.x(), event.y())
|
self.dragbox.pos_start = (
|
||||||
|
event.position().x(),
|
||||||
|
event.position().y(),
|
||||||
|
)
|
||||||
return
|
return
|
||||||
if event.modifiers() == Qt.KeyboardModifier.ShiftModifier:
|
if event.modifiers() == Qt.KeyboardModifier.ShiftModifier:
|
||||||
self.draggedMarker = self.getNearestMarker(event.x(), event.y())
|
self.draggedMarker = self.getNearestMarker(
|
||||||
|
event.position().x(), event.position().y()
|
||||||
|
)
|
||||||
self.mouseMoveEvent(event)
|
self.mouseMoveEvent(event)
|
||||||
|
|
||||||
def mouseReleaseEvent(self, a0: QtGui.QMouseEvent):
|
def mouseReleaseEvent(self, a0: QtGui.QMouseEvent):
|
||||||
|
|
|
@ -311,14 +311,15 @@ class FrequencyChart(Chart):
|
||||||
)
|
)
|
||||||
if not selected:
|
if not selected:
|
||||||
return
|
return
|
||||||
|
text = text.replace("dB", "")
|
||||||
min_val = parse_value(text)
|
min_val = parse_value(text)
|
||||||
yspan = abs(self.maxDisplayValue - self.minDisplayValue)
|
yspan = abs(self.maxDisplayValue - self.minDisplayValue)
|
||||||
self.minDisplayValue = min_val
|
self.minDisplayValue = min_val
|
||||||
if self.minDisplayValue >= self.maxDisplayValue:
|
if self.minDisplayValue >= self.maxDisplayValue:
|
||||||
self.maxDisplayValue = self.minDisplayValue + yspan
|
self.maxDisplayValue = self.minDisplayValue + yspan
|
||||||
# TODO: negativ logarythmical scale
|
# TODO: negativ logarythmical scale
|
||||||
if self.logarithmicY and min_val <= 0:
|
# if self.logarithmicY and min_val <= 0:
|
||||||
self.minDisplayValue = 0.01
|
# self.minDisplayValue = 0.01
|
||||||
self.fixedValues = True
|
self.fixedValues = True
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
@ -329,6 +330,7 @@ class FrequencyChart(Chart):
|
||||||
"Set maximum value",
|
"Set maximum value",
|
||||||
text=format_y_axis(self.maxDisplayValue, self.name_unit),
|
text=format_y_axis(self.maxDisplayValue, self.name_unit),
|
||||||
)
|
)
|
||||||
|
text = text.replace("dB", "")
|
||||||
if not selected:
|
if not selected:
|
||||||
return
|
return
|
||||||
max_val = parse_value(text)
|
max_val = parse_value(text)
|
||||||
|
@ -627,7 +629,7 @@ class FrequencyChart(Chart):
|
||||||
ticks = math.floor(self.dim.width / 100)
|
ticks = math.floor(self.dim.width / 100)
|
||||||
|
|
||||||
# try to adapt format to span
|
# try to adapt format to span
|
||||||
if int(fspan / ticks / self.fstart * 10000) > 2:
|
if self.fstart == 0 or int(fspan / ticks / self.fstart * 10000) > 2:
|
||||||
my_format_frequency = format_frequency_chart
|
my_format_frequency = format_frequency_chart
|
||||||
else:
|
else:
|
||||||
my_format_frequency = format_frequency_chart_2
|
my_format_frequency = format_frequency_chart_2
|
||||||
|
|
|
@ -97,7 +97,7 @@ class SerialControl(Control):
|
||||||
logger.error("Unable to connect to VNA: %s", exc)
|
logger.error("Unable to connect to VNA: %s", exc)
|
||||||
|
|
||||||
self.app.vna.validateInput = self.app.settings.value(
|
self.app.vna.validateInput = self.app.settings.value(
|
||||||
"SerialInputValidation", True, bool
|
"SerialInputValidation", False, bool
|
||||||
)
|
)
|
||||||
|
|
||||||
# connected
|
# connected
|
||||||
|
|
|
@ -34,7 +34,7 @@ from NanoVNASaver.Hardware.NanoVNA_F_V2 import NanoVNA_F_V2
|
||||||
from NanoVNASaver.Hardware.NanoVNA_H import NanoVNA_H
|
from NanoVNASaver.Hardware.NanoVNA_H import NanoVNA_H
|
||||||
from NanoVNASaver.Hardware.NanoVNA_H4 import NanoVNA_H4
|
from NanoVNASaver.Hardware.NanoVNA_H4 import NanoVNA_H4
|
||||||
from NanoVNASaver.Hardware.NanoVNA_V2 import NanoVNA_V2
|
from NanoVNASaver.Hardware.NanoVNA_V2 import NanoVNA_V2
|
||||||
from NanoVNASaver.Hardware.TinySA import TinySA
|
from NanoVNASaver.Hardware.TinySA import TinySA, TinySA_Ultra
|
||||||
from NanoVNASaver.Hardware.Serial import drain_serial, Interface
|
from NanoVNASaver.Hardware.Serial import drain_serial, Interface
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -59,6 +59,7 @@ NAME2DEVICE = {
|
||||||
"F": NanoVNA_F,
|
"F": NanoVNA_F,
|
||||||
"NanoVNA": NanoVNA,
|
"NanoVNA": NanoVNA,
|
||||||
"tinySA": TinySA,
|
"tinySA": TinySA,
|
||||||
|
"tinySA_Ultra": TinySA_Ultra,
|
||||||
"Unknown": NanoVNA,
|
"Unknown": NanoVNA,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,6 +153,7 @@ def get_comment(iface: Interface) -> str:
|
||||||
("NanoVNA-F_V2", "F_V2"),
|
("NanoVNA-F_V2", "F_V2"),
|
||||||
("NanoVNA-F", "F"),
|
("NanoVNA-F", "F"),
|
||||||
("NanoVNA", "NanoVNA"),
|
("NanoVNA", "NanoVNA"),
|
||||||
|
("tinySA4", "tinySA_Ultra"),
|
||||||
("tinySA", "tinySA"),
|
("tinySA", "tinySA"),
|
||||||
):
|
):
|
||||||
if info.find(search) >= 0:
|
if info.find(search) >= 0:
|
||||||
|
|
|
@ -22,7 +22,7 @@ from typing import List
|
||||||
|
|
||||||
import serial
|
import serial
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from PyQt6 import QtGui
|
from PyQt6.QtGui import QImage, QPixmap
|
||||||
|
|
||||||
from NanoVNASaver.Hardware.Serial import drain_serial, Interface
|
from NanoVNASaver.Hardware.Serial import drain_serial, Interface
|
||||||
from NanoVNASaver.Hardware.VNA import VNA
|
from NanoVNASaver.Hardware.VNA import VNA
|
||||||
|
@ -82,23 +82,23 @@ class NanoVNA(VNA):
|
||||||
+ ((rgb_array & 0x001F) << 3)
|
+ ((rgb_array & 0x001F) << 3)
|
||||||
)
|
)
|
||||||
|
|
||||||
def getScreenshot(self) -> QtGui.QPixmap:
|
def getScreenshot(self) -> QPixmap:
|
||||||
logger.debug("Capturing screenshot...")
|
logger.debug("Capturing screenshot...")
|
||||||
if not self.connected():
|
if not self.connected():
|
||||||
return QtGui.QPixmap()
|
return QPixmap()
|
||||||
try:
|
try:
|
||||||
rgba_array = self._convert_data(self._capture_data())
|
rgba_array = self._convert_data(self._capture_data())
|
||||||
image = QtGui.QImage(
|
image = QImage(
|
||||||
rgba_array,
|
rgba_array,
|
||||||
self.screenwidth,
|
self.screenwidth,
|
||||||
self.screenheight,
|
self.screenheight,
|
||||||
QtGui.QImage.Format_ARGB32,
|
QImage.Format_ARGB32,
|
||||||
)
|
)
|
||||||
logger.debug("Captured screenshot")
|
logger.debug("Captured screenshot")
|
||||||
return QtGui.QPixmap(image)
|
return QPixmap(image)
|
||||||
except serial.SerialException as exc:
|
except serial.SerialException as exc:
|
||||||
logger.exception("Exception while capturing screenshot: %s", exc)
|
logger.exception("Exception while capturing screenshot: %s", exc)
|
||||||
return QtGui.QPixmap()
|
return QPixmap()
|
||||||
|
|
||||||
def resetSweep(self, start: int, stop: int):
|
def resetSweep(self, start: int, stop: int):
|
||||||
list(self.exec_command(f"sweep {start} {stop} {self.datapoints}"))
|
list(self.exec_command(f"sweep {start} {stop} {self.datapoints}"))
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import serial
|
import serial
|
||||||
from PyQt6 import QtGui
|
from PyQt6.QtGui import QImage, QPixmap
|
||||||
|
|
||||||
from NanoVNASaver.Hardware.NanoVNA import NanoVNA
|
from NanoVNASaver.Hardware.NanoVNA import NanoVNA
|
||||||
from NanoVNASaver.Hardware.Serial import Interface
|
from NanoVNASaver.Hardware.Serial import Interface
|
||||||
|
@ -36,20 +36,20 @@ class NanoVNA_F_V2(NanoVNA):
|
||||||
super().__init__(iface)
|
super().__init__(iface)
|
||||||
self.sweep_max_freq_Hz = 3e9
|
self.sweep_max_freq_Hz = 3e9
|
||||||
|
|
||||||
def getScreenshot(self) -> QtGui.QPixmap:
|
def getScreenshot(self) -> QPixmap:
|
||||||
logger.debug("Capturing screenshot...")
|
logger.debug("Capturing screenshot...")
|
||||||
if not self.connected():
|
if not self.connected():
|
||||||
return QtGui.QPixmap()
|
return QPixmap()
|
||||||
try:
|
try:
|
||||||
rgba_array = self._capture_data()
|
rgba_array = self._capture_data()
|
||||||
image = QtGui.QImage(
|
image = QImage(
|
||||||
rgba_array,
|
rgba_array,
|
||||||
self.screenwidth,
|
self.screenwidth,
|
||||||
self.screenheight,
|
self.screenheight,
|
||||||
QtGui.QImage.Format_RGB16,
|
QImage.Format.Format_RGB16,
|
||||||
)
|
)
|
||||||
logger.debug("Captured screenshot")
|
logger.debug("Captured screenshot")
|
||||||
return QtGui.QPixmap(image)
|
return QPixmap(image)
|
||||||
except serial.SerialException as exc:
|
except serial.SerialException as exc:
|
||||||
logger.exception("Exception while capturing screenshot: %s", exc)
|
logger.exception("Exception while capturing screenshot: %s", exc)
|
||||||
return QtGui.QPixmap()
|
return QPixmap()
|
||||||
|
|
|
@ -22,7 +22,7 @@ from typing import List
|
||||||
|
|
||||||
import serial
|
import serial
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from PyQt6 import QtGui
|
from PyQt6.QtGui import QImage, QPixmap
|
||||||
|
|
||||||
from NanoVNASaver.Hardware.Serial import drain_serial, Interface
|
from NanoVNASaver.Hardware.Serial import drain_serial, Interface
|
||||||
from NanoVNASaver.Hardware.VNA import VNA
|
from NanoVNASaver.Hardware.VNA import VNA
|
||||||
|
@ -43,6 +43,7 @@ class TinySA(VNA):
|
||||||
self.start, self.stop = self._get_running_frequencies()
|
self.start, self.stop = self._get_running_frequencies()
|
||||||
self.sweep_max_freq_Hz = 950e6
|
self.sweep_max_freq_Hz = 950e6
|
||||||
self._sweepdata = []
|
self._sweepdata = []
|
||||||
|
self.validateInput = False
|
||||||
|
|
||||||
def _get_running_frequencies(self):
|
def _get_running_frequencies(self):
|
||||||
logger.debug("Reading values: frequencies")
|
logger.debug("Reading values: frequencies")
|
||||||
|
@ -81,23 +82,23 @@ class TinySA(VNA):
|
||||||
+ ((rgb_array & 0x001F) << 3)
|
+ ((rgb_array & 0x001F) << 3)
|
||||||
)
|
)
|
||||||
|
|
||||||
def getScreenshot(self) -> QtGui.QPixmap:
|
def getScreenshot(self) -> QPixmap:
|
||||||
logger.debug("Capturing screenshot...")
|
logger.debug("Capturing screenshot...")
|
||||||
if not self.connected():
|
if not self.connected():
|
||||||
return QtGui.QPixmap()
|
return QPixmap()
|
||||||
try:
|
try:
|
||||||
rgba_array = self._convert_data(self._capture_data())
|
rgba_array = self._convert_data(self._capture_data())
|
||||||
image = QtGui.QImage(
|
image = QImage(
|
||||||
rgba_array,
|
rgba_array,
|
||||||
self.screenwidth,
|
self.screenwidth,
|
||||||
self.screenheight,
|
self.screenheight,
|
||||||
QtGui.QImage.Format_ARGB32,
|
QImage.Format.Format_ARGB32,
|
||||||
)
|
)
|
||||||
logger.debug("Captured screenshot")
|
logger.debug("Captured screenshot")
|
||||||
return QtGui.QPixmap(image)
|
return QPixmap(image)
|
||||||
except serial.SerialException as exc:
|
except serial.SerialException as exc:
|
||||||
logger.exception("Exception while capturing screenshot: %s", exc)
|
logger.exception("Exception while capturing screenshot: %s", exc)
|
||||||
return QtGui.QPixmap()
|
return QPixmap()
|
||||||
|
|
||||||
def resetSweep(self, start: int, stop: int):
|
def resetSweep(self, start: int, stop: int):
|
||||||
return
|
return
|
||||||
|
@ -113,9 +114,32 @@ class TinySA(VNA):
|
||||||
return [int(line) for line in self.exec_command("frequencies")]
|
return [int(line) for line in self.exec_command("frequencies")]
|
||||||
|
|
||||||
def readValues(self, value) -> List[str]:
|
def readValues(self, value) -> List[str]:
|
||||||
|
def conv2float(data: str) -> float:
|
||||||
|
try:
|
||||||
|
return 10 ** (float(data.strip()) / 20)
|
||||||
|
except ValueError:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
logger.debug("Read: %s", value)
|
logger.debug("Read: %s", value)
|
||||||
if value == "data 0":
|
if value == "data 0":
|
||||||
self._sweepdata = [
|
self._sweepdata = [
|
||||||
f"0 {line.strip()}" for line in self.exec_command("data")
|
f"{conv2float(line)} 0.0"
|
||||||
|
for line in self.exec_command("data 0")
|
||||||
]
|
]
|
||||||
return self._sweepdata
|
return self._sweepdata
|
||||||
|
|
||||||
|
|
||||||
|
class TinySA_Ultra(TinySA):
|
||||||
|
name = "tinySA Ultra"
|
||||||
|
screenwidth = 480
|
||||||
|
screenheight = 320
|
||||||
|
valid_datapoints = (450, 51, 101, 145, 290)
|
||||||
|
|
||||||
|
def __init__(self, iface: Interface):
|
||||||
|
super().__init__(iface)
|
||||||
|
self.features = {"Screenshots", "Customizable data points"}
|
||||||
|
logger.debug("Setting initial start,stop")
|
||||||
|
self.start, self.stop = self._get_running_frequencies()
|
||||||
|
self.sweep_max_freq_Hz = 5.4e9
|
||||||
|
self._sweepdata = []
|
||||||
|
self.validateInput = False
|
||||||
|
|
|
@ -33,13 +33,16 @@ class FrequencyInputWidget(QtWidgets.QLineEdit):
|
||||||
|
|
||||||
class MarkerFrequencyInputWidget(FrequencyInputWidget):
|
class MarkerFrequencyInputWidget(FrequencyInputWidget):
|
||||||
def keyPressEvent(self, a0: QtGui.QKeyEvent) -> None:
|
def keyPressEvent(self, a0: QtGui.QKeyEvent) -> None:
|
||||||
if a0.type() == QtCore.QEvent.KeyPress:
|
if a0.type() == QtGui.QKeyEvent.Type.KeyPress:
|
||||||
if a0.key() == QtCore.Qt.Key_Up and self.nextFrequency != -1:
|
if a0.key() == QtCore.Qt.Key.Key_Up and self.nextFrequency != -1:
|
||||||
a0.accept()
|
a0.accept()
|
||||||
self.setText(str(self.nextFrequency))
|
self.setText(str(self.nextFrequency))
|
||||||
self.editingFinished.emit() # self.text())
|
self.editingFinished.emit() # self.text())
|
||||||
return
|
return
|
||||||
if a0.key() == QtCore.Qt.Key_Down and self.previousFrequency != -1:
|
if (
|
||||||
|
a0.key() == QtCore.Qt.Key.Key_Down
|
||||||
|
and self.previousFrequency != -1
|
||||||
|
):
|
||||||
a0.accept()
|
a0.accept()
|
||||||
self.setText(str(self.previousFrequency))
|
self.setText(str(self.previousFrequency))
|
||||||
self.editingFinished.emit() # self.text())
|
self.editingFinished.emit() # self.text())
|
||||||
|
|
|
@ -101,6 +101,6 @@ class Value:
|
||||||
]
|
]
|
||||||
|
|
||||||
self.freq = s11[1].freq
|
self.freq = s11[1].freq
|
||||||
self.s11 = s11[index - 1: index + 2]
|
self.s11 = s11[index - 1 : index + 2]
|
||||||
if s21:
|
if s21:
|
||||||
self.s21 = s21[index - 1: index + 2]
|
self.s21 = s21[index - 1 : index + 2]
|
||||||
|
|
|
@ -23,6 +23,7 @@ import threading
|
||||||
from time import strftime, localtime
|
from time import strftime, localtime
|
||||||
|
|
||||||
from PyQt6 import QtWidgets, QtCore, QtGui
|
from PyQt6 import QtWidgets, QtCore, QtGui
|
||||||
|
from PyQt6.QtCore import QObject
|
||||||
from PyQt6.QtWidgets import QWidget
|
from PyQt6.QtWidgets import QWidget
|
||||||
|
|
||||||
from NanoVNASaver import Defaults
|
from NanoVNASaver import Defaults
|
||||||
|
@ -74,18 +75,22 @@ from .SweepWorker import SweepWorker
|
||||||
from .Settings.Bands import BandsModel
|
from .Settings.Bands import BandsModel
|
||||||
from .Settings.Sweep import Sweep
|
from .Settings.Sweep import Sweep
|
||||||
from .Touchstone import Touchstone
|
from .Touchstone import Touchstone
|
||||||
from .About import VERSION
|
from .About import version
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Communicate(QObject):
|
||||||
|
data_available = QtCore.pyqtSignal()
|
||||||
|
|
||||||
|
|
||||||
class NanoVNASaver(QWidget):
|
class NanoVNASaver(QWidget):
|
||||||
version = VERSION
|
version = version
|
||||||
dataAvailable = QtCore.pyqtSignal()
|
|
||||||
scaleFactor = 1
|
scaleFactor = 1
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.communicate = Communicate()
|
||||||
self.s21att = 0.0
|
self.s21att = 0.0
|
||||||
if getattr(sys, "frozen", False):
|
if getattr(sys, "frozen", False):
|
||||||
logger.debug("Running from pyinstaller bundle")
|
logger.debug("Running from pyinstaller bundle")
|
||||||
|
@ -186,6 +191,7 @@ class NanoVNASaver(QWidget):
|
||||||
"smith": SmithChart("S11 Smith Chart"),
|
"smith": SmithChart("S11 Smith Chart"),
|
||||||
"s_parameter": SParameterChart("S11 Real/Imaginary"),
|
"s_parameter": SParameterChart("S11 Real/Imaginary"),
|
||||||
"vswr": VSWRChart("S11 VSWR"),
|
"vswr": VSWRChart("S11 VSWR"),
|
||||||
|
"sa_dbm": LogMagChart("Signal Analyser dBm"),
|
||||||
},
|
},
|
||||||
"s21": {
|
"s21": {
|
||||||
"group_delay": GroupDelayChart(
|
"group_delay": GroupDelayChart(
|
||||||
|
@ -589,7 +595,7 @@ class NanoVNASaver(QWidget):
|
||||||
self.s21_max_gain_label.setText("")
|
self.s21_max_gain_label.setText("")
|
||||||
|
|
||||||
self.updateTitle()
|
self.updateTitle()
|
||||||
self.dataAvailable.emit()
|
self.communicate.data_available.emit()
|
||||||
|
|
||||||
def sweepFinished(self):
|
def sweepFinished(self):
|
||||||
self._sweep_control(start=False)
|
self._sweep_control(start=False)
|
||||||
|
|
|
@ -54,7 +54,7 @@ class Datapoint(NamedTuple):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def wavelength(self) -> float:
|
def wavelength(self) -> float:
|
||||||
return 299792458 / self.freq
|
return 299792458 / self.freq if self.freq else math.inf
|
||||||
|
|
||||||
def impedance(self, ref_impedance: float = 50) -> complex:
|
def impedance(self, ref_impedance: float = 50) -> complex:
|
||||||
return gamma_to_impedance(self.z, ref_impedance)
|
return gamma_to_impedance(self.z, ref_impedance)
|
||||||
|
|
|
@ -101,7 +101,7 @@ class Sweep:
|
||||||
if (
|
if (
|
||||||
self.segments <= 0
|
self.segments <= 0
|
||||||
or self.points <= 0
|
or self.points <= 0
|
||||||
or self.start <= 0
|
or self.start < 0
|
||||||
or self.end <= 0
|
or self.end <= 0
|
||||||
or self.stepsize < 1
|
or self.stepsize < 1
|
||||||
):
|
):
|
||||||
|
|
|
@ -128,9 +128,9 @@ class AnalysisWindow(QtWidgets.QWidget):
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def toggleAutomaticRun(self, state: Qt.CheckState):
|
def toggleAutomaticRun(self, state: Qt.CheckState):
|
||||||
if state == Qt.CheckState.Checked:
|
if state == Qt.CheckState.Checked.value:
|
||||||
self.analysis_list.setDisabled(True)
|
self.analysis_list.setDisabled(True)
|
||||||
self.app.dataAvailable.connect(self.runAnalysis)
|
self.app.communicate.data_available.connect(self.runAnalysis)
|
||||||
else:
|
else:
|
||||||
self.analysis_list.setDisabled(False)
|
self.analysis_list.setDisabled(False)
|
||||||
self.app.dataAvailable.disconnect(self.runAnalysis)
|
self.app.communicate.data_available.disconnect(self.runAnalysis)
|
||||||
|
|
|
@ -19,8 +19,8 @@
|
||||||
import logging
|
import logging
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from PyQt6 import QtWidgets, QtCore, QtGui
|
from PyQt6 import QtWidgets, QtCore
|
||||||
from PyQt6.QtGui import QColorConstants
|
from PyQt6.QtGui import QColor, QColorConstants, QPalette, QShortcut
|
||||||
|
|
||||||
from NanoVNASaver import Defaults
|
from NanoVNASaver import Defaults
|
||||||
from NanoVNASaver.Charts.Chart import Chart, ChartColors
|
from NanoVNASaver.Charts.Chart import Chart, ChartColors
|
||||||
|
@ -42,7 +42,7 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
|
||||||
self.marker_window = MarkerSettingsWindow(self.app)
|
self.marker_window = MarkerSettingsWindow(self.app)
|
||||||
self.callback_params = {}
|
self.callback_params = {}
|
||||||
|
|
||||||
QtGui.QShortcut(QtCore.Qt.Key.Key_Escape, self, self.hide)
|
QShortcut(QtCore.Qt.Key.Key_Escape, self, self.hide)
|
||||||
|
|
||||||
layout = QtWidgets.QHBoxLayout()
|
layout = QtWidgets.QHBoxLayout()
|
||||||
make_scrollable(self, layout)
|
make_scrollable(self, layout)
|
||||||
|
@ -273,18 +273,60 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
|
||||||
charts_box = QtWidgets.QGroupBox("Displayed charts")
|
charts_box = QtWidgets.QGroupBox("Displayed charts")
|
||||||
charts_layout = QtWidgets.QGridLayout(charts_box)
|
charts_layout = QtWidgets.QGridLayout(charts_box)
|
||||||
|
|
||||||
# selections = ["S11 Smith chart",
|
|
||||||
# "S11 LogMag",
|
|
||||||
# "S11 VSWR",
|
|
||||||
# "S11 Phase",
|
|
||||||
# "S21 Smith chart",
|
|
||||||
# "S21 LogMag",
|
|
||||||
# "S21 Phase",
|
|
||||||
# "None"]
|
|
||||||
|
|
||||||
selections = [c.name for c in self.app.selectable_charts]
|
selections = [c.name for c in self.app.selectable_charts]
|
||||||
|
|
||||||
selections.append("None")
|
selections.append("None")
|
||||||
|
|
||||||
|
self._chart_selection(charts_layout, selections)
|
||||||
|
|
||||||
|
chart_colors = ChartColors()
|
||||||
|
Chart.color.background = self.app.settings.value(
|
||||||
|
"BackgroundColor",
|
||||||
|
defaultValue=chart_colors.background,
|
||||||
|
type=QColor,
|
||||||
|
)
|
||||||
|
Chart.color.foreground = self.app.settings.value(
|
||||||
|
"ForegroundColor",
|
||||||
|
defaultValue=chart_colors.foreground,
|
||||||
|
type=QColor,
|
||||||
|
)
|
||||||
|
Chart.color.text = self.app.settings.value(
|
||||||
|
"TextColor", defaultValue=chart_colors.text, type=QColor
|
||||||
|
)
|
||||||
|
self.bandsColor = self.app.settings.value(
|
||||||
|
"BandsColor", defaultValue=chart_colors.bands, type=QColor
|
||||||
|
)
|
||||||
|
self.app.bands.color = Chart.color.bands
|
||||||
|
Chart.color.swr = self.app.settings.value(
|
||||||
|
"VSWRColor", defaultValue=chart_colors.swr, type=QColor
|
||||||
|
)
|
||||||
|
|
||||||
|
self.dark_mode_option.setChecked(Defaults.cfg.gui.dark_mode)
|
||||||
|
self.show_lines_option.setChecked(Defaults.cfg.chart.show_lines)
|
||||||
|
self.show_marker_number_option.setChecked(
|
||||||
|
Defaults.cfg.chart.marker_label
|
||||||
|
)
|
||||||
|
self.filled_marker_option.setChecked(Defaults.cfg.chart.marker_filled)
|
||||||
|
|
||||||
|
if self.app.settings.value(
|
||||||
|
"UseCustomColors", defaultValue=False, type=bool
|
||||||
|
):
|
||||||
|
self.dark_mode_option.setDisabled(True)
|
||||||
|
self.dark_mode_option.setChecked(False)
|
||||||
|
self.use_custom_colors.setChecked(True)
|
||||||
|
|
||||||
|
left_layout.addWidget(display_options_box)
|
||||||
|
left_layout.addWidget(charts_box)
|
||||||
|
left_layout.addWidget(markers_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)
|
||||||
|
right_layout.addStretch(1)
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def _chart_selection(self, charts_layout, selections):
|
||||||
chart00_selection = QtWidgets.QComboBox()
|
chart00_selection = QtWidgets.QComboBox()
|
||||||
chart00_selection.setMinimumHeight(30)
|
chart00_selection.setMinimumHeight(30)
|
||||||
chart00_selection.addItems(selections)
|
chart00_selection.addItems(selections)
|
||||||
|
@ -370,54 +412,6 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
|
||||||
self.changeChart(1, 1, chart11_selection.currentText())
|
self.changeChart(1, 1, chart11_selection.currentText())
|
||||||
self.changeChart(1, 2, chart12_selection.currentText())
|
self.changeChart(1, 2, chart12_selection.currentText())
|
||||||
|
|
||||||
chart_colors = ChartColors()
|
|
||||||
Chart.color.background = self.app.settings.value(
|
|
||||||
"BackgroundColor",
|
|
||||||
defaultValue=chart_colors.background,
|
|
||||||
type=QtGui.QColor,
|
|
||||||
)
|
|
||||||
Chart.color.foreground = self.app.settings.value(
|
|
||||||
"ForegroundColor",
|
|
||||||
defaultValue=chart_colors.foreground,
|
|
||||||
type=QtGui.QColor,
|
|
||||||
)
|
|
||||||
Chart.color.text = self.app.settings.value(
|
|
||||||
"TextColor", defaultValue=chart_colors.text, type=QtGui.QColor
|
|
||||||
)
|
|
||||||
self.bandsColor = self.app.settings.value(
|
|
||||||
"BandsColor", defaultValue=chart_colors.bands, type=QtGui.QColor
|
|
||||||
)
|
|
||||||
self.app.bands.color = Chart.color.bands
|
|
||||||
Chart.color.swr = self.app.settings.value(
|
|
||||||
"VSWRColor", defaultValue=chart_colors.swr, type=QtGui.QColor
|
|
||||||
)
|
|
||||||
|
|
||||||
self.dark_mode_option.setChecked(Defaults.cfg.gui.dark_mode)
|
|
||||||
self.show_lines_option.setChecked(Defaults.cfg.chart.show_lines)
|
|
||||||
self.show_marker_number_option.setChecked(
|
|
||||||
Defaults.cfg.chart.marker_label
|
|
||||||
)
|
|
||||||
self.filled_marker_option.setChecked(Defaults.cfg.chart.marker_filled)
|
|
||||||
|
|
||||||
if self.app.settings.value(
|
|
||||||
"UseCustomColors", defaultValue=False, type=bool
|
|
||||||
):
|
|
||||||
self.dark_mode_option.setDisabled(True)
|
|
||||||
self.dark_mode_option.setChecked(False)
|
|
||||||
self.use_custom_colors.setChecked(True)
|
|
||||||
|
|
||||||
left_layout.addWidget(display_options_box)
|
|
||||||
left_layout.addWidget(charts_box)
|
|
||||||
left_layout.addWidget(markers_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)
|
|
||||||
right_layout.addStretch(1)
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def trace_colors(self, layout: QtWidgets.QLayout):
|
def trace_colors(self, layout: QtWidgets.QLayout):
|
||||||
for setting, name, attr in (
|
for setting, name, attr in (
|
||||||
("SweepColor", "Sweep color", "sweep"),
|
("SweepColor", "Sweep color", "sweep"),
|
||||||
|
@ -447,15 +441,13 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
|
||||||
cp.setMinimumHeight(20)
|
cp.setMinimumHeight(20)
|
||||||
default = getattr(Chart.color, attr)
|
default = getattr(Chart.color, attr)
|
||||||
color = self.app.settings.value(
|
color = self.app.settings.value(
|
||||||
setting, defaultValue=default, type=QtGui.QColor
|
setting, defaultValue=default, type=QColor
|
||||||
)
|
)
|
||||||
setattr(Chart.color, attr, color)
|
setattr(Chart.color, attr, color)
|
||||||
self.callback_params[cp] = (setting, attr)
|
self.callback_params[cp] = (setting, attr)
|
||||||
cp.clicked.connect(self.setColor)
|
cp.clicked.connect(self.setColor)
|
||||||
p = cp.palette()
|
p = cp.palette()
|
||||||
p.setColor(
|
p.setColor(QPalette.ColorRole.ButtonText, getattr(Chart.color, attr))
|
||||||
QtGui.QPalette.ColorRole.ButtonText, getattr(Chart.color, attr)
|
|
||||||
)
|
|
||||||
cp.setPalette(p)
|
cp.setPalette(p)
|
||||||
return cp
|
return cp
|
||||||
|
|
||||||
|
@ -529,13 +521,13 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
|
||||||
def changeDarkMode(self):
|
def changeDarkMode(self):
|
||||||
state = self.dark_mode_option.isChecked()
|
state = self.dark_mode_option.isChecked()
|
||||||
Defaults.cfg.gui.dark_mode = bool(state)
|
Defaults.cfg.gui.dark_mode = bool(state)
|
||||||
Chart.color.foreground = QtGui.QColor(QColorConstants.LightGray)
|
Chart.color.foreground = QColor(QColorConstants.LightGray)
|
||||||
if state:
|
if state:
|
||||||
Chart.color.background = QtGui.QColor(QColorConstants.Black)
|
Chart.color.background = QColor(QColorConstants.Black)
|
||||||
Chart.color.text = QtGui.QColor(QColorConstants.White)
|
Chart.color.text = QColor(QColorConstants.White)
|
||||||
else:
|
else:
|
||||||
Chart.color.background = QtGui.QColor(QColorConstants.White)
|
Chart.color.background = QColor(QColorConstants.White)
|
||||||
Chart.color.text = QtGui.QColor(QColorConstants.Black)
|
Chart.color.text = QColor(QColorConstants.Black)
|
||||||
Chart.color.swr = Chart.color.swr
|
Chart.color.swr = Chart.color.swr
|
||||||
self.updateCharts()
|
self.updateCharts()
|
||||||
|
|
||||||
|
@ -561,7 +553,7 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
|
||||||
return
|
return
|
||||||
|
|
||||||
palette = sender.palette()
|
palette = sender.palette()
|
||||||
palette.setColor(QtGui.QPalette.ButtonText, color)
|
palette.setColor(QPalette.ButtonText, color)
|
||||||
sender.setPalette(palette)
|
sender.setPalette(palette)
|
||||||
self.changeSetting(setting, color)
|
self.changeSetting(setting, color)
|
||||||
|
|
||||||
|
|
|
@ -62,8 +62,8 @@ class ScreenshotWindow(QtWidgets.QLabel):
|
||||||
self.setPixmap(
|
self.setPixmap(
|
||||||
self.pix.scaled(
|
self.pix.scaled(
|
||||||
self.size(),
|
self.size(),
|
||||||
QtCore.Qt.KeepAspectRatio,
|
QtCore.Qt.AspectRatioMode.KeepAspectRatio,
|
||||||
QtCore.Qt.FastTransformation,
|
QtCore.Qt.TransformationMode.FastTransformation,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
w, h = pixmap.width(), pixmap.height()
|
w, h = pixmap.width(), pixmap.height()
|
||||||
|
@ -104,8 +104,8 @@ class ScreenshotWindow(QtWidgets.QLabel):
|
||||||
self.setPixmap(
|
self.setPixmap(
|
||||||
self.pix.scaled(
|
self.pix.scaled(
|
||||||
self.size(),
|
self.size(),
|
||||||
QtCore.Qt.KeepAspectRatio,
|
QtCore.Qt.AspectRatioMode.KeepAspectRatio,
|
||||||
QtCore.Qt.FastTransformation,
|
QtCore.Qt.TransformationMode.FastTransformation,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ import sys
|
||||||
|
|
||||||
from PyQt6 import QtWidgets
|
from PyQt6 import QtWidgets
|
||||||
|
|
||||||
from NanoVNASaver.About import VERSION, INFO
|
from NanoVNASaver.About import version, INFO
|
||||||
from NanoVNASaver.NanoVNASaver import NanoVNASaver
|
from NanoVNASaver.NanoVNASaver import NanoVNASaver
|
||||||
from NanoVNASaver.Touchstone import Touchstone
|
from NanoVNASaver.Touchstone import Touchstone
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ def main():
|
||||||
help="Touchstone file to load as reference for off" " device usage",
|
help="Touchstone file to load as reference for off" " device usage",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--version", action="version", version=f"NanoVNASaver {VERSION}"
|
"--version", action="version", version=f"NanoVNASaver {version}"
|
||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|
|
@ -36,12 +36,12 @@ class TestCases(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
self.settings_1 = CFG.AppSettings(
|
self.settings_1 = CFG.AppSettings(
|
||||||
CFG.QSettings.IniFormat,
|
CFG.QSettings.Format.IniFormat,
|
||||||
CFG.QSettings.UserScope,
|
CFG.QSettings.Scope.UserScope,
|
||||||
"NanoVNASaver", "Test_1")
|
"NanoVNASaver", "Test_1")
|
||||||
self.settings_2 = CFG.AppSettings(
|
self.settings_2 = CFG.AppSettings(
|
||||||
CFG.QSettings.IniFormat,
|
CFG.QSettings.Format.IniFormat,
|
||||||
CFG.QSettings.UserScope,
|
CFG.QSettings.Scope.UserScope,
|
||||||
"NanoVNASaver", "Test_2")
|
"NanoVNASaver", "Test_2")
|
||||||
self.config_1 = TConfig()
|
self.config_1 = TConfig()
|
||||||
self.config_2 = TConfig(
|
self.config_2 = TConfig(
|
||||||
|
|
Ładowanie…
Reference in New Issue