kopia lustrzana https://github.com/NanoVNA-Saver/nanovna-saver
Trivial style changes (#625)
* Style: update type annotations * Style: simplify extraction of version from metadata * Style: replace some handwritten classes with namedtuples or dataclasses * RIZ.py: remove unused import * Style: remove some redundant lambda constructs * Marker/Values: remove __init__ parameters Mutable default values imply some complexity. In this case, the constructor is always called without arguments.pull/629/head
rodzic
d89c9f9d94
commit
6eb24f2315
|
@ -18,7 +18,6 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
from typing import Dict, List
|
|
||||||
|
|
||||||
from PyQt6 import QtWidgets
|
from PyQt6 import QtWidgets
|
||||||
|
|
||||||
|
@ -158,13 +157,13 @@ class BandPassAnalysis(Analysis):
|
||||||
self.set_result(f"Analysis complete ({len(s21)} points)")
|
self.set_result(f"Analysis complete ({len(s21)} points)")
|
||||||
|
|
||||||
def derive_60dB(
|
def derive_60dB(
|
||||||
self, cutoff_pos: Dict[str, int], cutoff_freq: Dict[str, float]
|
self, cutoff_pos: dict[str, int], cutoff_freq: dict[str, float]
|
||||||
):
|
):
|
||||||
"""derive 60dB cutoff if needed an possible
|
"""derive 60dB cutoff if needed an possible
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cutoff_pos (Dict[str, int])
|
cutoff_pos (dict[str, int])
|
||||||
cutoff_freq (Dict[str, float])
|
cutoff_freq (dict[str, float])
|
||||||
"""
|
"""
|
||||||
if (
|
if (
|
||||||
math.isnan(cutoff_freq["60.0dB_l"])
|
math.isnan(cutoff_freq["60.0dB_l"])
|
||||||
|
@ -191,7 +190,7 @@ class BandPassAnalysis(Analysis):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def find_center(self, gains: List[float]) -> int:
|
def find_center(self, gains: list[float]) -> int:
|
||||||
marker = self.app.markers[0]
|
marker = self.app.markers[0]
|
||||||
if marker.location <= 0 or marker.location >= len(gains) - 1:
|
if marker.location <= 0 or marker.location >= len(gains) - 1:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
@ -207,8 +206,8 @@ class BandPassAnalysis(Analysis):
|
||||||
return peak
|
return peak
|
||||||
|
|
||||||
def find_bounderies(
|
def find_bounderies(
|
||||||
self, gains: List[float], peak: int, peak_db: float
|
self, gains: list[float], peak: int, peak_db: float
|
||||||
) -> Dict[str, int]:
|
) -> dict[str, int]:
|
||||||
cutoff_pos = {}
|
cutoff_pos = {}
|
||||||
for attn in CUTOFF_VALS:
|
for attn in CUTOFF_VALS:
|
||||||
cutoff_pos[f"{attn:.1f}dB_l"] = at.cut_off_left(
|
cutoff_pos[f"{attn:.1f}dB_l"] = at.cut_off_left(
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
from typing import Dict, List
|
|
||||||
|
|
||||||
import NanoVNASaver.AnalyticTools as at
|
import NanoVNASaver.AnalyticTools as at
|
||||||
from NanoVNASaver.Analysis.Base import CUTOFF_VALS
|
from NanoVNASaver.Analysis.Base import CUTOFF_VALS
|
||||||
|
@ -31,12 +30,12 @@ class BandStopAnalysis(BandPassAnalysis):
|
||||||
super().__init__(app)
|
super().__init__(app)
|
||||||
self.set_titel("Band stop filter analysis")
|
self.set_titel("Band stop filter analysis")
|
||||||
|
|
||||||
def find_center(self, gains: List[float]) -> int:
|
def find_center(self, gains: list[float]) -> int:
|
||||||
return max(enumerate(gains), key=lambda i: i[1])[0]
|
return max(enumerate(gains), key=lambda i: i[1])[0]
|
||||||
|
|
||||||
def find_bounderies(
|
def find_bounderies(
|
||||||
self, gains: List[float], _: int, peak_db: float
|
self, gains: list[float], _: int, peak_db: float
|
||||||
) -> Dict[str, int]:
|
) -> dict[str, int]:
|
||||||
cutoff_pos = {}
|
cutoff_pos = {}
|
||||||
for attn in CUTOFF_VALS:
|
for attn in CUTOFF_VALS:
|
||||||
(
|
(
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
from typing import Dict
|
|
||||||
from PyQt6 import QtWidgets
|
from PyQt6 import QtWidgets
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -34,7 +33,7 @@ class QHLine(QtWidgets.QFrame):
|
||||||
class Analysis:
|
class Analysis:
|
||||||
def __init__(self, app: QtWidgets.QWidget):
|
def __init__(self, app: QtWidgets.QWidget):
|
||||||
self.app = app
|
self.app = app
|
||||||
self.label: Dict[str, QtWidgets.QLabel] = {
|
self.label: dict[str, QtWidgets.QLabel] = {
|
||||||
"titel": QtWidgets.QLabel(),
|
"titel": QtWidgets.QLabel(),
|
||||||
"result": QtWidgets.QLabel(),
|
"result": QtWidgets.QLabel(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
from typing import Dict, List
|
|
||||||
|
|
||||||
from PyQt6 import QtWidgets
|
from PyQt6 import QtWidgets
|
||||||
|
|
||||||
|
@ -109,7 +108,7 @@ class HighPassAnalysis(Analysis):
|
||||||
|
|
||||||
self.set_result(f"Analysis complete ({len(s21)}) points)")
|
self.set_result(f"Analysis complete ({len(s21)}) points)")
|
||||||
|
|
||||||
def find_level(self, gains: List[float]) -> int:
|
def find_level(self, gains: list[float]) -> int:
|
||||||
marker = self.app.markers[0]
|
marker = self.app.markers[0]
|
||||||
logger.debug("Pass band location: %d", marker.location)
|
logger.debug("Pass band location: %d", marker.location)
|
||||||
if marker.location < 0:
|
if marker.location < 0:
|
||||||
|
@ -118,8 +117,8 @@ class HighPassAnalysis(Analysis):
|
||||||
return at.center_from_idx(gains, marker.location)
|
return at.center_from_idx(gains, marker.location)
|
||||||
|
|
||||||
def find_cutoffs(
|
def find_cutoffs(
|
||||||
self, gains: List[float], peak: int, peak_db: float
|
self, gains: list[float], peak: int, peak_db: float
|
||||||
) -> Dict[str, int]:
|
) -> dict[str, int]:
|
||||||
return {
|
return {
|
||||||
f"{attn:.1f}dB": at.cut_off_left(gains, peak, peak_db, attn)
|
f"{attn:.1f}dB": at.cut_off_left(gains, peak, peak_db, attn)
|
||||||
for attn in CUTOFF_VALS
|
for attn in CUTOFF_VALS
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
from typing import Dict, List
|
|
||||||
|
|
||||||
import NanoVNASaver.AnalyticTools as at
|
import NanoVNASaver.AnalyticTools as at
|
||||||
from NanoVNASaver.Analysis.Base import CUTOFF_VALS
|
from NanoVNASaver.Analysis.Base import CUTOFF_VALS
|
||||||
|
@ -33,8 +32,8 @@ class LowPassAnalysis(HighPassAnalysis):
|
||||||
self.set_titel("Lowpass filter analysis")
|
self.set_titel("Lowpass filter analysis")
|
||||||
|
|
||||||
def find_cutoffs(
|
def find_cutoffs(
|
||||||
self, gains: List[float], peak: int, peak_db: float
|
self, gains: list[float], peak: int, peak_db: float
|
||||||
) -> Dict[str, int]:
|
) -> dict[str, int]:
|
||||||
return {
|
return {
|
||||||
f"{attn:.1f}dB": at.cut_off_right(gains, peak, peak_db, attn)
|
f"{attn:.1f}dB": at.cut_off_right(gains, peak, peak_db, attn)
|
||||||
for attn in CUTOFF_VALS
|
for attn in CUTOFF_VALS
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
import os
|
import os
|
||||||
import csv
|
import csv
|
||||||
import logging
|
import logging
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from PyQt6 import QtWidgets
|
from PyQt6 import QtWidgets
|
||||||
|
|
||||||
|
@ -44,7 +43,7 @@ def vswr_transformed(z, ratio=49) -> float:
|
||||||
class ResonanceAnalysis(Analysis):
|
class ResonanceAnalysis(Analysis):
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
super().__init__(app)
|
super().__init__(app)
|
||||||
self.crossings: List[int] = []
|
self.crossings: list[int] = []
|
||||||
self.filename = ""
|
self.filename = ""
|
||||||
self._widget = QtWidgets.QWidget()
|
self._widget = QtWidgets.QWidget()
|
||||||
self.layout = QtWidgets.QFormLayout()
|
self.layout = QtWidgets.QFormLayout()
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
from typing import Callable, List, Tuple
|
from typing import Callable
|
||||||
|
|
||||||
from PyQt6 import QtWidgets
|
from PyQt6 import QtWidgets
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
@ -102,7 +102,7 @@ class SimplePeakSearchAnalysis(Analysis):
|
||||||
if self.button["move_marker"].isChecked() and self.app.markers:
|
if self.button["move_marker"].isChecked() and self.app.markers:
|
||||||
self.app.markers[0].setFrequency(f"{s11[idx_peak].freq}")
|
self.app.markers[0].setFrequency(f"{s11[idx_peak].freq}")
|
||||||
|
|
||||||
def data_and_format(self) -> Tuple[List[float], Callable]:
|
def data_and_format(self) -> tuple[list[float], Callable]:
|
||||||
s11 = self.app.data.s11
|
s11 = self.app.data.s11
|
||||||
s21 = self.app.data.s21
|
s21 = self.app.data.s21
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from PyQt6 import QtWidgets
|
from PyQt6 import QtWidgets
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ class VSWRAnalysis(Analysis):
|
||||||
self.results_label = QtWidgets.QLabel("<b>Results</b>")
|
self.results_label = QtWidgets.QLabel("<b>Results</b>")
|
||||||
self.layout.addRow(self.results_label)
|
self.layout.addRow(self.results_label)
|
||||||
|
|
||||||
self.minimums: List[int] = []
|
self.minimums: list[int] = []
|
||||||
|
|
||||||
def runAnalysis(self):
|
def runAnalysis(self):
|
||||||
if not self.app.data.s11:
|
if not self.app.data.s11:
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import itertools as it
|
import itertools as it
|
||||||
import math
|
import math
|
||||||
from typing import Callable, List, Tuple
|
from typing import Callable
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
@ -28,14 +28,14 @@ from scipy.signal import find_peaks
|
||||||
from NanoVNASaver.RFTools import Datapoint
|
from NanoVNASaver.RFTools import Datapoint
|
||||||
|
|
||||||
|
|
||||||
def zero_crossings(data: List[float]) -> List[int]:
|
def zero_crossings(data: list[float]) -> list[int]:
|
||||||
"""find zero crossings
|
"""find zero crossings
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data (List[float]): data list execute
|
data (list[float]): data list execute
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List[int]: sorted indices of zero crossing points
|
list[int]: sorted indices of zero crossing points
|
||||||
"""
|
"""
|
||||||
if not data:
|
if not data:
|
||||||
return []
|
return []
|
||||||
|
@ -54,27 +54,27 @@ def zero_crossings(data: List[float]) -> List[int]:
|
||||||
return sorted(real_zeros + crossings)
|
return sorted(real_zeros + crossings)
|
||||||
|
|
||||||
|
|
||||||
def maxima(data: List[float], threshold: float = 0.0) -> List[int]:
|
def maxima(data: list[float], threshold: float = 0.0) -> list[int]:
|
||||||
"""maxima
|
"""maxima
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data (List[float]): data list to execute
|
data (list[float]): data list to execute
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List[int]: indices of maxima
|
list[int]: indices of maxima
|
||||||
"""
|
"""
|
||||||
peaks = find_peaks(data, width=2, distance=3, prominence=1)[0].tolist()
|
peaks = find_peaks(data, width=2, distance=3, prominence=1)[0].tolist()
|
||||||
return [i for i in peaks if data[i] > threshold] if threshold else peaks
|
return [i for i in peaks if data[i] > threshold] if threshold else peaks
|
||||||
|
|
||||||
|
|
||||||
def minima(data: List[float], threshold: float = 0.0) -> List[int]:
|
def minima(data: list[float], threshold: float = 0.0) -> list[int]:
|
||||||
"""minima
|
"""minima
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data (List[float]): data list to execute
|
data (list[float]): data list to execute
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List[int]: indices of minima
|
list[int]: indices of minima
|
||||||
"""
|
"""
|
||||||
bottoms = find_peaks(-np.array(data), width=2, distance=3, prominence=1)[
|
bottoms = find_peaks(-np.array(data), width=2, distance=3, prominence=1)[
|
||||||
0
|
0
|
||||||
|
@ -83,18 +83,18 @@ def minima(data: List[float], threshold: float = 0.0) -> List[int]:
|
||||||
|
|
||||||
|
|
||||||
def take_from_idx(
|
def take_from_idx(
|
||||||
data: List[float], idx: int, predicate: Callable
|
data: list[float], idx: int, predicate: Callable
|
||||||
) -> List[int]:
|
) -> list[int]:
|
||||||
"""take_from_center
|
"""take_from_center
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data (List[float]): data list to execute
|
data (list[float]): data list to execute
|
||||||
idx (int): index of a start position
|
idx (int): index of a start position
|
||||||
predicate (Callable): predicate on which elements to take
|
predicate (Callable): predicate on which elements to take
|
||||||
from center. (e.g. lambda i: i[1] < threshold)
|
from center. (e.g. lambda i: i[1] < threshold)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List[int]: indices of element matching predicate left
|
list[int]: indices of element matching predicate left
|
||||||
and right from index
|
and right from index
|
||||||
"""
|
"""
|
||||||
lower = list(
|
lower = list(
|
||||||
|
@ -111,11 +111,11 @@ def take_from_idx(
|
||||||
return lower + upper
|
return lower + upper
|
||||||
|
|
||||||
|
|
||||||
def center_from_idx(gains: List[float], idx: int, delta: float = 3.0) -> int:
|
def center_from_idx(gains: list[float], idx: int, delta: float = 3.0) -> int:
|
||||||
"""find maximum from index postion of gains in a attn dB gain span
|
"""find maximum from index postion of gains in a attn dB gain span
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
gains (List[float]): gain values
|
gains (list[float]): gain values
|
||||||
idx (int): start position to search from
|
idx (int): start position to search from
|
||||||
delta (float, optional): max gain delta from start. Defaults to 3.0.
|
delta (float, optional): max gain delta from start. Defaults to 3.0.
|
||||||
|
|
||||||
|
@ -128,13 +128,13 @@ def center_from_idx(gains: List[float], idx: int, delta: float = 3.0) -> int:
|
||||||
|
|
||||||
|
|
||||||
def cut_off_left(
|
def cut_off_left(
|
||||||
gains: List[float], idx: int, peak_gain: float, attn: float = 3.0
|
gains: list[float], idx: int, peak_gain: float, attn: float = 3.0
|
||||||
) -> int:
|
) -> int:
|
||||||
"""find first position in list where gain in attn lower then peak
|
"""find first position in list where gain in attn lower then peak
|
||||||
left from index
|
left from index
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
gains (List[float]): gain values
|
gains (list[float]): gain values
|
||||||
idx (int): start position to search from
|
idx (int): start position to search from
|
||||||
peak_gain (float): reference gain value
|
peak_gain (float): reference gain value
|
||||||
attn (float, optional): attenuation to search position for.
|
attn (float, optional): attenuation to search position for.
|
||||||
|
@ -149,13 +149,13 @@ def cut_off_left(
|
||||||
|
|
||||||
|
|
||||||
def cut_off_right(
|
def cut_off_right(
|
||||||
gains: List[float], idx: int, peak_gain: float, attn: float = 3.0
|
gains: list[float], idx: int, peak_gain: float, attn: float = 3.0
|
||||||
) -> int:
|
) -> int:
|
||||||
"""find first position in list where gain in attn lower then peak
|
"""find first position in list where gain in attn lower then peak
|
||||||
right from index
|
right from index
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
gains (List[float]): gain values
|
gains (list[float]): gain values
|
||||||
idx (int): start position to search from
|
idx (int): start position to search from
|
||||||
peak_gain (float): reference gain value
|
peak_gain (float): reference gain value
|
||||||
attn (float, optional): attenuation to search position for.
|
attn (float, optional): attenuation to search position for.
|
||||||
|
@ -171,15 +171,15 @@ def cut_off_right(
|
||||||
|
|
||||||
|
|
||||||
def dip_cut_offs(
|
def dip_cut_offs(
|
||||||
gains: List[float], peak_gain: float, attn: float = 3.0
|
gains: list[float], peak_gain: float, attn: float = 3.0
|
||||||
) -> Tuple[int, int]:
|
) -> tuple[int, int]:
|
||||||
rng = np.where(np.array(gains) < (peak_gain - attn))[0].tolist()
|
rng = np.where(np.array(gains) < (peak_gain - attn))[0].tolist()
|
||||||
return (rng[0], rng[-1]) if rng else (math.nan, math.nan)
|
return (rng[0], rng[-1]) if rng else (math.nan, math.nan)
|
||||||
|
|
||||||
|
|
||||||
def calculate_rolloff(
|
def calculate_rolloff(
|
||||||
s21: List[Datapoint], idx_1: int, idx_2: int
|
s21: list[Datapoint], idx_1: int, idx_2: int
|
||||||
) -> Tuple[float, float]:
|
) -> tuple[float, float]:
|
||||||
if idx_1 == idx_2:
|
if idx_1 == idx_2:
|
||||||
return (math.nan, math.nan)
|
return (math.nan, math.nan)
|
||||||
freq_1, freq_2 = s21[idx_1].freq, s21[idx_2].freq
|
freq_1, freq_2 = s21[idx_1].freq, s21[idx_2].freq
|
||||||
|
|
|
@ -23,7 +23,6 @@ import os
|
||||||
import re
|
import re
|
||||||
from collections import defaultdict, UserDict
|
from collections import defaultdict, UserDict
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from scipy.interpolate import interp1d
|
from scipy.interpolate import interp1d
|
||||||
|
|
||||||
|
@ -235,7 +234,7 @@ class CalDataSet(UserDict):
|
||||||
setattr(self.data[freq], name, (dp.z))
|
setattr(self.data[freq], name, (dp.z))
|
||||||
self.data[freq].freq = freq
|
self.data[freq].freq = freq
|
||||||
|
|
||||||
def frequencies(self) -> List[int]:
|
def frequencies(self) -> list[int]:
|
||||||
return sorted(self.data.keys())
|
return sorted(self.data.keys())
|
||||||
|
|
||||||
def get(self, key: int, default: CalData = None) -> CalData:
|
def get(self, key: int, default: CalData = None) -> CalData:
|
||||||
|
@ -276,7 +275,7 @@ class Calibration:
|
||||||
|
|
||||||
self.source = "Manual"
|
self.source = "Manual"
|
||||||
|
|
||||||
def insert(self, name: str, data: List[Datapoint]):
|
def insert(self, name: str, data: list[Datapoint]):
|
||||||
for dp in data:
|
for dp in data:
|
||||||
self.dataset.insert(name, dp)
|
self.dataset.insert(name, dp)
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import math
|
import math
|
||||||
import logging
|
import logging
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from PyQt6 import QtGui
|
from PyQt6 import QtGui
|
||||||
|
|
||||||
|
@ -33,11 +32,11 @@ class CombinedLogMagChart(LogMagChart):
|
||||||
def __init__(self, name=""):
|
def __init__(self, name=""):
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
|
|
||||||
self.data11: List[Datapoint] = []
|
self.data11: list[Datapoint] = []
|
||||||
self.data21: List[Datapoint] = []
|
self.data21: list[Datapoint] = []
|
||||||
|
|
||||||
self.reference11: List[Datapoint] = []
|
self.reference11: list[Datapoint] = []
|
||||||
self.reference21: List[Datapoint] = []
|
self.reference21: list[Datapoint] = []
|
||||||
|
|
||||||
def setCombinedData(self, data11, data21):
|
def setCombinedData(self, data11, data21):
|
||||||
self.data11 = data11
|
self.data11 = data11
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from dataclasses import dataclass, field, replace
|
from dataclasses import dataclass, field, replace
|
||||||
from typing import List, Set, Tuple, ClassVar, Any, Optional
|
from typing import ClassVar, Any
|
||||||
|
|
||||||
from PyQt6 import QtWidgets, QtGui, QtCore
|
from PyQt6 import QtWidgets, QtGui, QtCore
|
||||||
from PyQt6.QtCore import pyqtSignal, Qt
|
from PyQt6.QtCore import pyqtSignal, Qt
|
||||||
|
@ -67,8 +67,8 @@ class ChartDimensions:
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ChartDragBox:
|
class ChartDragBox:
|
||||||
pos: Tuple[int] = (-1, -1)
|
pos: tuple[int] = (-1, -1)
|
||||||
pos_start: Tuple[int] = (0, 0)
|
pos_start: tuple[int] = (0, 0)
|
||||||
state: bool = False
|
state: bool = False
|
||||||
move_x: int = -1
|
move_x: int = -1
|
||||||
move_y: int = -1
|
move_y: int = -1
|
||||||
|
@ -128,11 +128,11 @@ class Chart(QtWidgets.QWidget):
|
||||||
|
|
||||||
self.draggedMarker = None
|
self.draggedMarker = None
|
||||||
|
|
||||||
self.data: List[Datapoint] = []
|
self.data: list[Datapoint] = []
|
||||||
self.reference: List[Datapoint] = []
|
self.reference: list[Datapoint] = []
|
||||||
|
|
||||||
self.markers: List[Marker] = []
|
self.markers: list[Marker] = []
|
||||||
self.swrMarkers: Set[float] = set()
|
self.swrMarkers: set[float] = set()
|
||||||
|
|
||||||
self.action_popout = QAction("Popout chart")
|
self.action_popout = QAction("Popout chart")
|
||||||
self.action_popout.triggered.connect(
|
self.action_popout.triggered.connect(
|
||||||
|
@ -192,7 +192,7 @@ class Chart(QtWidgets.QWidget):
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
|
||||||
def getNearestMarker(self, x, y) -> Optional[Marker]:
|
def getNearestMarker(self, x, y) -> Marker | None:
|
||||||
if not self.data:
|
if not self.data:
|
||||||
return None
|
return None
|
||||||
shortest = 10**6
|
shortest = 10**6
|
||||||
|
@ -205,7 +205,7 @@ class Chart(QtWidgets.QWidget):
|
||||||
nearest = m
|
nearest = m
|
||||||
return nearest
|
return nearest
|
||||||
|
|
||||||
def getPosition(self, d: Datapoint) -> Tuple[int, int]:
|
def getPosition(self, d: Datapoint) -> tuple[int, int]:
|
||||||
return self.getXPosition(d), self.getYPosition(d)
|
return self.getXPosition(d), self.getYPosition(d)
|
||||||
|
|
||||||
def setDrawLines(self, draw_lines):
|
def setDrawLines(self, draw_lines):
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import math
|
import math
|
||||||
import logging
|
import logging
|
||||||
from typing import List, Tuple
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from PyQt6 import QtWidgets, QtGui, QtCore
|
from PyQt6 import QtWidgets, QtGui, QtCore
|
||||||
|
@ -405,7 +404,7 @@ class FrequencyChart(Chart):
|
||||||
step = span / self.dim.width
|
step = span / self.dim.width
|
||||||
return round(self.fstart + absx * step)
|
return round(self.fstart + absx * step)
|
||||||
|
|
||||||
def valueAtPosition(self, y) -> List[float]:
|
def valueAtPosition(self, y) -> list[float]:
|
||||||
"""
|
"""
|
||||||
Returns the chart-specific value(s) at the specified Y-position
|
Returns the chart-specific value(s) at the specified Y-position
|
||||||
:param y: The Y position to calculate for.
|
:param y: The Y position to calculate for.
|
||||||
|
@ -493,7 +492,7 @@ class FrequencyChart(Chart):
|
||||||
self.drawDragbog(qp)
|
self.drawDragbog(qp)
|
||||||
qp.end()
|
qp.end()
|
||||||
|
|
||||||
def _data_oob(self, data: List[Datapoint]) -> bool:
|
def _data_oob(self, data: list[Datapoint]) -> bool:
|
||||||
return data[0].freq > self.fstop or self.data[-1].freq < self.fstart
|
return data[0].freq > self.fstop or self.data[-1].freq < self.fstart
|
||||||
|
|
||||||
def _check_frequency_boundaries(self, qp: QtGui.QPainter):
|
def _check_frequency_boundaries(self, qp: QtGui.QPainter):
|
||||||
|
@ -605,7 +604,7 @@ class FrequencyChart(Chart):
|
||||||
self.drawData(qp, self.reference, Chart.color.reference)
|
self.drawData(qp, self.reference, Chart.color.reference)
|
||||||
self.drawMarkers(qp)
|
self.drawMarkers(qp)
|
||||||
|
|
||||||
def _find_scaling(self) -> Tuple[float, float]:
|
def _find_scaling(self) -> tuple[float, float]:
|
||||||
min_value = self.minDisplayValue / 10e11
|
min_value = self.minDisplayValue / 10e11
|
||||||
max_value = self.maxDisplayValue / 10e11
|
max_value = self.maxDisplayValue / 10e11
|
||||||
if self.fixedValues:
|
if self.fixedValues:
|
||||||
|
@ -686,7 +685,7 @@ class FrequencyChart(Chart):
|
||||||
def drawData(
|
def drawData(
|
||||||
self,
|
self,
|
||||||
qp: QtGui.QPainter,
|
qp: QtGui.QPainter,
|
||||||
data: List[Datapoint],
|
data: list[Datapoint],
|
||||||
color: QtGui.QColor,
|
color: QtGui.QColor,
|
||||||
y_function=None,
|
y_function=None,
|
||||||
):
|
):
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import math
|
import math
|
||||||
import logging
|
import logging
|
||||||
from typing import List
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
@ -74,7 +73,7 @@ class GroupDelayChart(FrequencyChart):
|
||||||
self.groupDelayReference = self.calc_data(self.reference)
|
self.groupDelayReference = self.calc_data(self.reference)
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def calc_data(self, data: List[Datapoint]):
|
def calc_data(self, data: list[Datapoint]):
|
||||||
data_len = len(data)
|
data_len = len(data)
|
||||||
if data_len <= 1:
|
if data_len <= 1:
|
||||||
return []
|
return []
|
||||||
|
@ -172,8 +171,8 @@ class GroupDelayChart(FrequencyChart):
|
||||||
self,
|
self,
|
||||||
qp: QtGui.QPainter,
|
qp: QtGui.QPainter,
|
||||||
color: QtGui.QColor,
|
color: QtGui.QColor,
|
||||||
data: List[Datapoint],
|
data: list[Datapoint],
|
||||||
delay: List[Datapoint],
|
delay: list[Datapoint],
|
||||||
):
|
):
|
||||||
pen = QtGui.QPen(color)
|
pen = QtGui.QPen(color)
|
||||||
pen.setWidth(self.dim.point)
|
pen.setWidth(self.dim.point)
|
||||||
|
@ -216,7 +215,7 @@ class GroupDelayChart(FrequencyChart):
|
||||||
(self.maxDelay - delay) / self.span * self.dim.height
|
(self.maxDelay - delay) / self.span * self.dim.height
|
||||||
)
|
)
|
||||||
|
|
||||||
def valueAtPosition(self, y) -> List[float]:
|
def valueAtPosition(self, y) -> list[float]:
|
||||||
absy = y - self.topMargin
|
absy = y - self.topMargin
|
||||||
val = -1 * ((absy / self.dim.height * self.span) - self.maxDelay)
|
val = -1 * ((absy / self.dim.height * self.span) - self.maxDelay)
|
||||||
return [val]
|
return [val]
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import math
|
import math
|
||||||
import logging
|
import logging
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from PyQt6 import QtGui
|
from PyQt6 import QtGui
|
||||||
|
|
||||||
|
@ -165,7 +164,7 @@ class LogMagChart(FrequencyChart):
|
||||||
(self.maxValue - logMag) / self.span * self.dim.height
|
(self.maxValue - logMag) / self.span * self.dim.height
|
||||||
)
|
)
|
||||||
|
|
||||||
def valueAtPosition(self, y) -> List[float]:
|
def valueAtPosition(self, y) -> list[float]:
|
||||||
absy = y - self.topMargin
|
absy = y - self.topMargin
|
||||||
val = -1 * ((absy / self.dim.height * self.span) - self.maxValue)
|
val = -1 * ((absy / self.dim.height * self.span) - self.maxValue)
|
||||||
return [val]
|
return [val]
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import math
|
import math
|
||||||
import logging
|
import logging
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from PyQt6 import QtGui
|
from PyQt6 import QtGui
|
||||||
|
|
||||||
|
@ -127,7 +126,7 @@ class MagnitudeChart(FrequencyChart):
|
||||||
(self.maxValue - mag) / self.span * self.dim.height
|
(self.maxValue - mag) / self.span * self.dim.height
|
||||||
)
|
)
|
||||||
|
|
||||||
def valueAtPosition(self, y) -> List[float]:
|
def valueAtPosition(self, y) -> list[float]:
|
||||||
absy = y - self.topMargin
|
absy = y - self.topMargin
|
||||||
val = -1 * ((absy / self.dim.height * self.span) - self.maxValue)
|
val = -1 * ((absy / self.dim.height * self.span) - self.maxValue)
|
||||||
return [val]
|
return [val]
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import math
|
import math
|
||||||
import logging
|
import logging
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from PyQt6 import QtGui
|
from PyQt6 import QtGui
|
||||||
|
|
||||||
|
@ -129,7 +128,7 @@ class MagnitudeZChart(FrequencyChart):
|
||||||
)
|
)
|
||||||
return self.topMargin
|
return self.topMargin
|
||||||
|
|
||||||
def valueAtPosition(self, y) -> List[float]:
|
def valueAtPosition(self, y) -> list[float]:
|
||||||
absy = y - self.topMargin
|
absy = y - self.topMargin
|
||||||
if self.logarithmicY:
|
if self.logarithmicY:
|
||||||
span = math.log(self.maxValue) - math.log(self.minValue)
|
span = math.log(self.maxValue) - math.log(self.minValue)
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import math
|
import math
|
||||||
import logging
|
import logging
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from PyQt6 import QtGui
|
from PyQt6 import QtGui
|
||||||
|
|
||||||
|
@ -325,7 +324,7 @@ class PermeabilityChart(FrequencyChart):
|
||||||
self.topMargin + (self.max - re) / self.span * self.dim.height
|
self.topMargin + (self.max - re) / self.span * self.dim.height
|
||||||
)
|
)
|
||||||
|
|
||||||
def valueAtPosition(self, y) -> List[float]:
|
def valueAtPosition(self, y) -> list[float]:
|
||||||
absy = y - self.topMargin
|
absy = y - self.topMargin
|
||||||
if self.logarithmicY:
|
if self.logarithmicY:
|
||||||
min_val = self.max - self.span
|
min_val = self.max - self.span
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
import math
|
import math
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from typing import List
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from PyQt6.QtGui import QAction, QPainter, QPen
|
from PyQt6.QtGui import QAction, QPainter, QPen
|
||||||
|
@ -151,7 +150,7 @@ class PhaseChart(FrequencyChart):
|
||||||
(self.maxAngle - angle) / self.span * self.dim.height
|
(self.maxAngle - angle) / self.span * self.dim.height
|
||||||
)
|
)
|
||||||
|
|
||||||
def valueAtPosition(self, y) -> List[float]:
|
def valueAtPosition(self, y) -> list[float]:
|
||||||
absy = y - self.topMargin
|
absy = y - self.topMargin
|
||||||
val = -1 * ((absy / self.dim.height * self.span) - self.maxAngle)
|
val = -1 * ((absy / self.dim.height * self.span) - self.maxAngle)
|
||||||
return [val]
|
return [val]
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import math
|
import math
|
||||||
import logging
|
import logging
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from PyQt6 import QtGui
|
from PyQt6 import QtGui
|
||||||
|
|
||||||
|
@ -127,7 +126,7 @@ class QualityFactorChart(FrequencyChart):
|
||||||
(self.maxQ - Q) / self.span * self.dim.height
|
(self.maxQ - Q) / self.span * self.dim.height
|
||||||
)
|
)
|
||||||
|
|
||||||
def valueAtPosition(self, y) -> List[float]:
|
def valueAtPosition(self, y) -> list[float]:
|
||||||
absy = y - self.topMargin
|
absy = y - self.topMargin
|
||||||
val = -1 * ((absy / self.dim.height * self.span) - self.maxQ)
|
val = -1 * ((absy / self.dim.height * self.span) - self.maxQ)
|
||||||
return [val]
|
return [val]
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import math
|
import math
|
||||||
import logging
|
import logging
|
||||||
from typing import List, Optional
|
|
||||||
|
|
||||||
from PyQt6 import QtWidgets, QtGui
|
from PyQt6 import QtWidgets, QtGui
|
||||||
|
|
||||||
|
@ -381,7 +380,7 @@ class RealImaginaryChart(FrequencyChart):
|
||||||
else self.topMargin
|
else self.topMargin
|
||||||
)
|
)
|
||||||
|
|
||||||
def valueAtPosition(self, y) -> List[float]:
|
def valueAtPosition(self, y) -> list[float]:
|
||||||
absy = y - self.topMargin
|
absy = y - self.topMargin
|
||||||
valRe = -1 * ((absy / self.dim.height * self.span_real) - self.max_real)
|
valRe = -1 * ((absy / self.dim.height * self.span_real) - self.max_real)
|
||||||
valIm = -1 * ((absy / self.dim.height * self.span_imag) - self.max_imag)
|
valIm = -1 * ((absy / self.dim.height * self.span_imag) - self.max_imag)
|
||||||
|
@ -408,7 +407,7 @@ class RealImaginaryChart(FrequencyChart):
|
||||||
|
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def getNearestMarker(self, x, y) -> Optional[Marker]:
|
def getNearestMarker(self, x, y) -> Marker | None:
|
||||||
if not self.data:
|
if not self.data:
|
||||||
return None
|
return None
|
||||||
shortest = 10e6
|
shortest = 10e6
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from PyQt6 import QtWidgets, QtGui
|
from PyQt6 import QtGui
|
||||||
|
|
||||||
from NanoVNASaver.Formatting import format_frequency_chart
|
from NanoVNASaver.Formatting import format_frequency_chart
|
||||||
from NanoVNASaver.RFTools import Datapoint
|
from NanoVNASaver.RFTools import Datapoint
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from PyQt6 import QtGui
|
from PyQt6 import QtGui
|
||||||
|
|
||||||
|
@ -142,7 +141,7 @@ class SParameterChart(FrequencyChart):
|
||||||
+ (self.maxValue - d.im) / self.span * self.dim.height
|
+ (self.maxValue - d.im) / self.span * self.dim.height
|
||||||
)
|
)
|
||||||
|
|
||||||
def valueAtPosition(self, y) -> List[float]:
|
def valueAtPosition(self, y) -> list[float]:
|
||||||
absy = y - self.topMargin
|
absy = y - self.topMargin
|
||||||
val = -1 * ((absy / self.dim.height * self.span) - self.maxValue)
|
val = -1 * ((absy / self.dim.height * self.span) - self.maxValue)
|
||||||
return [val]
|
return [val]
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from PyQt6 import QtGui, QtCore, QtWidgets
|
from PyQt6 import QtGui, QtCore, QtWidgets
|
||||||
|
|
||||||
|
@ -58,7 +57,7 @@ class SquareChart(Chart):
|
||||||
self,
|
self,
|
||||||
qp: QtGui.QPainter,
|
qp: QtGui.QPainter,
|
||||||
color: QtGui.QColor,
|
color: QtGui.QColor,
|
||||||
data: List[Datapoint],
|
data: list[Datapoint],
|
||||||
fstart: int = 0,
|
fstart: int = 0,
|
||||||
fstop: int = 0,
|
fstop: int = 0,
|
||||||
):
|
):
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import math
|
import math
|
||||||
import logging
|
import logging
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from PyQt6 import QtGui
|
from PyQt6 import QtGui
|
||||||
|
|
||||||
|
@ -166,7 +165,7 @@ class VSWRChart(FrequencyChart):
|
||||||
def getYPosition(self, d: Datapoint) -> int:
|
def getYPosition(self, d: Datapoint) -> int:
|
||||||
return self.getYPositionFromValue(d.vswr)
|
return self.getYPositionFromValue(d.vswr)
|
||||||
|
|
||||||
def valueAtPosition(self, y) -> List[float]:
|
def valueAtPosition(self, y) -> list[float]:
|
||||||
absy = y - self.topMargin
|
absy = y - self.topMargin
|
||||||
if self.logarithmicY:
|
if self.logarithmicY:
|
||||||
min_val = self.maxVSWR - self.span
|
min_val = self.maxVSWR - self.span
|
||||||
|
|
|
@ -62,7 +62,7 @@ class Chart:
|
||||||
marker_size: int = 8
|
marker_size: int = 8
|
||||||
returnloss_is_positive: bool = False
|
returnloss_is_positive: bool = False
|
||||||
show_bands: bool = False
|
show_bands: bool = False
|
||||||
vswr_lines: list = DC.field(default_factory=lambda: [])
|
vswr_lines: list = DC.field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
@DC.dataclass
|
@DC.dataclass
|
||||||
|
@ -126,11 +126,11 @@ class Markers:
|
||||||
|
|
||||||
@DC.dataclass
|
@DC.dataclass
|
||||||
class CFG:
|
class CFG:
|
||||||
gui: object = DC.field(default_factory=lambda: GUI())
|
gui: object = DC.field(default_factory=GUI)
|
||||||
charts_selected: object = DC.field(default_factory=lambda: ChartsSelected())
|
charts_selected: object = DC.field(default_factory=ChartsSelected)
|
||||||
chart: object = DC.field(default_factory=lambda: Chart())
|
chart: object = DC.field(default_factory=Chart)
|
||||||
chart_colors: object = DC.field(default_factory=lambda: ChartColors())
|
chart_colors: object = DC.field(default_factory=ChartColors)
|
||||||
markers: object = DC.field(default_factory=lambda: Markers())
|
markers: object = DC.field(default_factory=Markers)
|
||||||
|
|
||||||
|
|
||||||
cfg = CFG()
|
cfg = CFG()
|
||||||
|
@ -159,9 +159,9 @@ def store(settings: "AppSettings", data: CFG = None) -> None:
|
||||||
|
|
||||||
def from_type(data) -> str:
|
def from_type(data) -> str:
|
||||||
type_map = {
|
type_map = {
|
||||||
bytearray: lambda x: x.hex(),
|
bytearray: bytearray.hex,
|
||||||
QColor: lambda x: x.getRgb(),
|
QColor: QColor.getRgb,
|
||||||
QByteArray: lambda x: x.toHex(),
|
QByteArray: QByteArray.toHex,
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
f"{type_map[type(data)](data)}" if type(data) in type_map else f"{data}"
|
f"{type_map[type(data)](data)}" if type(data) in type_map else f"{data}"
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import math
|
import math
|
||||||
from numbers import Number
|
from numbers import Number
|
||||||
from typing import Union
|
|
||||||
|
|
||||||
from NanoVNASaver import SITools
|
from NanoVNASaver import SITools
|
||||||
|
|
||||||
|
@ -55,7 +54,7 @@ def format_frequency(freq: Number) -> str:
|
||||||
return str(SITools.Value(freq, "Hz", FMT_FREQ))
|
return str(SITools.Value(freq, "Hz", FMT_FREQ))
|
||||||
|
|
||||||
|
|
||||||
def format_frequency_inputs(freq: Union[Number, str]) -> str:
|
def format_frequency_inputs(freq: Number | str) -> str:
|
||||||
return str(SITools.Value(freq, "Hz", FMT_FREQ_INPUTS))
|
return str(SITools.Value(freq, "Hz", FMT_FREQ_INPUTS))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ import logging
|
||||||
import platform
|
import platform
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from typing import List
|
|
||||||
|
|
||||||
import serial
|
import serial
|
||||||
from serial.tools import list_ports
|
from serial.tools import list_ports
|
||||||
|
@ -91,7 +90,7 @@ def usb_typename(device: ListPortInfo) -> str:
|
||||||
# Get list of interfaces with VNAs connected
|
# Get list of interfaces with VNAs connected
|
||||||
|
|
||||||
|
|
||||||
def get_interfaces() -> List[Interface]:
|
def get_interfaces() -> list[Interface]:
|
||||||
interfaces = []
|
interfaces = []
|
||||||
# serial like usb interfaces
|
# serial like usb interfaces
|
||||||
for d in list_ports.comports():
|
for d in list_ports.comports():
|
||||||
|
@ -117,7 +116,7 @@ def get_interfaces() -> List[Interface]:
|
||||||
return interfaces
|
return interfaces
|
||||||
|
|
||||||
|
|
||||||
def get_portinfos() -> List[str]:
|
def get_portinfos() -> list[str]:
|
||||||
portinfos = []
|
portinfos = []
|
||||||
# serial like usb interfaces
|
# serial like usb interfaces
|
||||||
for d in list_ports.comports():
|
for d in list_ports.comports():
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
import struct
|
import struct
|
||||||
from typing import List
|
|
||||||
|
|
||||||
import serial
|
import serial
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
@ -123,7 +122,7 @@ class NanoVNA(VNA):
|
||||||
self.features.add("Scan command")
|
self.features.add("Scan command")
|
||||||
self.sweep_method = "scan"
|
self.sweep_method = "scan"
|
||||||
|
|
||||||
def readFrequencies(self) -> List[int]:
|
def readFrequencies(self) -> list[int]:
|
||||||
logger.debug("readFrequencies: %s", self.sweep_method)
|
logger.debug("readFrequencies: %s", self.sweep_method)
|
||||||
if self.sweep_method != "scan_mask":
|
if self.sweep_method != "scan_mask":
|
||||||
return super().readFrequencies()
|
return super().readFrequencies()
|
||||||
|
@ -134,7 +133,7 @@ class NanoVNA(VNA):
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
def readValues(self, value) -> List[str]:
|
def readValues(self, value) -> list[str]:
|
||||||
if self.sweep_method != "scan_mask":
|
if self.sweep_method != "scan_mask":
|
||||||
return super().readValues(value)
|
return super().readValues(value)
|
||||||
logger.debug("readValue with scan mask (%s)", value)
|
logger.debug("readValue with scan mask (%s)", value)
|
||||||
|
|
|
@ -20,7 +20,6 @@ import logging
|
||||||
import platform
|
import platform
|
||||||
from struct import pack, unpack_from
|
from struct import pack, unpack_from
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from NanoVNASaver.Hardware.Serial import Interface
|
from NanoVNASaver.Hardware.Serial import Interface
|
||||||
from NanoVNASaver.Hardware.VNA import VNA
|
from NanoVNASaver.Hardware.VNA import VNA
|
||||||
|
@ -131,7 +130,7 @@ class NanoVNA_V2(VNA):
|
||||||
logger.debug("readFirmware: %s", result)
|
logger.debug("readFirmware: %s", result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def readFrequencies(self) -> List[int]:
|
def readFrequencies(self) -> list[int]:
|
||||||
return [
|
return [
|
||||||
int(self.sweepStartHz + i * self.sweepStepHz)
|
int(self.sweepStartHz + i * self.sweepStepHz)
|
||||||
for i in range(self.datapoints)
|
for i in range(self.datapoints)
|
||||||
|
@ -159,7 +158,7 @@ class NanoVNA_V2(VNA):
|
||||||
|
|
||||||
logger.debug("Freq index to: %i", freq_index)
|
logger.debug("Freq index to: %i", freq_index)
|
||||||
|
|
||||||
def readValues(self, value) -> List[str]:
|
def readValues(self, value) -> list[str]:
|
||||||
# Actually grab the data only when requesting channel 0.
|
# Actually grab the data only when requesting channel 0.
|
||||||
# The hardware will return all channels which we will store.
|
# The hardware will return all channels which we will store.
|
||||||
if value == "data 0":
|
if value == "data 0":
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
import struct
|
import struct
|
||||||
from typing import List
|
|
||||||
|
|
||||||
import serial
|
import serial
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
@ -109,11 +108,11 @@ class TinySA(VNA):
|
||||||
list(self.exec_command(f"sweep {start} {stop} {self.datapoints}"))
|
list(self.exec_command(f"sweep {start} {stop} {self.datapoints}"))
|
||||||
list(self.exec_command("trigger auto"))
|
list(self.exec_command("trigger auto"))
|
||||||
|
|
||||||
def readFrequencies(self) -> List[int]:
|
def readFrequencies(self) -> list[int]:
|
||||||
logger.debug("readFrequencies")
|
logger.debug("readFrequencies")
|
||||||
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:
|
def conv2float(data: str) -> float:
|
||||||
try:
|
try:
|
||||||
return 10 ** (float(data.strip()) / 20)
|
return 10 ** (float(data.strip()) / 20)
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from typing import List, Iterator, Set
|
from typing import Iterator
|
||||||
|
|
||||||
from PyQt6 import QtGui
|
from PyQt6 import QtGui
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ class VNA:
|
||||||
if len(self.valid_datapoints) > 1:
|
if len(self.valid_datapoints) > 1:
|
||||||
self.features.add("Customizable data points")
|
self.features.add("Customizable data points")
|
||||||
|
|
||||||
def get_bandwidths(self) -> List[int]:
|
def get_bandwidths(self) -> list[int]:
|
||||||
logger.debug("get bandwidths")
|
logger.debug("get bandwidths")
|
||||||
if self.bw_method == "dislord":
|
if self.bw_method == "dislord":
|
||||||
return list(DISLORD_BW.keys())
|
return list(DISLORD_BW.keys())
|
||||||
|
@ -153,7 +153,7 @@ class VNA:
|
||||||
raise IOError(f"set_bandwith({bandwidth}: {result}")
|
raise IOError(f"set_bandwith({bandwidth}: {result}")
|
||||||
self.bandwidth = bandwidth
|
self.bandwidth = bandwidth
|
||||||
|
|
||||||
def readFrequencies(self) -> List[int]:
|
def readFrequencies(self) -> list[int]:
|
||||||
return [int(f) for f in self.readValues("frequencies")]
|
return [int(f) for f in self.readValues("frequencies")]
|
||||||
|
|
||||||
def resetSweep(self, start: int, stop: int):
|
def resetSweep(self, start: int, stop: int):
|
||||||
|
@ -170,7 +170,7 @@ class VNA:
|
||||||
def connected(self) -> bool:
|
def connected(self) -> bool:
|
||||||
return self.serial.is_open
|
return self.serial.is_open
|
||||||
|
|
||||||
def getFeatures(self) -> Set[str]:
|
def getFeatures(self) -> set[str]:
|
||||||
return self.features
|
return self.features
|
||||||
|
|
||||||
def getCalibration(self) -> str:
|
def getCalibration(self) -> str:
|
||||||
|
@ -194,7 +194,7 @@ class VNA:
|
||||||
logger.debug("result:\n%s", result)
|
logger.debug("result:\n%s", result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def readValues(self, value) -> List[str]:
|
def readValues(self, value) -> list[str]:
|
||||||
logger.debug("VNA reading %s", value)
|
logger.debug("VNA reading %s", value)
|
||||||
result = list(self.exec_command(value))
|
result = list(self.exec_command(value))
|
||||||
logger.debug("VNA done reading %s (%d values)", value, len(result))
|
logger.debug("VNA done reading %s (%d values)", value, len(result))
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from typing import List, NamedTuple
|
from typing import NamedTuple
|
||||||
from NanoVNASaver.RFTools import Datapoint
|
from NanoVNASaver.RFTools import Datapoint
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,16 +71,13 @@ class Value:
|
||||||
"""Contains the data area to calculate marker values from"""
|
"""Contains the data area to calculate marker values from"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
freq: int = 0,
|
|
||||||
s11: List[Datapoint] = None,
|
|
||||||
s21: List[Datapoint] = None,
|
|
||||||
):
|
):
|
||||||
self.freq = freq
|
self.freq: int = 0
|
||||||
self.s11 = [] if s11 is None else s11[:]
|
self.s11: list[Datapoint] = []
|
||||||
self.s21 = [] if s21 is None else s21[:]
|
self.s21: list[Datapoint] = []
|
||||||
|
|
||||||
def store(self, index: int, s11: List[Datapoint], s21: List[Datapoint]):
|
def store(self, index: int, s11: list[Datapoint], s21: list[Datapoint]):
|
||||||
# handle boundaries
|
# handle boundaries
|
||||||
if index == 0:
|
if index == 0:
|
||||||
index = 1
|
index = 1
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import math
|
import math
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from PyQt6 import QtGui, QtWidgets, QtCore
|
from PyQt6 import QtGui, QtWidgets, QtCore
|
||||||
from PyQt6.QtCore import pyqtSignal
|
from PyQt6.QtCore import pyqtSignal
|
||||||
|
@ -253,7 +252,7 @@ class Marker(QtCore.QObject, Value):
|
||||||
def getRow(self):
|
def getRow(self):
|
||||||
return QtWidgets.QLabel(self.name), self.layout
|
return QtWidgets.QLabel(self.name), self.layout
|
||||||
|
|
||||||
def findLocation(self, data: List[RFTools.Datapoint]):
|
def findLocation(self, data: list[RFTools.Datapoint]):
|
||||||
self.location = -1
|
self.location = -1
|
||||||
self.frequencyInput.nextFrequency = -1
|
self.frequencyInput.nextFrequency = -1
|
||||||
self.frequencyInput.previousFrequency = -1
|
self.frequencyInput.previousFrequency = -1
|
||||||
|
@ -298,7 +297,7 @@ class Marker(QtCore.QObject, Value):
|
||||||
v.setText("")
|
v.setText("")
|
||||||
|
|
||||||
def updateLabels(
|
def updateLabels(
|
||||||
self, s11: List[RFTools.Datapoint], s21: List[RFTools.Datapoint]
|
self, s11: list[RFTools.Datapoint], s21: list[RFTools.Datapoint]
|
||||||
):
|
):
|
||||||
if not s11:
|
if not s11:
|
||||||
return
|
return
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import math
|
import math
|
||||||
import cmath
|
import cmath
|
||||||
from typing import List, NamedTuple
|
from typing import NamedTuple
|
||||||
|
|
||||||
from NanoVNASaver.SITools import Format, clamp_value
|
from NanoVNASaver.SITools import Format, clamp_value
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ def gamma_to_impedance(gamma: complex, ref_impedance: float = 50) -> complex:
|
||||||
return math.inf
|
return math.inf
|
||||||
|
|
||||||
|
|
||||||
def groupDelay(data: List[Datapoint], index: int) -> float:
|
def groupDelay(data: list[Datapoint], index: int) -> float:
|
||||||
idx0 = clamp_value(index - 1, 0, len(data) - 1)
|
idx0 = clamp_value(index - 1, 0, len(data) - 1)
|
||||||
idx1 = clamp_value(index + 1, 0, len(data) - 1)
|
idx1 = clamp_value(index + 1, 0, len(data) - 1)
|
||||||
delta_angle = data[idx1].phase - data[idx0].phase
|
delta_angle = data[idx1].phase - data[idx0].phase
|
||||||
|
@ -147,7 +147,7 @@ def serial_to_parallel(z: complex) -> complex:
|
||||||
return complex(z_sq_sum / z.real, z_sq_sum / z.imag)
|
return complex(z_sq_sum / z.real, z_sq_sum / z.imag)
|
||||||
|
|
||||||
|
|
||||||
def corr_att_data(data: List[Datapoint], att: float) -> List[Datapoint]:
|
def corr_att_data(data: list[Datapoint], att: float) -> list[Datapoint]:
|
||||||
"""Correct the ratio for a given attenuation on s21 input"""
|
"""Correct the ratio for a given attenuation on s21 input"""
|
||||||
if att <= 0:
|
if att <= 0:
|
||||||
return data
|
return data
|
||||||
|
|
|
@ -17,10 +17,11 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
|
from dataclasses import dataclass, replace
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from math import log
|
from math import log
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from typing import Iterator, Tuple
|
from typing import Iterator, NamedTuple
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -31,63 +32,28 @@ class SweepMode(Enum):
|
||||||
AVERAGE = 2
|
AVERAGE = 2
|
||||||
|
|
||||||
|
|
||||||
class Properties:
|
class Properties(NamedTuple):
|
||||||
def __init__(
|
name: str = ""
|
||||||
self,
|
mode: "SweepMode" = SweepMode.SINGLE
|
||||||
name: str = "",
|
averages: tuple[int, int] = (3, 0)
|
||||||
mode: "SweepMode" = SweepMode.SINGLE,
|
logarithmic: bool = False
|
||||||
averages: Tuple[int, int] = (3, 0),
|
|
||||||
logarithmic: bool = False,
|
|
||||||
):
|
|
||||||
self.name = name
|
|
||||||
self.mode = mode
|
|
||||||
self.averages = averages
|
|
||||||
self.logarithmic = logarithmic
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return (
|
|
||||||
f"Properties('{self.name}', {self.mode}, {self.averages},"
|
|
||||||
f" {self.logarithmic})"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
class Sweep:
|
class Sweep:
|
||||||
def __init__(
|
start: int = 3600000
|
||||||
self,
|
end: int = 30000000
|
||||||
start: int = 3600000,
|
points: int = 101
|
||||||
end: int = 30000000,
|
segments: int = 1
|
||||||
points: int = 101,
|
properties: "Properties" = Properties()
|
||||||
segments: int = 1,
|
|
||||||
properties: "Properties" = Properties(),
|
def __post_init__(self):
|
||||||
):
|
|
||||||
self.start = start
|
|
||||||
self.end = end
|
|
||||||
self.points = points
|
|
||||||
self.segments = segments
|
|
||||||
self.properties = properties
|
|
||||||
self.lock = Lock()
|
self.lock = Lock()
|
||||||
self.check()
|
self.check()
|
||||||
logger.debug("%s", self)
|
logger.debug("%s", self)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return (
|
|
||||||
f"Sweep({self.start}, {self.end}, {self.points}, {self.segments},"
|
|
||||||
f" {self.properties})"
|
|
||||||
)
|
|
||||||
|
|
||||||
def __eq__(self, other) -> bool:
|
|
||||||
return (
|
|
||||||
self.start == other.start
|
|
||||||
and self.end == other.end
|
|
||||||
and self.points == other.points
|
|
||||||
and self.segments == other.segments
|
|
||||||
and self.properties == other.properties
|
|
||||||
)
|
|
||||||
|
|
||||||
def copy(self) -> "Sweep":
|
def copy(self) -> "Sweep":
|
||||||
return Sweep(
|
return replace(self)
|
||||||
self.start, self.end, self.points, self.segments, self.properties
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def span(self) -> int:
|
def span(self) -> int:
|
||||||
|
@ -110,7 +76,7 @@ class Sweep:
|
||||||
def _exp_factor(self, index: int) -> float:
|
def _exp_factor(self, index: int) -> float:
|
||||||
return 1 - log(self.segments + 1 - index) / log(self.segments + 1)
|
return 1 - log(self.segments + 1 - index) / log(self.segments + 1)
|
||||||
|
|
||||||
def get_index_range(self, index: int) -> Tuple[int, int]:
|
def get_index_range(self, index: int) -> tuple[int, int]:
|
||||||
if not self.properties.logarithmic:
|
if not self.properties.logarithmic:
|
||||||
start = self.start + index * self.points * self.stepsize
|
start = self.start + index * self.points * self.stepsize
|
||||||
end = start + (self.points - 1) * self.stepsize
|
end = start + (self.points - 1) * self.stepsize
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from typing import List, Tuple
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from PyQt6 import QtCore, QtWidgets
|
from PyQt6 import QtCore, QtWidgets
|
||||||
|
@ -31,7 +30,7 @@ from NanoVNASaver.Settings.Sweep import Sweep, SweepMode
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def truncate(values: List[List[Tuple]], count: int) -> List[List[Tuple]]:
|
def truncate(values: list[list[tuple]], count: int) -> list[list[tuple]]:
|
||||||
"""truncate drops extrema from data list if averaging is active"""
|
"""truncate drops extrema from data list if averaging is active"""
|
||||||
keep = len(values) - count
|
keep = len(values) - count
|
||||||
logger.debug("Truncating from %d values to %d", len(values), keep)
|
logger.debug("Truncating from %d values to %d", len(values), keep)
|
||||||
|
@ -62,10 +61,10 @@ class SweepWorker(QtCore.QRunnable):
|
||||||
self.sweep = Sweep()
|
self.sweep = Sweep()
|
||||||
self.setAutoDelete(False)
|
self.setAutoDelete(False)
|
||||||
self.percentage = 0
|
self.percentage = 0
|
||||||
self.data11: List[Datapoint] = []
|
self.data11: list[Datapoint] = []
|
||||||
self.data21: List[Datapoint] = []
|
self.data21: list[Datapoint] = []
|
||||||
self.rawData11: List[Datapoint] = []
|
self.rawData11: list[Datapoint] = []
|
||||||
self.rawData21: List[Datapoint] = []
|
self.rawData21: list[Datapoint] = []
|
||||||
self.init_data()
|
self.init_data()
|
||||||
self.stopped = False
|
self.stopped = False
|
||||||
self.running = False
|
self.running = False
|
||||||
|
@ -187,10 +186,10 @@ class SweepWorker(QtCore.QRunnable):
|
||||||
self.signals.updated.emit()
|
self.signals.updated.emit()
|
||||||
|
|
||||||
def applyCalibration(
|
def applyCalibration(
|
||||||
self, raw_data11: List[Datapoint], raw_data21: List[Datapoint]
|
self, raw_data11: list[Datapoint], raw_data21: list[Datapoint]
|
||||||
) -> Tuple[List[Datapoint], List[Datapoint]]:
|
) -> tuple[list[Datapoint], list[Datapoint]]:
|
||||||
data11: List[Datapoint] = []
|
data11: list[Datapoint] = []
|
||||||
data21: List[Datapoint] = []
|
data21: list[Datapoint] = []
|
||||||
|
|
||||||
if not self.app.calibration.isCalculated:
|
if not self.app.calibration.isCalculated:
|
||||||
data11 = raw_data11.copy()
|
data11 = raw_data11.copy()
|
||||||
|
|
|
@ -22,8 +22,6 @@ import cmath
|
||||||
import io
|
import io
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from scipy.interpolate import interp1d
|
from scipy.interpolate import interp1d
|
||||||
|
|
||||||
from NanoVNASaver.RFTools import Datapoint
|
from NanoVNASaver.RFTools import Datapoint
|
||||||
|
@ -108,42 +106,42 @@ class Touchstone:
|
||||||
self._interp = {}
|
self._interp = {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def s11(self) -> List[Datapoint]:
|
def s11(self) -> list[Datapoint]:
|
||||||
return self.s("11")
|
return self.s("11")
|
||||||
|
|
||||||
@s11.setter
|
@s11.setter
|
||||||
def s11(self, value: List[Datapoint]):
|
def s11(self, value: list[Datapoint]):
|
||||||
self.sdata[0] = value
|
self.sdata[0] = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def s12(self) -> List[Datapoint]:
|
def s12(self) -> list[Datapoint]:
|
||||||
return self.s("12")
|
return self.s("12")
|
||||||
|
|
||||||
@s12.setter
|
@s12.setter
|
||||||
def s12(self, value: List[Datapoint]):
|
def s12(self, value: list[Datapoint]):
|
||||||
self.sdata[2] = value
|
self.sdata[2] = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def s21(self) -> List[Datapoint]:
|
def s21(self) -> list[Datapoint]:
|
||||||
return self.s("21")
|
return self.s("21")
|
||||||
|
|
||||||
@s21.setter
|
@s21.setter
|
||||||
def s21(self, value: List[Datapoint]):
|
def s21(self, value: list[Datapoint]):
|
||||||
self.sdata[1] = value
|
self.sdata[1] = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def s22(self) -> List[Datapoint]:
|
def s22(self) -> list[Datapoint]:
|
||||||
return self.s("22")
|
return self.s("22")
|
||||||
|
|
||||||
@s22.setter
|
@s22.setter
|
||||||
def s22(self, value: List[Datapoint]):
|
def s22(self, value: list[Datapoint]):
|
||||||
self.sdata[3] = value
|
self.sdata[3] = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def r(self) -> int:
|
def r(self) -> int:
|
||||||
return self.opts.resistance
|
return self.opts.resistance
|
||||||
|
|
||||||
def s(self, name: str) -> List[Datapoint]:
|
def s(self, name: str) -> list[Datapoint]:
|
||||||
return self.sdata[Touchstone.FIELD_ORDER.index(name)]
|
return self.sdata[Touchstone.FIELD_ORDER.index(name)]
|
||||||
|
|
||||||
def s_freq(self, name: str, freq: int) -> Datapoint:
|
def s_freq(self, name: str, freq: int) -> Datapoint:
|
||||||
|
|
|
@ -17,12 +17,12 @@
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
import typing
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Version:
|
_RXP = re.compile(
|
||||||
RXP = re.compile(
|
|
||||||
r"""^
|
r"""^
|
||||||
\D*
|
\D*
|
||||||
(?P<major>\d+)\.
|
(?P<major>\d+)\.
|
||||||
|
@ -33,61 +33,26 @@ class Version:
|
||||||
re.VERBOSE,
|
re.VERBOSE,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, vstring: str = "0.0.0"):
|
|
||||||
self.data = {
|
|
||||||
"major": 0,
|
|
||||||
"minor": 0,
|
|
||||||
"revision": 0,
|
|
||||||
"note": "",
|
|
||||||
}
|
|
||||||
try:
|
|
||||||
self.data = Version.RXP.search(vstring).groupdict()
|
|
||||||
for name in ("major", "minor", "revision"):
|
|
||||||
self.data[name] = int(self.data[name])
|
|
||||||
except TypeError:
|
|
||||||
self.data["revision"] = 0
|
|
||||||
except AttributeError:
|
|
||||||
logger.error("Unable to parse version: %s", vstring)
|
|
||||||
|
|
||||||
def __gt__(self, other: "Version") -> bool:
|
class _Version(typing.NamedTuple):
|
||||||
left, right = self.data, other.data
|
major: int
|
||||||
for name in ("major", "minor", "revision"):
|
minor: int
|
||||||
if left[name] > right[name]:
|
revision: int
|
||||||
return True
|
note: str
|
||||||
if left[name] < right[name]:
|
|
||||||
return False
|
|
||||||
return False
|
|
||||||
|
|
||||||
def __lt__(self, other: "Version") -> bool:
|
|
||||||
return other.__gt__(self)
|
|
||||||
|
|
||||||
def __ge__(self, other: "Version") -> bool:
|
|
||||||
return self.__gt__(other) or self.__eq__(other)
|
|
||||||
|
|
||||||
def __le__(self, other: "Version") -> bool:
|
|
||||||
return other.__gt__(self) or self.__eq__(other)
|
|
||||||
|
|
||||||
def __eq__(self, other: "Version") -> bool:
|
|
||||||
return self.data == other.data
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return (
|
return (
|
||||||
f'{self.data["major"]}.{self.data["minor"]}'
|
f'{self.major}.{self.minor}'
|
||||||
f'.{self.data["revision"]}{self.data["note"]}'
|
f'.{self.revision}{self.note}'
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
|
||||||
def major(self) -> int:
|
|
||||||
return self.data["major"]
|
|
||||||
|
|
||||||
@property
|
def Version(vstring: str = "0.0.0") -> '_Version':
|
||||||
def minor(self) -> int:
|
if (match := _RXP.search(vstring)) is None:
|
||||||
return self.data["minor"]
|
logger.error("Unable to parse version: %s", vstring)
|
||||||
|
return _Version(0, 0, 0, '')
|
||||||
|
|
||||||
@property
|
return _Version(int(match.group('major')),
|
||||||
def revision(self) -> int:
|
int(match.group('minor')),
|
||||||
return self.data["revision"]
|
int(match.group('revision') or '0'),
|
||||||
|
match.group('note'))
|
||||||
@property
|
|
||||||
def note(self) -> str:
|
|
||||||
return self.data["note"]
|
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import logging
|
import logging
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from PyQt6 import QtWidgets, QtCore
|
from PyQt6 import QtWidgets, QtCore
|
||||||
from PyQt6.QtGui import QColor, QColorConstants, QPalette, QShortcut
|
from PyQt6.QtGui import QColor, QColorConstants, QPalette, QShortcut
|
||||||
|
@ -210,7 +209,7 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
|
||||||
vswr_marker_box = QtWidgets.QGroupBox("VSWR Markers")
|
vswr_marker_box = QtWidgets.QGroupBox("VSWR Markers")
|
||||||
vswr_marker_layout = QtWidgets.QFormLayout(vswr_marker_box)
|
vswr_marker_layout = QtWidgets.QFormLayout(vswr_marker_box)
|
||||||
|
|
||||||
self.vswrMarkers: List[float] = self.app.settings.value(
|
self.vswrMarkers: list[float] = self.app.settings.value(
|
||||||
"VSWRMarkers", [], float
|
"VSWRMarkers", [], float
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,7 @@
|
||||||
from importlib.metadata import (
|
import importlib.metadata
|
||||||
PackageNotFoundError,
|
|
||||||
version,
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Change here if project is renamed and does not equal the package name
|
# Change here if project is renamed and does not equal the package name
|
||||||
dist_name = "nanovna-saver"
|
__version__ = importlib.metadata.version(distribution_name="nanovna-saver")
|
||||||
__version__ = version(dist_name)
|
except importlib.metadata.PackageNotFoundError: # pragma: no cover
|
||||||
except PackageNotFoundError: # pragma: no cover
|
|
||||||
__version__ = "unknown"
|
__version__ = "unknown"
|
||||||
finally:
|
|
||||||
del version, PackageNotFoundError
|
|
||||||
|
|
|
@ -27,8 +27,10 @@ class TestCases(unittest.TestCase):
|
||||||
def test_sweep(self):
|
def test_sweep(self):
|
||||||
sweep = Sweep()
|
sweep = Sweep()
|
||||||
self.assertEqual(str(sweep),
|
self.assertEqual(str(sweep),
|
||||||
"Sweep(3600000, 30000000, 101, 1, Properties('',"
|
"Sweep(start=3600000, end=30000000, points=101,"
|
||||||
" SweepMode.SINGLE, (3, 0), False))")
|
" segments=1, properties=Properties(name='',"
|
||||||
|
" mode=<SweepMode.SINGLE: 0>, averages=(3, 0),"
|
||||||
|
" logarithmic=False))")
|
||||||
self.assertTrue(Sweep(3600000) == sweep)
|
self.assertTrue(Sweep(3600000) == sweep)
|
||||||
self.assertFalse(Sweep(3600001) == sweep)
|
self.assertFalse(Sweep(3600001) == sweep)
|
||||||
self.assertRaises(ValueError, Sweep, -1)
|
self.assertRaises(ValueError, Sweep, -1)
|
||||||
|
|
Ładowanie…
Reference in New Issue