kopia lustrzana https://github.com/NanoVNA-Saver/nanovna-saver
commit
7b9d803b35
|
@ -30,7 +30,7 @@ class Analysis:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def find_crossing_zero(cls, data):
|
def find_crossing_zero(cls, data):
|
||||||
'''
|
"""
|
||||||
|
|
||||||
Find values crossing zero
|
Find values crossing zero
|
||||||
return list of tuples (before, crossing, after)
|
return list of tuples (before, crossing, after)
|
||||||
|
@ -44,7 +44,7 @@ class Analysis:
|
||||||
|
|
||||||
:param cls:
|
:param cls:
|
||||||
:param data: list of values
|
:param data: list of values
|
||||||
'''
|
"""
|
||||||
my_data = np.array(data)
|
my_data = np.array(data)
|
||||||
zeroes = np.where(my_data == 0)[0]
|
zeroes = np.where(my_data == 0)[0]
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ class Analysis:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def find_minimums(cls, data, threshold):
|
def find_minimums(cls, data, threshold):
|
||||||
'''
|
"""
|
||||||
|
|
||||||
Find values above threshold
|
Find values above threshold
|
||||||
return list of tuples (start, lowest, end)
|
return list of tuples (start, lowest, end)
|
||||||
|
@ -75,7 +75,7 @@ class Analysis:
|
||||||
:param cls:
|
:param cls:
|
||||||
:param data: list of values
|
:param data: list of values
|
||||||
:param threshold:
|
:param threshold:
|
||||||
'''
|
"""
|
||||||
|
|
||||||
minimums = []
|
minimums = []
|
||||||
min_start = -1
|
min_start = -1
|
||||||
|
@ -100,7 +100,7 @@ class Analysis:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def find_maximums(cls, data, threshold=None):
|
def find_maximums(cls, data, threshold=None):
|
||||||
'''
|
"""
|
||||||
|
|
||||||
Find peacs
|
Find peacs
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ class Analysis:
|
||||||
:param cls:
|
:param cls:
|
||||||
:param data: list of values
|
:param data: list of values
|
||||||
:param threshold:
|
:param threshold:
|
||||||
'''
|
"""
|
||||||
peaks, _ = signal.find_peaks(
|
peaks, _ = signal.find_peaks(
|
||||||
data, width=2, distance=3, prominence=1)
|
data, width=2, distance=3, prominence=1)
|
||||||
|
|
||||||
|
|
|
@ -34,11 +34,11 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class MagLoopAnalysis(VSWRAnalysis):
|
class MagLoopAnalysis(VSWRAnalysis):
|
||||||
'''
|
"""
|
||||||
Find min vswr and change sweep to zoom.
|
Find min vswr and change sweep to zoom.
|
||||||
Useful for tuning magloop.
|
Useful for tuning magloop.
|
||||||
|
|
||||||
'''
|
"""
|
||||||
max_dips_shown = 1
|
max_dips_shown = 1
|
||||||
|
|
||||||
vswr_bandwith_value = 2.56 # -3 dB ?!?
|
vswr_bandwith_value = 2.56 # -3 dB ?!?
|
||||||
|
@ -115,10 +115,10 @@ class MagLoopAnalysis(VSWRAnalysis):
|
||||||
QTimer.singleShot(2000, self._safe_sweep)
|
QTimer.singleShot(2000, self._safe_sweep)
|
||||||
|
|
||||||
def _safe_sweep(self):
|
def _safe_sweep(self):
|
||||||
'''
|
"""
|
||||||
sweep only if button enabled
|
sweep only if button enabled
|
||||||
to prevent multiple/concurrent sweep
|
to prevent multiple/concurrent sweep
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if self.app.sweep_control.btn_start.isEnabled():
|
if self.app.sweep_control.btn_start.isEnabled():
|
||||||
self.app.sweep_start()
|
self.app.sweep_start()
|
||||||
|
|
|
@ -220,10 +220,10 @@ class ResonanceAnalysis(Analysis):
|
||||||
for _ in range(results_header, self.layout.rowCount()):
|
for _ in range(results_header, self.layout.rowCount()):
|
||||||
self.layout.removeRow(self.layout.rowCount() - 1)
|
self.layout.removeRow(self.layout.rowCount() - 1)
|
||||||
|
|
||||||
# if len(crossing) > max_dips_shown:
|
# if len(crossing) > max_dips_shown:
|
||||||
# self.layout.addRow(QtWidgets.QLabel("<b>More than " + str(max_dips_shown) +
|
# self.layout.addRow(QtWidgets.QLabel("<b>More than " + str(max_dips_shown) +
|
||||||
# " dips found. Lowest shown.</b>"))
|
# " dips found. Lowest shown.</b>"))
|
||||||
# self.crossing = crossing[:max_dips_shown]
|
# self.crossing = crossing[:max_dips_shown]
|
||||||
if len(crossing) > 0:
|
if len(crossing) > 0:
|
||||||
extended_data = []
|
extended_data = []
|
||||||
for m in crossing:
|
for m in crossing:
|
||||||
|
@ -262,9 +262,9 @@ class ResonanceAnalysis(Analysis):
|
||||||
|
|
||||||
|
|
||||||
class EFHWAnalysis(ResonanceAnalysis):
|
class EFHWAnalysis(ResonanceAnalysis):
|
||||||
'''
|
"""
|
||||||
find only resonance when HI impedance
|
find only resonance when HI impedance
|
||||||
'''
|
"""
|
||||||
old_data = []
|
old_data = []
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
|
@ -296,7 +296,7 @@ class EFHWAnalysis(ResonanceAnalysis):
|
||||||
|
|
||||||
extended_data = OrderedDict()
|
extended_data = OrderedDict()
|
||||||
|
|
||||||
#both = np.intersect1d([i[1] for i in crossing], maximums)
|
# both = np.intersect1d([i[1] for i in crossing], maximums)
|
||||||
both = []
|
both = []
|
||||||
|
|
||||||
tolerance = 2
|
tolerance = 2
|
||||||
|
@ -320,7 +320,6 @@ class EFHWAnalysis(ResonanceAnalysis):
|
||||||
else:
|
else:
|
||||||
extended_data[m] = my_data
|
extended_data[m] = my_data
|
||||||
for i in range(min(len(both), len(self.app.markers))):
|
for i in range(min(len(both), len(self.app.markers))):
|
||||||
|
|
||||||
# self.app.markers[i].label = {}
|
# self.app.markers[i].label = {}
|
||||||
# for l in TYPES:
|
# for l in TYPES:
|
||||||
# self.app.markers[i][l.label_id] = MarkerLabel(l.name)
|
# self.app.markers[i][l.label_id] = MarkerLabel(l.name)
|
||||||
|
@ -366,7 +365,6 @@ class EFHWAnalysis(ResonanceAnalysis):
|
||||||
self.old_data.append(extended_data)
|
self.old_data.append(extended_data)
|
||||||
|
|
||||||
for i, index in enumerate(sorted(extended_data.keys())):
|
for i, index in enumerate(sorted(extended_data.keys())):
|
||||||
|
|
||||||
self.layout.addRow(
|
self.layout.addRow(
|
||||||
f"{format_frequency_short(self.app.data.s11[index].freq)}",
|
f"{format_frequency_short(self.app.data.s11[index].freq)}",
|
||||||
QtWidgets.QLabel(f" ({diff[i]['freq']})"
|
QtWidgets.QLabel(f" ({diff[i]['freq']})"
|
||||||
|
@ -389,7 +387,7 @@ class EFHWAnalysis(ResonanceAnalysis):
|
||||||
writer.writerow(row)
|
writer.writerow(row)
|
||||||
|
|
||||||
def compare(self, old, new, fields=None):
|
def compare(self, old, new, fields=None):
|
||||||
'''
|
"""
|
||||||
Compare data to help changes
|
Compare data to help changes
|
||||||
|
|
||||||
NB
|
NB
|
||||||
|
@ -397,7 +395,7 @@ class EFHWAnalysis(ResonanceAnalysis):
|
||||||
( same index must be same frequence )
|
( same index must be same frequence )
|
||||||
:param old:
|
:param old:
|
||||||
:param new:
|
:param new:
|
||||||
'''
|
"""
|
||||||
fields = fields or [("freq", str), ]
|
fields = fields or [("freq", str), ]
|
||||||
|
|
||||||
def no_compare():
|
def no_compare():
|
||||||
|
|
|
@ -198,14 +198,14 @@ class Calibration:
|
||||||
g2 * g3 * gm2 - g2 * g3 * gm3 -
|
g2 * g3 * gm2 - g2 * g3 * gm3 -
|
||||||
(g2 * gm2 - g3 * gm3) * g1)
|
(g2 * gm2 - g3 * gm3) * g1)
|
||||||
cal["e00"] = - ((g2 * gm3 - g3 * gm3) * g1 * gm2 -
|
cal["e00"] = - ((g2 * gm3 - g3 * gm3) * g1 * gm2 -
|
||||||
(g2 * g3 * gm2 - g2 * g3 * gm3 -
|
(g2 * g3 * gm2 - g2 * g3 * gm3 -
|
||||||
(g3 * gm2 - g2 * gm3) * g1) * gm1
|
(g3 * gm2 - g2 * gm3) * g1) * gm1
|
||||||
) / denominator
|
) / denominator
|
||||||
cal["e11"] = ((g2 - g3) * gm1 - g1 * (gm2 - gm3) +
|
cal["e11"] = ((g2 - g3) * gm1 - g1 * (gm2 - gm3) +
|
||||||
g3 * gm2 - g2 * gm3) / denominator
|
g3 * gm2 - g2 * gm3) / denominator
|
||||||
cal["delta_e"] = - ((g1 * (gm2 - gm3) - g2 * gm2 + g3 *
|
cal["delta_e"] = - ((g1 * (gm2 - gm3) - g2 * gm2 + g3 *
|
||||||
gm3) * gm1 + (g2 * gm3 - g3 * gm3) *
|
gm3) * gm1 + (g2 * gm3 - g3 * gm3) *
|
||||||
gm2) / denominator
|
gm2) / denominator
|
||||||
|
|
||||||
def _calc_port_2(self, freq: int, cal: CalData):
|
def _calc_port_2(self, freq: int, cal: CalData):
|
||||||
gt = self.gamma_through(freq)
|
gt = self.gamma_through(freq)
|
||||||
|
@ -218,9 +218,9 @@ class Calibration:
|
||||||
cal["e30"] = cal["isolation"].z
|
cal["e30"] = cal["isolation"].z
|
||||||
cal["e10e01"] = cal["e00"] * cal["e11"] - cal["delta_e"]
|
cal["e10e01"] = cal["e00"] * cal["e11"] - cal["delta_e"]
|
||||||
cal["e22"] = gm7 / (
|
cal["e22"] = gm7 / (
|
||||||
gm7 * cal["e11"] * gt**2 + cal["e10e01"] * gt**2)
|
gm7 * cal["e11"] * gt ** 2 + cal["e10e01"] * gt ** 2)
|
||||||
cal["e10e32"] = (gm4 - gm6) * (
|
cal["e10e32"] = (gm4 - gm6) * (
|
||||||
1 - cal["e11"] * cal["e22"] *gt**2) / gt
|
1 - cal["e11"] * cal["e22"] * gt ** 2) / gt
|
||||||
|
|
||||||
def calc_corrections(self):
|
def calc_corrections(self):
|
||||||
if not self.isValid1Port():
|
if not self.isValid1Port():
|
||||||
|
@ -254,8 +254,8 @@ class Calibration:
|
||||||
if not self.useIdealShort:
|
if not self.useIdealShort:
|
||||||
logger.debug("Using short calibration set values.")
|
logger.debug("Using short calibration set values.")
|
||||||
Zsp = complex(0, 2 * math.pi * freq * (
|
Zsp = complex(0, 2 * math.pi * freq * (
|
||||||
self.shortL0 + self.shortL1 * freq +
|
self.shortL0 + self.shortL1 * freq +
|
||||||
self.shortL2 * freq**2 + self.shortL3 * freq**3))
|
self.shortL2 * freq ** 2 + self.shortL3 * freq ** 3))
|
||||||
# Referencing https://arxiv.org/pdf/1606.02446.pdf (18) - (21)
|
# Referencing https://arxiv.org/pdf/1606.02446.pdf (18) - (21)
|
||||||
g = (Zsp / 50 - 1) / (Zsp / 50 + 1) * cmath.exp(
|
g = (Zsp / 50 - 1) / (Zsp / 50 + 1) * cmath.exp(
|
||||||
complex(0, 2 * math.pi * 2 * freq * self.shortLength * -1))
|
complex(0, 2 * math.pi * 2 * freq * self.shortLength * -1))
|
||||||
|
@ -266,8 +266,8 @@ class Calibration:
|
||||||
if not self.useIdealOpen:
|
if not self.useIdealOpen:
|
||||||
logger.debug("Using open calibration set values.")
|
logger.debug("Using open calibration set values.")
|
||||||
Zop = complex(0, 2 * math.pi * freq * (
|
Zop = complex(0, 2 * math.pi * freq * (
|
||||||
self.openC0 + self.openC1 * freq +
|
self.openC0 + self.openC1 * freq +
|
||||||
self.openC2 * freq**2 + self.openC3 * freq**3))
|
self.openC2 * freq ** 2 + self.openC3 * freq ** 3))
|
||||||
g = ((1 - 50 * Zop) / (1 + 50 * Zop)) * cmath.exp(
|
g = ((1 - 50 * Zop) / (1 + 50 * Zop)) * cmath.exp(
|
||||||
complex(0, 2 * math.pi * 2 * freq * self.openLength * -1))
|
complex(0, 2 * math.pi * 2 * freq * self.openLength * -1))
|
||||||
return g
|
return g
|
||||||
|
@ -324,8 +324,8 @@ class Calibration:
|
||||||
kind="slinear", bounds_error=False,
|
kind="slinear", bounds_error=False,
|
||||||
fill_value=(delta_e[0], delta_e[-1])),
|
fill_value=(delta_e[0], delta_e[-1])),
|
||||||
"e10e01": interp1d(freq, e10e01,
|
"e10e01": interp1d(freq, e10e01,
|
||||||
kind="slinear", bounds_error=False,
|
kind="slinear", bounds_error=False,
|
||||||
fill_value=(e10e01[0], e10e01[-1])),
|
fill_value=(e10e01[0], e10e01[-1])),
|
||||||
"e30": interp1d(freq, e30,
|
"e30": interp1d(freq, e30,
|
||||||
kind="slinear", bounds_error=False,
|
kind="slinear", bounds_error=False,
|
||||||
fill_value=(e30[0], e30[-1])),
|
fill_value=(e30[0], e30[-1])),
|
||||||
|
@ -340,13 +340,13 @@ class Calibration:
|
||||||
def correct11(self, dp: Datapoint):
|
def correct11(self, dp: Datapoint):
|
||||||
i = self.interp
|
i = self.interp
|
||||||
s11 = (dp.z - i["e00"](dp.freq)) / (
|
s11 = (dp.z - i["e00"](dp.freq)) / (
|
||||||
(dp.z * i["e11"](dp.freq)) - i["delta_e"](dp.freq))
|
(dp.z * i["e11"](dp.freq)) - i["delta_e"](dp.freq))
|
||||||
return Datapoint(dp.freq, s11.real, s11.imag)
|
return Datapoint(dp.freq, s11.real, s11.imag)
|
||||||
|
|
||||||
def correct21(self, dp: Datapoint, dp11: Datapoint):
|
def correct21(self, dp: Datapoint, dp11: Datapoint):
|
||||||
i = self.interp
|
i = self.interp
|
||||||
s21 = (dp.z - i["e30"](dp.freq)) / i["e10e32"](dp.freq)
|
s21 = (dp.z - i["e30"](dp.freq)) / i["e10e32"](dp.freq)
|
||||||
s21 = s21 * (i["e10e01"](dp.freq)/(i["e11"](dp.freq)*dp11.z-i["delta_e"](dp.freq)))
|
s21 = s21 * (i["e10e01"](dp.freq) / (i["e11"](dp.freq) * dp11.z - i["delta_e"](dp.freq)))
|
||||||
return Datapoint(dp.freq, s21.real, s21.imag)
|
return Datapoint(dp.freq, s21.real, s21.imag)
|
||||||
|
|
||||||
# TODO: implement tests
|
# TODO: implement tests
|
||||||
|
@ -381,7 +381,7 @@ class Calibration:
|
||||||
continue
|
continue
|
||||||
if line.startswith("#"):
|
if line.startswith("#"):
|
||||||
if not parsed_header and line == (
|
if not parsed_header and line == (
|
||||||
"# Hz ShortR ShortI OpenR OpenI LoadR LoadI"
|
"# Hz ShortR ShortI OpenR OpenI LoadR LoadI"
|
||||||
" ThroughR ThroughI ThrureflR ThrureflI IsolationR IsolationI"):
|
" ThroughR ThroughI ThrureflR ThrureflI IsolationR IsolationI"):
|
||||||
parsed_header = True
|
parsed_header = True
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -22,6 +22,7 @@ from PyQt5 import QtWidgets, QtCore
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Control(QtWidgets.QGroupBox):
|
class Control(QtWidgets.QGroupBox):
|
||||||
updated = QtCore.pyqtSignal(object)
|
updated = QtCore.pyqtSignal(object)
|
||||||
|
|
||||||
|
|
|
@ -25,12 +25,11 @@ from NanoVNASaver import Defaults
|
||||||
from NanoVNASaver.Marker import Marker
|
from NanoVNASaver.Marker import Marker
|
||||||
from NanoVNASaver.Controls.Control import Control
|
from NanoVNASaver.Controls.Control import Control
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ShowButton(QtWidgets.QPushButton):
|
class ShowButton(QtWidgets.QPushButton):
|
||||||
def setText(self, text: str=''):
|
def setText(self, text: str = ''):
|
||||||
if not text:
|
if not text:
|
||||||
text = ("Show data"
|
text = ("Show data"
|
||||||
if Defaults.cfg.gui.markers_hidden else "Hide data")
|
if Defaults.cfg.gui.markers_hidden else "Hide data")
|
||||||
|
@ -87,6 +86,7 @@ class MarkerControl(Control):
|
||||||
Defaults.cfg.gui.markers_hidden)
|
Defaults.cfg.gui.markers_hidden)
|
||||||
self.showMarkerButton.setText()
|
self.showMarkerButton.setText()
|
||||||
self.showMarkerButton.repaint()
|
self.showMarkerButton.repaint()
|
||||||
|
|
||||||
settings(self.app.marker_frame.isHidden())
|
settings(self.app.marker_frame.isHidden())
|
||||||
|
|
||||||
def toggle_delta(self):
|
def toggle_delta(self):
|
||||||
|
|
|
@ -26,6 +26,7 @@ from NanoVNASaver.Controls.Control import Control
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class SerialControl(Control):
|
class SerialControl(Control):
|
||||||
|
|
||||||
def __init__(self, app: QtWidgets.QWidget):
|
def __init__(self, app: QtWidgets.QWidget):
|
||||||
|
|
|
@ -18,6 +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
|
||||||
from numbers import Number
|
from numbers import Number
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
from NanoVNASaver import SITools
|
from NanoVNASaver import SITools
|
||||||
|
|
||||||
|
@ -42,13 +43,14 @@ FMT_PARSE = SITools.Format(parse_sloppy_unit=True, parse_sloppy_kilo=True,
|
||||||
parse_clamp_min=0)
|
parse_clamp_min=0)
|
||||||
FMT_PARSE_VALUE = SITools.Format(
|
FMT_PARSE_VALUE = SITools.Format(
|
||||||
parse_sloppy_unit=True, parse_sloppy_kilo=True)
|
parse_sloppy_unit=True, parse_sloppy_kilo=True)
|
||||||
|
FMT_VSWR = SITools.Format(max_nr_digits=3)
|
||||||
|
|
||||||
|
|
||||||
def format_frequency(freq: Number) -> str:
|
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: float) -> str:
|
def format_frequency_inputs(freq: Union[Number, str]) -> str:
|
||||||
return str(SITools.Value(freq, "Hz", FMT_FREQ_INPUTS))
|
return str(SITools.Value(freq, "Hz", FMT_FREQ_INPUTS))
|
||||||
|
|
||||||
|
|
||||||
|
@ -140,7 +142,7 @@ def format_wavelength(length: Number) -> str:
|
||||||
return str(SITools.Value(length, "m", FMT_WAVELENGTH))
|
return str(SITools.Value(length, "m", FMT_WAVELENGTH))
|
||||||
|
|
||||||
|
|
||||||
def format_y_axis(val: float, unit: str="") -> str:
|
def format_y_axis(val: float, unit: str = "") -> str:
|
||||||
return str(SITools.Value(val, unit, FMT_SHORT))
|
return str(SITools.Value(val, unit, FMT_SHORT))
|
||||||
|
|
||||||
|
|
||||||
|
@ -152,7 +154,7 @@ def parse_frequency(freq: str) -> int:
|
||||||
|
|
||||||
|
|
||||||
def parse_value(val: str, unit: str = "",
|
def parse_value(val: str, unit: str = "",
|
||||||
fmt: SITools.Format = FMT_PARSE_VALUE) -> int:
|
fmt: SITools.Format = FMT_PARSE_VALUE) -> float:
|
||||||
try:
|
try:
|
||||||
val.replace(',', '.')
|
val.replace(',', '.')
|
||||||
return float(SITools.Value(val, unit, fmt))
|
return float(SITools.Value(val, unit, fmt))
|
||||||
|
|
|
@ -25,6 +25,7 @@ from typing import List
|
||||||
import serial
|
import serial
|
||||||
from serial.tools import list_ports
|
from serial.tools import list_ports
|
||||||
|
|
||||||
|
from NanoVNASaver.Hardware.VNA import VNA
|
||||||
from NanoVNASaver.Hardware.AVNA import AVNA
|
from NanoVNASaver.Hardware.AVNA import AVNA
|
||||||
from NanoVNASaver.Hardware.NanoVNA import NanoVNA
|
from NanoVNASaver.Hardware.NanoVNA import NanoVNA
|
||||||
from NanoVNASaver.Hardware.NanoVNA_F import NanoVNA_F
|
from NanoVNASaver.Hardware.NanoVNA_F import NanoVNA_F
|
||||||
|
@ -34,8 +35,6 @@ 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
|
||||||
from NanoVNASaver.Hardware.Serial import drain_serial, Interface
|
from NanoVNASaver.Hardware.Serial import drain_serial, Interface
|
||||||
from NanoVNASaver.Hardware.VNA import VNA
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -51,7 +50,7 @@ TIMEOUT = 0.2
|
||||||
WAIT = 0.05
|
WAIT = 0.05
|
||||||
|
|
||||||
NAME2DEVICE = {
|
NAME2DEVICE = {
|
||||||
"S-A-A-2" : NanoVNA_V2,
|
"S-A-A-2": NanoVNA_V2,
|
||||||
"AVNA": AVNA,
|
"AVNA": AVNA,
|
||||||
"H4": NanoVNA_H4,
|
"H4": NanoVNA_H4,
|
||||||
"H": NanoVNA_H,
|
"H": NanoVNA_H,
|
||||||
|
@ -62,6 +61,7 @@ NAME2DEVICE = {
|
||||||
"Unknown": NanoVNA,
|
"Unknown": NanoVNA,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# The USB Driver for NanoVNA V2 seems to deliver an
|
# The USB Driver for NanoVNA V2 seems to deliver an
|
||||||
# incompatible hardware info like:
|
# incompatible hardware info like:
|
||||||
# 'PORTS\\VID_04B4&PID_0008\\DEMO'
|
# 'PORTS\\VID_04B4&PID_0008\\DEMO'
|
||||||
|
@ -88,11 +88,7 @@ def get_interfaces() -> List[Interface]:
|
||||||
t.name, d.vid, d.pid, d.device)
|
t.name, d.vid, d.pid, d.device)
|
||||||
iface = Interface('serial', t.name)
|
iface = Interface('serial', t.name)
|
||||||
iface.port = d.device
|
iface.port = d.device
|
||||||
try:
|
iface.open()
|
||||||
iface.open()
|
|
||||||
except serial.SerialException:
|
|
||||||
logger.warning("Could not open serial port %s", d.device)
|
|
||||||
continue
|
|
||||||
iface.comment = get_comment(iface)
|
iface.comment = get_comment(iface)
|
||||||
iface.close()
|
iface.close()
|
||||||
interfaces.append(iface)
|
interfaces.append(iface)
|
||||||
|
@ -101,10 +97,11 @@ def get_interfaces() -> List[Interface]:
|
||||||
return interfaces
|
return interfaces
|
||||||
|
|
||||||
|
|
||||||
def get_VNA(iface: Interface) -> 'VNA':
|
def get_VNA(iface: Interface) -> VNA:
|
||||||
# serial_port.timeout = TIMEOUT
|
# serial_port.timeout = TIMEOUT
|
||||||
return NAME2DEVICE[iface.comment](iface)
|
return NAME2DEVICE[iface.comment](iface)
|
||||||
|
|
||||||
|
|
||||||
def get_comment(iface: Interface) -> str:
|
def get_comment(iface: Interface) -> str:
|
||||||
logger.info("Finding correct VNA type...")
|
logger.info("Finding correct VNA type...")
|
||||||
with iface.lock:
|
with iface.lock:
|
||||||
|
@ -116,19 +113,20 @@ def get_comment(iface: Interface) -> str:
|
||||||
logger.info("Finding firmware variant...")
|
logger.info("Finding firmware variant...")
|
||||||
info = get_info(iface)
|
info = get_info(iface)
|
||||||
for search, name in (
|
for search, name in (
|
||||||
("AVNA + Teensy", "AVNA"),
|
("AVNA + Teensy", "AVNA"),
|
||||||
("NanoVNA-H 4", "H4"),
|
("NanoVNA-H 4", "H4"),
|
||||||
("NanoVNA-H", "H"),
|
("NanoVNA-H", "H"),
|
||||||
("NanoVNA-F_V2", "F_V2"),
|
("NanoVNA-F_V2", "F_V2"),
|
||||||
("NanoVNA-F", "F"),
|
("NanoVNA-F", "F"),
|
||||||
("NanoVNA", "NanoVNA"),
|
("NanoVNA", "NanoVNA"),
|
||||||
("tinySA", "tinySA"),
|
("tinySA", "tinySA"),
|
||||||
):
|
):
|
||||||
if info.find(search) >= 0:
|
if info.find(search) >= 0:
|
||||||
return name
|
return name
|
||||||
logger.warning("Did not recognize NanoVNA type from firmware.")
|
logger.warning("Did not recognize NanoVNA type from firmware.")
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
|
|
||||||
|
|
||||||
def detect_version(serial_port: serial.Serial) -> str:
|
def detect_version(serial_port: serial.Serial) -> str:
|
||||||
data = ""
|
data = ""
|
||||||
for i in range(RETRIES):
|
for i in range(RETRIES):
|
||||||
|
|
|
@ -61,7 +61,6 @@ class NanoVNA(VNA):
|
||||||
timeout = self.serial.timeout
|
timeout = self.serial.timeout
|
||||||
with self.serial.lock:
|
with self.serial.lock:
|
||||||
drain_serial(self.serial)
|
drain_serial(self.serial)
|
||||||
timeout = self.serial.timeout
|
|
||||||
self.serial.write("capture\r".encode('ascii'))
|
self.serial.write("capture\r".encode('ascii'))
|
||||||
self.serial.readline()
|
self.serial.readline()
|
||||||
self.serial.timeout = 4
|
self.serial.timeout = 4
|
||||||
|
|
|
@ -66,6 +66,7 @@ _ADF4350_TXPOWER_DESC_MAP = {
|
||||||
_ADF4350_TXPOWER_DESC_REV_MAP = {
|
_ADF4350_TXPOWER_DESC_REV_MAP = {
|
||||||
value: key for key, value in _ADF4350_TXPOWER_DESC_MAP.items()}
|
value: key for key, value in _ADF4350_TXPOWER_DESC_MAP.items()}
|
||||||
|
|
||||||
|
|
||||||
class NanoVNA_V2(VNA):
|
class NanoVNA_V2(VNA):
|
||||||
name = "NanoVNA-V2"
|
name = "NanoVNA-V2"
|
||||||
valid_datapoints = (101, 11, 51, 201, 301, 501, 1023)
|
valid_datapoints = (101, 11, 51, 201, 301, 501, 1023)
|
||||||
|
@ -145,7 +146,7 @@ class NanoVNA_V2(VNA):
|
||||||
sleep(WRITE_SLEEP)
|
sleep(WRITE_SLEEP)
|
||||||
# clear sweepdata
|
# clear sweepdata
|
||||||
self._sweepdata = [(complex(), complex())] * (
|
self._sweepdata = [(complex(), complex())] * (
|
||||||
self.datapoints + s21hack)
|
self.datapoints + s21hack)
|
||||||
pointstodo = self.datapoints + s21hack
|
pointstodo = self.datapoints + s21hack
|
||||||
# we read at most 255 values at a time and the time required empirically is
|
# we read at most 255 values at a time and the time required empirically is
|
||||||
# just over 3 seconds for 101 points or 7 seconds for 255 points
|
# just over 3 seconds for 101 points or 7 seconds for 255 points
|
||||||
|
@ -178,7 +179,7 @@ class NanoVNA_V2(VNA):
|
||||||
for i in range(pointstoread):
|
for i in range(pointstoread):
|
||||||
(fwd_real, fwd_imag, rev0_real, rev0_imag, rev1_real,
|
(fwd_real, fwd_imag, rev0_real, rev0_imag, rev1_real,
|
||||||
rev1_imag, freq_index) = unpack_from(
|
rev1_imag, freq_index) = unpack_from(
|
||||||
"<iiiiiihxxxxxx", arr, i * 32)
|
"<iiiiiihxxxxxx", arr, i * 32)
|
||||||
fwd = complex(fwd_real, fwd_imag)
|
fwd = complex(fwd_real, fwd_imag)
|
||||||
refl = complex(rev0_real, rev0_imag)
|
refl = complex(rev0_real, rev0_imag)
|
||||||
thru = complex(rev1_real, rev1_imag)
|
thru = complex(rev1_real, rev1_imag)
|
||||||
|
@ -237,7 +238,6 @@ class NanoVNA_V2(VNA):
|
||||||
logger.debug("read_board_revision: %s", result)
|
logger.debug("read_board_revision: %s", result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def setSweep(self, start, stop):
|
def setSweep(self, start, stop):
|
||||||
step = (stop - start) / (self.datapoints - 1)
|
step = (stop - start) / (self.datapoints - 1)
|
||||||
if start == self.sweepStartHz and step == self.sweepStepHz:
|
if start == self.sweepStartHz and step == self.sweepStepHz:
|
||||||
|
@ -271,6 +271,7 @@ class NanoVNA_V2(VNA):
|
||||||
self._set_register(0x42, _ADF4350_TXPOWER_DESC_REV_MAP[power_desc], 1)
|
self._set_register(0x42, _ADF4350_TXPOWER_DESC_REV_MAP[power_desc], 1)
|
||||||
|
|
||||||
def _set_register(self, addr, value, size):
|
def _set_register(self, addr, value, size):
|
||||||
|
packet = b''
|
||||||
if size == 1:
|
if size == 1:
|
||||||
packet = pack("<BBB", _CMD_WRITE, addr, value)
|
packet = pack("<BBB", _CMD_WRITE, addr, value)
|
||||||
elif size == 2:
|
elif size == 2:
|
||||||
|
|
|
@ -60,7 +60,6 @@ class TinySA(VNA):
|
||||||
timeout = self.serial.timeout
|
timeout = self.serial.timeout
|
||||||
with self.serial.lock:
|
with self.serial.lock:
|
||||||
drain_serial(self.serial)
|
drain_serial(self.serial)
|
||||||
timeout = self.serial.timeout
|
|
||||||
self.serial.write("capture\r".encode('ascii'))
|
self.serial.write("capture\r".encode('ascii'))
|
||||||
self.serial.readline()
|
self.serial.readline()
|
||||||
self.serial.timeout = 4
|
self.serial.timeout = 4
|
||||||
|
|
|
@ -155,11 +155,11 @@ class VNA:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _get_running_frequencies(self):
|
def _get_running_frequencies(self):
|
||||||
'''
|
"""
|
||||||
If possible, read frequencies already runnung
|
If possible, read frequencies already runnung
|
||||||
if not return default values
|
if not return default values
|
||||||
Overwrite in specific HW
|
Overwrite in specific HW
|
||||||
'''
|
"""
|
||||||
return 27000000, 30000000
|
return 27000000, 30000000
|
||||||
|
|
||||||
def connected(self) -> bool:
|
def connected(self) -> bool:
|
||||||
|
|
|
@ -28,7 +28,6 @@ class FrequencyInputWidget(QtWidgets.QLineEdit):
|
||||||
self.previousFrequency = -1
|
self.previousFrequency = -1
|
||||||
|
|
||||||
def setText(self, text: str) -> None:
|
def setText(self, text: str) -> None:
|
||||||
# TODO: Fix wrong type here
|
|
||||||
super().setText(format_frequency_inputs(text))
|
super().setText(format_frequency_inputs(text))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ from NanoVNASaver.Formatting import (
|
||||||
|
|
||||||
from .Widget import Marker
|
from .Widget import Marker
|
||||||
|
|
||||||
|
|
||||||
class DeltaMarker(Marker):
|
class DeltaMarker(Marker):
|
||||||
def __init__(self, name: str = "", qsettings: QtCore.QSettings = None):
|
def __init__(self, name: str = "", qsettings: QtCore.QSettings = None):
|
||||||
super().__init__(name, qsettings)
|
super().__init__(name, qsettings)
|
||||||
|
@ -71,10 +72,10 @@ class DeltaMarker(Marker):
|
||||||
imp_p = imp_p_b - imp_p_a
|
imp_p = imp_p_b - imp_p_a
|
||||||
|
|
||||||
cap_p_str = format_capacitance(
|
cap_p_str = format_capacitance(
|
||||||
RFTools.impedance_to_capacitance(imp_p_b, s11_b.freq)-
|
RFTools.impedance_to_capacitance(imp_p_b, s11_b.freq) -
|
||||||
RFTools.impedance_to_capacitance(imp_p_a, s11_a.freq))
|
RFTools.impedance_to_capacitance(imp_p_a, s11_a.freq))
|
||||||
ind_p_str = format_inductance(
|
ind_p_str = format_inductance(
|
||||||
RFTools.impedance_to_inductance(imp_p_b, s11_b.freq)-
|
RFTools.impedance_to_inductance(imp_p_b, s11_b.freq) -
|
||||||
RFTools.impedance_to_inductance(imp_p_a, s11_a.freq))
|
RFTools.impedance_to_inductance(imp_p_a, s11_a.freq))
|
||||||
|
|
||||||
x_str = cap_str if imp.imag < 0 else ind_str
|
x_str = cap_str if imp.imag < 0 else ind_str
|
||||||
|
@ -126,5 +127,5 @@ class DeltaMarker(Marker):
|
||||||
self.label['s21phase'].setText(format_phase(
|
self.label['s21phase'].setText(format_phase(
|
||||||
s21_b.phase - s21_a.phase))
|
s21_b.phase - s21_a.phase))
|
||||||
self.label['s21polar'].setText(
|
self.label['s21polar'].setText(
|
||||||
f"{round(abs(s21_b.z) - abs(s21_a.z) , 2)}∠"
|
f"{round(abs(s21_b.z) - abs(s21_a.z), 2)}∠"
|
||||||
f"{format_phase(s21_b.phase - s21_a.phase)}")
|
f"{format_phase(s21_b.phase - s21_a.phase)}")
|
||||||
|
|
|
@ -45,16 +45,12 @@ class Datapoint(NamedTuple):
|
||||||
@property
|
@property
|
||||||
def gain(self) -> float:
|
def gain(self) -> float:
|
||||||
mag = abs(self.z)
|
mag = abs(self.z)
|
||||||
if mag > 0:
|
return 20 * math.log10(mag) if mag > 0 else -math.inf
|
||||||
return 20 * math.log10(mag)
|
|
||||||
return -math.inf
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def vswr(self) -> float:
|
def vswr(self) -> float:
|
||||||
mag = abs(self.z)
|
mag = abs(self.z)
|
||||||
if mag >= 1:
|
return (1 + mag) / (1 - mag) if mag < 1 else math.inf
|
||||||
return math.inf
|
|
||||||
return (1 + mag) / (1 - mag)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def wavelength(self) -> float:
|
def wavelength(self) -> float:
|
||||||
|
@ -77,9 +73,7 @@ class Datapoint(NamedTuple):
|
||||||
|
|
||||||
def qFactor(self, ref_impedance: float = 50) -> float:
|
def qFactor(self, ref_impedance: float = 50) -> float:
|
||||||
imp = self.impedance(ref_impedance)
|
imp = self.impedance(ref_impedance)
|
||||||
if imp.real == 0.0:
|
return -1 if imp.real == 0.0 else abs(imp.imag / imp.real)
|
||||||
return -1
|
|
||||||
return abs(imp.imag / imp.real)
|
|
||||||
|
|
||||||
def capacitiveEquivalent(self, ref_impedance: float = 50) -> float:
|
def capacitiveEquivalent(self, ref_impedance: float = 50) -> float:
|
||||||
return impedance_to_capacitance(self.impedance(ref_impedance), self.freq)
|
return impedance_to_capacitance(self.impedance(ref_impedance), self.freq)
|
||||||
|
@ -101,9 +95,7 @@ def groupDelay(data: List[Datapoint], index: int) -> float:
|
||||||
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
|
||||||
delta_freq = data[idx1].freq - data[idx0].freq
|
delta_freq = data[idx1].freq - data[idx0].freq
|
||||||
if delta_freq == 0:
|
return 0 if delta_freq == 0 else -delta_angle / math.tau / delta_freq
|
||||||
return 0
|
|
||||||
return -delta_angle / math.tau / delta_freq
|
|
||||||
|
|
||||||
|
|
||||||
def impedance_to_capacitance(z: complex, freq: float) -> float:
|
def impedance_to_capacitance(z: complex, freq: float) -> float:
|
||||||
|
@ -117,9 +109,7 @@ def impedance_to_capacitance(z: complex, freq: float) -> float:
|
||||||
|
|
||||||
def impedance_to_inductance(z: complex, freq: float) -> float:
|
def impedance_to_inductance(z: complex, freq: float) -> float:
|
||||||
"""Calculate inductive equivalent for reactance"""
|
"""Calculate inductive equivalent for reactance"""
|
||||||
if freq == 0:
|
return 0 if freq == 0 else z.imag * 1 / (freq * 2 * math.pi)
|
||||||
return 0
|
|
||||||
return z.imag * 1 / (freq * 2 * math.pi)
|
|
||||||
|
|
||||||
|
|
||||||
def impedance_to_norm(z: complex, ref_impedance: float = 50) -> complex:
|
def impedance_to_norm(z: complex, ref_impedance: float = 50) -> complex:
|
||||||
|
@ -134,8 +124,7 @@ def norm_to_impedance(z: complex, ref_impedance: float = 50) -> complex:
|
||||||
|
|
||||||
def parallel_to_serial(z: complex) -> complex:
|
def parallel_to_serial(z: complex) -> complex:
|
||||||
"""Convert parallel impedance to serial impedance equivalent"""
|
"""Convert parallel impedance to serial impedance equivalent"""
|
||||||
z_sq_sum = z.real ** 2 + z.imag ** 2
|
z_sq_sum = z.real ** 2 + z.imag ** 2 or 10.0e-30
|
||||||
# TODO: Fix divide by zero
|
|
||||||
return complex(z.real * z.imag ** 2 / z_sq_sum,
|
return complex(z.real * z.imag ** 2 / z_sq_sum,
|
||||||
z.real ** 2 * z.imag / z_sq_sum)
|
z.real ** 2 * z.imag / z_sq_sum)
|
||||||
|
|
||||||
|
@ -150,9 +139,6 @@ def serial_to_parallel(z: complex) -> complex:
|
||||||
z_sq_sum = z.real ** 2 + z.imag ** 2
|
z_sq_sum = z.real ** 2 + z.imag ** 2
|
||||||
if z.real == 0 and z.imag == 0:
|
if z.real == 0 and z.imag == 0:
|
||||||
return complex(math.inf, math.inf)
|
return complex(math.inf, math.inf)
|
||||||
# only possible if real and imag == 0, therefor commented out
|
|
||||||
# if z_sq_sum == 0:
|
|
||||||
# return complex(0, 0)
|
|
||||||
if z.imag == 0:
|
if z.imag == 0:
|
||||||
return complex(z_sq_sum / z.real, math.copysign(math.inf, z_sq_sum))
|
return complex(z_sq_sum / z.real, math.copysign(math.inf, z_sq_sum))
|
||||||
if z.real == 0:
|
if z.real == 0:
|
||||||
|
|
|
@ -34,14 +34,17 @@ def clamp_value(value: Real, rmin: Real, rmax: Real) -> Real:
|
||||||
return rmax
|
return rmax
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def round_ceil(value: Real, digits: int=0) -> Real:
|
|
||||||
factor = 10 ** digits
|
def round_ceil(value: Real, digits: int = 0) -> Real:
|
||||||
|
factor = 10 ** -digits
|
||||||
return factor * math.ceil(value / factor)
|
return factor * math.ceil(value / factor)
|
||||||
|
|
||||||
def round_floor(value: Real, digits: int=0) -> Real:
|
|
||||||
factor = 10 ** digits
|
def round_floor(value: Real, digits: int = 0) -> Real:
|
||||||
|
factor = 10 ** -digits
|
||||||
return factor * math.floor(value / factor)
|
return factor * math.floor(value / factor)
|
||||||
|
|
||||||
|
|
||||||
class Format(NamedTuple):
|
class Format(NamedTuple):
|
||||||
max_nr_digits: int = 6
|
max_nr_digits: int = 6
|
||||||
fix_decimals: bool = False
|
fix_decimals: bool = False
|
||||||
|
@ -105,8 +108,8 @@ class Value:
|
||||||
formstr = ".0f"
|
formstr = ".0f"
|
||||||
else:
|
else:
|
||||||
max_digits = fmt.max_nr_digits + (
|
max_digits = fmt.max_nr_digits + (
|
||||||
(1 if not fmt.fix_decimals and abs(real) < 10 else 0) +
|
(1 if not fmt.fix_decimals and abs(real) < 10 else 0) +
|
||||||
(1 if not fmt.fix_decimals and abs(real) < 100 else 0))
|
(1 if not fmt.fix_decimals and abs(real) < 100 else 0))
|
||||||
formstr = f".{max_digits - 3}f"
|
formstr = f".{max_digits - 3}f"
|
||||||
|
|
||||||
if self.fmt.allways_signed:
|
if self.fmt.allways_signed:
|
||||||
|
|
|
@ -31,7 +31,7 @@ class SweepMode(Enum):
|
||||||
AVERAGE = 2
|
AVERAGE = 2
|
||||||
|
|
||||||
|
|
||||||
class Properties():
|
class Properties:
|
||||||
def __init__(self, name: str = "",
|
def __init__(self, name: str = "",
|
||||||
mode: 'SweepMode' = SweepMode.SINGLE,
|
mode: 'SweepMode' = SweepMode.SINGLE,
|
||||||
averages: Tuple[int, int] = (3, 0),
|
averages: Tuple[int, int] = (3, 0),
|
||||||
|
@ -47,7 +47,7 @@ class Properties():
|
||||||
f" {self.logarithmic})")
|
f" {self.logarithmic})")
|
||||||
|
|
||||||
|
|
||||||
class Sweep():
|
class Sweep:
|
||||||
def __init__(self, start: int = 3600000, end: int = 30000000,
|
def __init__(self, start: int = 3600000, end: int = 30000000,
|
||||||
points: int = 101, segments: int = 1,
|
points: int = 101, segments: int = 1,
|
||||||
properties: 'Properties' = Properties()):
|
properties: 'Properties' = Properties()):
|
||||||
|
@ -66,11 +66,11 @@ class Sweep():
|
||||||
f" {self.properties})")
|
f" {self.properties})")
|
||||||
|
|
||||||
def __eq__(self, other) -> bool:
|
def __eq__(self, other) -> bool:
|
||||||
return(self.start == other.start and
|
return (self.start == other.start and
|
||||||
self.end == other.end and
|
self.end == other.end and
|
||||||
self.points == other.points and
|
self.points == other.points and
|
||||||
self.segments == other.segments and
|
self.segments == other.segments and
|
||||||
self.properties == other.properties)
|
self.properties == other.properties)
|
||||||
|
|
||||||
def copy(self) -> 'Sweep':
|
def copy(self) -> 'Sweep':
|
||||||
return Sweep(self.start, self.end, self.points, self.segments,
|
return Sweep(self.start, self.end, self.points, self.segments,
|
||||||
|
@ -82,15 +82,15 @@ class Sweep():
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def stepsize(self) -> int:
|
def stepsize(self) -> int:
|
||||||
return round(self.span / (self.points * self.segments - 1))
|
return round(self.span / (self.points * self.segments - 1))
|
||||||
|
|
||||||
def check(self):
|
def check(self):
|
||||||
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
|
||||||
):
|
):
|
||||||
raise ValueError(f"Illegal sweep settings: {self}")
|
raise ValueError(f"Illegal sweep settings: {self}")
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ class Sweep():
|
||||||
start = round(self.start + self.span * self._exp_factor(index))
|
start = round(self.start + self.span * self._exp_factor(index))
|
||||||
end = round(self.start + self.span * self._exp_factor(index + 1))
|
end = round(self.start + self.span * self._exp_factor(index + 1))
|
||||||
logger.debug("get_index_range(%s) -> (%s, %s)", index, start, end)
|
logger.debug("get_index_range(%s) -> (%s, %s)", index, start, end)
|
||||||
return (start, end)
|
return start, end
|
||||||
|
|
||||||
def get_frequencies(self) -> Iterator[int]:
|
def get_frequencies(self) -> Iterator[int]:
|
||||||
for i in range(self.segments):
|
for i in range(self.segments):
|
||||||
|
|
|
@ -226,6 +226,7 @@ class SweepWorker(QtCore.QRunnable):
|
||||||
logger.debug("Reading average no %d / %d", i + 1, averages)
|
logger.debug("Reading average no %d / %d", i + 1, averages)
|
||||||
retry = 0
|
retry = 0
|
||||||
tmp11 = []
|
tmp11 = []
|
||||||
|
tmp21 = []
|
||||||
while not tmp11 and retry < 5:
|
while not tmp11 and retry < 5:
|
||||||
sleep(0.5 * retry)
|
sleep(0.5 * retry)
|
||||||
retry += 1
|
retry += 1
|
||||||
|
|
|
@ -35,10 +35,10 @@ class Options:
|
||||||
# Fun fact: In Touchstone 1.1 spec all params are optional unordered.
|
# Fun fact: In Touchstone 1.1 spec all params are optional unordered.
|
||||||
# Just the line has to start with "#"
|
# Just the line has to start with "#"
|
||||||
UNIT_TO_FACTOR = {
|
UNIT_TO_FACTOR = {
|
||||||
"ghz": 10**9,
|
"ghz": 10 ** 9,
|
||||||
"mhz": 10**6,
|
"mhz": 10 ** 6,
|
||||||
"khz": 10**3,
|
"khz": 10 ** 3,
|
||||||
"hz": 10**0,
|
"hz": 10 ** 0,
|
||||||
}
|
}
|
||||||
VALID_UNITS = UNIT_TO_FACTOR.keys()
|
VALID_UNITS = UNIT_TO_FACTOR.keys()
|
||||||
VALID_PARAMETERS = "syzgh"
|
VALID_PARAMETERS = "syzgh"
|
||||||
|
@ -98,7 +98,7 @@ class Options:
|
||||||
class Touchstone:
|
class Touchstone:
|
||||||
FIELD_ORDER = ("11", "21", "12", "22")
|
FIELD_ORDER = ("11", "21", "12", "22")
|
||||||
|
|
||||||
def __init__(self, filename: str=""):
|
def __init__(self, filename: str = ""):
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.sdata = [[], [], [], []] # at max 4 data pairs
|
self.sdata = [[], [], [], []] # at max 4 data pairs
|
||||||
self.comments = []
|
self.comments = []
|
||||||
|
@ -244,7 +244,6 @@ class Touchstone:
|
||||||
if data_len % 2 != 0:
|
if data_len % 2 != 0:
|
||||||
raise TypeError("Data values aren't pairs: " + line)
|
raise TypeError("Data values aren't pairs: " + line)
|
||||||
|
|
||||||
|
|
||||||
# consistency checks
|
# consistency checks
|
||||||
if freq <= prev_freq:
|
if freq <= prev_freq:
|
||||||
logger.warning("Frequency not ascending: %s", line)
|
logger.warning("Frequency not ascending: %s", line)
|
||||||
|
|
|
@ -187,7 +187,7 @@ class CalibrationWindow(QtWidgets.QWidget):
|
||||||
self.load_inductance.setMinimumHeight(20)
|
self.load_inductance.setMinimumHeight(20)
|
||||||
self.load_capacitance = QtWidgets.QLineEdit("0")
|
self.load_capacitance = QtWidgets.QLineEdit("0")
|
||||||
self.load_capacitance.setMinimumHeight(20)
|
self.load_capacitance.setMinimumHeight(20)
|
||||||
#self.load_capacitance.setDisabled(True) # Not yet implemented
|
# self.load_capacitance.setDisabled(True) # Not yet implemented
|
||||||
self.load_length = QtWidgets.QLineEdit("0")
|
self.load_length = QtWidgets.QLineEdit("0")
|
||||||
self.load_length.setMinimumHeight(20)
|
self.load_length.setMinimumHeight(20)
|
||||||
cal_load_form.addRow("Resistance (\N{OHM SIGN})", self.load_resistance)
|
cal_load_form.addRow("Resistance (\N{OHM SIGN})", self.load_resistance)
|
||||||
|
@ -493,15 +493,15 @@ class CalibrationWindow(QtWidgets.QWidget):
|
||||||
# We are using custom calibration standards
|
# We are using custom calibration standards
|
||||||
try:
|
try:
|
||||||
self.app.calibration.shortL0 = self.getFloatValue(
|
self.app.calibration.shortL0 = self.getFloatValue(
|
||||||
self.short_l0_input.text())/10**12
|
self.short_l0_input.text()) / 10 ** 12
|
||||||
self.app.calibration.shortL1 = self.getFloatValue(
|
self.app.calibration.shortL1 = self.getFloatValue(
|
||||||
self.short_l1_input.text())/10**24
|
self.short_l1_input.text()) / 10 ** 24
|
||||||
self.app.calibration.shortL2 = self.getFloatValue(
|
self.app.calibration.shortL2 = self.getFloatValue(
|
||||||
self.short_l2_input.text())/10**33
|
self.short_l2_input.text()) / 10 ** 33
|
||||||
self.app.calibration.shortL3 = self.getFloatValue(
|
self.app.calibration.shortL3 = self.getFloatValue(
|
||||||
self.short_l3_input.text())/10**42
|
self.short_l3_input.text()) / 10 ** 42
|
||||||
self.app.calibration.shortLength = self.getFloatValue(
|
self.app.calibration.shortLength = self.getFloatValue(
|
||||||
self.short_length.text())/10**12
|
self.short_length.text()) / 10 ** 12
|
||||||
self.app.calibration.useIdealShort = False
|
self.app.calibration.useIdealShort = False
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.app.calibration.useIdealShort = True
|
self.app.calibration.useIdealShort = True
|
||||||
|
@ -510,15 +510,15 @@ class CalibrationWindow(QtWidgets.QWidget):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.app.calibration.openC0 = self.getFloatValue(
|
self.app.calibration.openC0 = self.getFloatValue(
|
||||||
self.open_c0_input.text())/10**15
|
self.open_c0_input.text()) / 10 ** 15
|
||||||
self.app.calibration.openC1 = self.getFloatValue(
|
self.app.calibration.openC1 = self.getFloatValue(
|
||||||
self.open_c1_input.text())/10**27
|
self.open_c1_input.text()) / 10 ** 27
|
||||||
self.app.calibration.openC2 = self.getFloatValue(
|
self.app.calibration.openC2 = self.getFloatValue(
|
||||||
self.open_c2_input.text())/10**36
|
self.open_c2_input.text()) / 10 ** 36
|
||||||
self.app.calibration.openC3 = self.getFloatValue(
|
self.app.calibration.openC3 = self.getFloatValue(
|
||||||
self.open_c3_input.text())/10**45
|
self.open_c3_input.text()) / 10 ** 45
|
||||||
self.app.calibration.openLength = self.getFloatValue(
|
self.app.calibration.openLength = self.getFloatValue(
|
||||||
self.open_length.text())/10**12
|
self.open_length.text()) / 10 ** 12
|
||||||
self.app.calibration.useIdealOpen = False
|
self.app.calibration.useIdealOpen = False
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.app.calibration.useIdealOpen = True
|
self.app.calibration.useIdealOpen = True
|
||||||
|
@ -529,11 +529,11 @@ class CalibrationWindow(QtWidgets.QWidget):
|
||||||
self.app.calibration.loadR = self.getFloatValue(
|
self.app.calibration.loadR = self.getFloatValue(
|
||||||
self.load_resistance.text())
|
self.load_resistance.text())
|
||||||
self.app.calibration.loadL = self.getFloatValue(
|
self.app.calibration.loadL = self.getFloatValue(
|
||||||
self.load_inductance.text()) / 10**12
|
self.load_inductance.text()) / 10 ** 12
|
||||||
self.app.calibration.loadC = self.getFloatValue(
|
self.app.calibration.loadC = self.getFloatValue(
|
||||||
self.load_capacitance.text()) / 10 ** 15
|
self.load_capacitance.text()) / 10 ** 15
|
||||||
self.app.calibration.loadLength = self.getFloatValue(
|
self.app.calibration.loadLength = self.getFloatValue(
|
||||||
self.load_length.text())/10**12
|
self.load_length.text()) / 10 ** 12
|
||||||
self.app.calibration.useIdealLoad = False
|
self.app.calibration.useIdealLoad = False
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.app.calibration.useIdealLoad = True
|
self.app.calibration.useIdealLoad = True
|
||||||
|
@ -542,7 +542,7 @@ class CalibrationWindow(QtWidgets.QWidget):
|
||||||
' Using ideal values.')
|
' Using ideal values.')
|
||||||
try:
|
try:
|
||||||
self.app.calibration.throughLength = self.getFloatValue(
|
self.app.calibration.throughLength = self.getFloatValue(
|
||||||
self.through_length.text())/10**12
|
self.through_length.text()) / 10 ** 12
|
||||||
self.app.calibration.useIdealThrough = False
|
self.app.calibration.useIdealThrough = False
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.app.calibration.useIdealThrough = True
|
self.app.calibration.useIdealThrough = True
|
||||||
|
|
|
@ -24,6 +24,7 @@ from NanoVNASaver.RFTools import Datapoint
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class FilesWindow(QtWidgets.QWidget):
|
class FilesWindow(QtWidgets.QWidget):
|
||||||
def __init__(self, app: QtWidgets.QWidget):
|
def __init__(self, app: QtWidgets.QWidget):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
|
@ -102,7 +102,7 @@ class SweepSettingsWindow(QtWidgets.QWidget):
|
||||||
" amount of datapoints and many segments. Step display in"
|
" amount of datapoints and many segments. Step display in"
|
||||||
" SweepControl cannot reflect this currently.")
|
" SweepControl cannot reflect this currently.")
|
||||||
label.setWordWrap(True)
|
label.setWordWrap(True)
|
||||||
label.setMinimumSize(600,70)
|
label.setMinimumSize(600, 70)
|
||||||
layout.addRow(label)
|
layout.addRow(label)
|
||||||
checkbox = QtWidgets.QCheckBox("Logarithmic sweep")
|
checkbox = QtWidgets.QCheckBox("Logarithmic sweep")
|
||||||
checkbox.setMinimumHeight(20)
|
checkbox.setMinimumHeight(20)
|
||||||
|
|
|
@ -101,5 +101,6 @@ def main():
|
||||||
logger.exception("%s", exc)
|
logger.exception("%s", exc)
|
||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
|
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
with suppress(ImportError):
|
with suppress(ImportError):
|
||||||
# pylint: disable=no-name-in-module,import-error,unused-import
|
# pylint: disable=no-name-in-module,import-error,unused-import
|
||||||
# pyright: reportMissingImports=false
|
# pyright: reportMissingImports=false
|
||||||
|
|
|
@ -24,6 +24,7 @@ import unittest
|
||||||
# Import targets to be tested
|
# Import targets to be tested
|
||||||
from NanoVNASaver.Formatting import format_frequency_sweep
|
from NanoVNASaver.Formatting import format_frequency_sweep
|
||||||
|
|
||||||
|
|
||||||
class TestCases(unittest.TestCase):
|
class TestCases(unittest.TestCase):
|
||||||
|
|
||||||
def test_basicIntegerValues(self):
|
def test_basicIntegerValues(self):
|
||||||
|
@ -47,7 +48,7 @@ class TestCases(unittest.TestCase):
|
||||||
# self.assertEqual(rft.formatSweepFrequency(10000), '10.00kHz')
|
# self.assertEqual(rft.formatSweepFrequency(10000), '10.00kHz')
|
||||||
# self.assertEqual(rft.formatSweepFrequency(100000), '100.00kHz')
|
# self.assertEqual(rft.formatSweepFrequency(100000), '100.00kHz')
|
||||||
# self.assertEqual(rft.formatSweepFrequency(1000000), '1.00MHz')
|
# self.assertEqual(rft.formatSweepFrequency(1000000), '1.00MHz')
|
||||||
|
|
||||||
# def test_nonDefaultMinDigits(self):
|
# def test_nonDefaultMinDigits(self):
|
||||||
# # simple integers with trailing zeros. setting mindigit value to something
|
# # simple integers with trailing zeros. setting mindigit value to something
|
||||||
# # other than default, where trailing zeros >= mindigits, the number of
|
# # other than default, where trailing zeros >= mindigits, the number of
|
||||||
|
@ -69,4 +70,3 @@ class TestCases(unittest.TestCase):
|
||||||
# # TODO: Consider post-processing result for maxdigits based on SI unit.
|
# # TODO: Consider post-processing result for maxdigits based on SI unit.
|
||||||
# self.assertEqual(rft.formatSweepFrequency(1000, mindigits=5), '1.00000kHz')
|
# self.assertEqual(rft.formatSweepFrequency(1000, mindigits=5), '1.00000kHz')
|
||||||
# self.assertEqual(rft.formatSweepFrequency(1000, mindigits=10), '1.0000000000kHz')
|
# self.assertEqual(rft.formatSweepFrequency(1000, mindigits=10), '1.0000000000kHz')
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import unittest
|
||||||
# Import targets to be tested
|
# Import targets to be tested
|
||||||
from NanoVNASaver.Formatting import parse_frequency
|
from NanoVNASaver.Formatting import parse_frequency
|
||||||
|
|
||||||
|
|
||||||
# TODO: should be tested against SITools.Value
|
# TODO: should be tested against SITools.Value
|
||||||
# RFTools.parseFrequency will hopefully go away in future
|
# RFTools.parseFrequency will hopefully go away in future
|
||||||
# and be specialised by input field and device, like
|
# and be specialised by input field and device, like
|
||||||
|
@ -149,4 +150,3 @@ class TestCases(unittest.TestCase):
|
||||||
self.assertEqual(parse_frequency('123...Hz'), -1)
|
self.assertEqual(parse_frequency('123...Hz'), -1)
|
||||||
self.assertEqual(parse_frequency('123....Hz'), -1)
|
self.assertEqual(parse_frequency('123....Hz'), -1)
|
||||||
self.assertEqual(parse_frequency('1.23.Hz'), -1)
|
self.assertEqual(parse_frequency('1.23.Hz'), -1)
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ class TestRFTools(unittest.TestCase):
|
||||||
self.assertEqual(clamp_value(1, -10, -1), -1)
|
self.assertEqual(clamp_value(1, -10, -1), -1)
|
||||||
|
|
||||||
def test_parallel_to_serial(self):
|
def test_parallel_to_serial(self):
|
||||||
self.assertRaises(ZeroDivisionError, parallel_to_serial, 0)
|
self.assertEqual(parallel_to_serial(0), complex(0, 0))
|
||||||
self.assertAlmostEqual(
|
self.assertAlmostEqual(
|
||||||
parallel_to_serial(complex(52, 260)),
|
parallel_to_serial(complex(52, 260)),
|
||||||
complex(50, 10))
|
complex(50, 10))
|
||||||
|
|
|
@ -29,7 +29,7 @@ class TConfig:
|
||||||
my_str: str = "Hello World"
|
my_str: str = "Hello World"
|
||||||
my_bool: bool = True
|
my_bool: bool = True
|
||||||
my_list: list = field(default_factory=lambda: [1, 2, 3])
|
my_list: list = field(default_factory=lambda: [1, 2, 3])
|
||||||
my_bytearray: bytearray = field(default_factory=lambda: bytearray((1,2,3)))
|
my_bytearray: bytearray = field(default_factory=lambda: bytearray((1, 2, 3)))
|
||||||
|
|
||||||
|
|
||||||
class TestCases(unittest.TestCase):
|
class TestCases(unittest.TestCase):
|
||||||
|
@ -37,7 +37,7 @@ 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.IniFormat,
|
||||||
CFG.QSettings.UserScope,
|
CFG.QSettings.UserScope,
|
||||||
"NanoVNASaver", "Test_1")
|
"NanoVNASaver", "Test_1")
|
||||||
self.settings_2 = CFG.AppSettings(
|
self.settings_2 = CFG.AppSettings(
|
||||||
CFG.QSettings.IniFormat,
|
CFG.QSettings.IniFormat,
|
||||||
|
@ -57,7 +57,7 @@ class TestCases(unittest.TestCase):
|
||||||
illegal_config = TConfig(
|
illegal_config = TConfig(
|
||||||
my_int=4, my_float=3.0, my_str="Goodbye World",
|
my_int=4, my_float=3.0, my_str="Goodbye World",
|
||||||
my_bool="False", my_list=(4, 5, 6))
|
my_bool="False", my_list=(4, 5, 6))
|
||||||
with self.assertRaises(AssertionError):
|
with self.assertRaises(TypeError):
|
||||||
self.settings_1.store_dataclass("SectionX", illegal_config)
|
self.settings_1.store_dataclass("SectionX", illegal_config)
|
||||||
|
|
||||||
def test_restore_dataclass(self):
|
def test_restore_dataclass(self):
|
||||||
|
@ -85,4 +85,4 @@ class TestCases(unittest.TestCase):
|
||||||
tc_2 = CFG.restore(self.settings_2)
|
tc_2 = CFG.restore(self.settings_2)
|
||||||
print(f"\n{tc_1}\n{tc_2}\n")
|
print(f"\n{tc_1}\n{tc_2}\n")
|
||||||
self.assertEqual(tc_1, tc_2)
|
self.assertEqual(tc_1, tc_2)
|
||||||
self.assertNotEqual(tc_2.gui, CFG.GUI())
|
self.assertNotEqual(tc_2.gui, CFG.GUI())
|
||||||
|
|
|
@ -21,7 +21,7 @@ from math import inf
|
||||||
from decimal import Decimal # Needed for test_representation()
|
from decimal import Decimal # Needed for test_representation()
|
||||||
|
|
||||||
# Import targets to be tested
|
# Import targets to be tested
|
||||||
from NanoVNASaver.SITools import Format, Value
|
from NanoVNASaver.SITools import Format, Value, round_floor, round_ceil
|
||||||
|
|
||||||
F_DEFAULT = Format()
|
F_DEFAULT = Format()
|
||||||
|
|
||||||
|
@ -145,7 +145,6 @@ class TestTSIToolsValue(unittest.TestCase):
|
||||||
self.assertEqual(v.parse("\N{INFINITY}").value, inf)
|
self.assertEqual(v.parse("\N{INFINITY}").value, inf)
|
||||||
self.assertEqual(v.parse("-\N{INFINITY}").value, -inf)
|
self.assertEqual(v.parse("-\N{INFINITY}").value, -inf)
|
||||||
|
|
||||||
|
|
||||||
def test_format_attributes(self):
|
def test_format_attributes(self):
|
||||||
v = Value("10.0", "Hz", fmt=F_DIGITS_4)
|
v = Value("10.0", "Hz", fmt=F_DIGITS_4)
|
||||||
self.assertEqual(v.value, 10.0)
|
self.assertEqual(v.value, 10.0)
|
||||||
|
@ -156,7 +155,13 @@ class TestTSIToolsValue(unittest.TestCase):
|
||||||
v.parse("12 GHz")
|
v.parse("12 GHz")
|
||||||
self.assertEqual(v.unit, "Hz")
|
self.assertEqual(v.unit, "Hz")
|
||||||
|
|
||||||
|
def test_rounding(self):
|
||||||
|
self.assertEqual(round_floor(123.456), 123)
|
||||||
|
self.assertEqual(round_floor(123.456, 1), 123.4)
|
||||||
|
self.assertEqual(round_floor(123.456, -1), 120)
|
||||||
|
self.assertEqual(round_ceil(123.456), 124)
|
||||||
|
self.assertEqual(round_ceil(123.456, 1), 123.5)
|
||||||
|
self.assertEqual(round_ceil(123.456, -1), 130)
|
||||||
|
|
||||||
# TODO: test F_DIGITS_31
|
# TODO: test F_DIGITS_31
|
||||||
# F_WITH_SPACE
|
# F_WITH_SPACE
|
||||||
|
|
|
@ -21,6 +21,7 @@ import unittest
|
||||||
# Import targets to be tested
|
# Import targets to be tested
|
||||||
from NanoVNASaver.Settings.Sweep import Sweep, Properties
|
from NanoVNASaver.Settings.Sweep import Sweep, Properties
|
||||||
|
|
||||||
|
|
||||||
class TestCases(unittest.TestCase):
|
class TestCases(unittest.TestCase):
|
||||||
|
|
||||||
def test_sweep(self):
|
def test_sweep(self):
|
||||||
|
|
|
@ -24,6 +24,7 @@ import os
|
||||||
from NanoVNASaver.Touchstone import Options, Touchstone
|
from NanoVNASaver.Touchstone import Options, Touchstone
|
||||||
from NanoVNASaver.RFTools import Datapoint
|
from NanoVNASaver.RFTools import Datapoint
|
||||||
|
|
||||||
|
|
||||||
class TestTouchstoneOptions(unittest.TestCase):
|
class TestTouchstoneOptions(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.opts = Options()
|
self.opts = Options()
|
||||||
|
@ -113,7 +114,7 @@ class TestTouchstoneTouchstone(unittest.TestCase):
|
||||||
s11, s21, s12, s22 = ts.sdata
|
s11, s21, s12, s22 = ts.sdata
|
||||||
ts.swap()
|
ts.swap()
|
||||||
s11_, s21_, s12_, s22_ = ts.sdata
|
s11_, s21_, s12_, s22_ = ts.sdata
|
||||||
self.assertEqual([s11_, s21_, s12_, s22_] ,[s22, s12, s21, s11])
|
self.assertEqual([s11_, s21_, s12_, s22_], [s22, s12, s21, s11])
|
||||||
|
|
||||||
def test_db_conversation(self):
|
def test_db_conversation(self):
|
||||||
ts_db = Touchstone("./test/data/attenuator-0643_DB.s2p")
|
ts_db = Touchstone("./test/data/attenuator-0643_DB.s2p")
|
||||||
|
@ -143,7 +144,7 @@ class TestTouchstoneTouchstone(unittest.TestCase):
|
||||||
' 15000000.0 0.849810063 -0.4147357 -0.000306106 0.0041482'
|
' 15000000.0 0.849810063 -0.4147357 -0.000306106 0.0041482'
|
||||||
' 0.0 0.0 0.0 0.0',
|
' 0.0 0.0 0.0 0.0',
|
||||||
'WARNING:NanoVNASaver.Touchstone:Reordering data',
|
'WARNING:NanoVNASaver.Touchstone:Reordering data',
|
||||||
])
|
])
|
||||||
self.assertEqual(str(ts.opts), "# HZ S RI R 50")
|
self.assertEqual(str(ts.opts), "# HZ S RI R 50")
|
||||||
self.assertEqual(len(ts.s11), 101)
|
self.assertEqual(len(ts.s11), 101)
|
||||||
self.assertIn("!freq ReS11 ImS11 ReS21 ImS21 ReS12 ImS12 ReS22 ImS22",
|
self.assertIn("!freq ReS11 ImS11 ReS21 ImS21 ReS12 ImS12 ReS22 ImS22",
|
||||||
|
@ -165,7 +166,6 @@ class TestTouchstoneTouchstone(unittest.TestCase):
|
||||||
ts.gen_interpolation()
|
ts.gen_interpolation()
|
||||||
self.assertEqual(ts.s_freq("11", 2), Datapoint(2, 0.5, 0.5))
|
self.assertEqual(ts.s_freq("11", 2), Datapoint(2, 0.5, 0.5))
|
||||||
|
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
ts = Touchstone("./test/data/valid.s2p")
|
ts = Touchstone("./test/data/valid.s2p")
|
||||||
self.assertEqual(ts.saves(), "# HZ S RI R 50\n")
|
self.assertEqual(ts.saves(), "# HZ S RI R 50\n")
|
||||||
|
|
|
@ -21,6 +21,7 @@ import unittest
|
||||||
# Import targets to be tested
|
# Import targets to be tested
|
||||||
from NanoVNASaver.Version import Version
|
from NanoVNASaver.Version import Version
|
||||||
|
|
||||||
|
|
||||||
class TestCases(unittest.TestCase):
|
class TestCases(unittest.TestCase):
|
||||||
|
|
||||||
def test_version(self):
|
def test_version(self):
|
||||||
|
@ -30,9 +31,11 @@ class TestCases(unittest.TestCase):
|
||||||
self.assertFalse(ver > Version("1.2.4"))
|
self.assertFalse(ver > Version("1.2.4"))
|
||||||
self.assertFalse(ver > Version("1.2.3-u"))
|
self.assertFalse(ver > Version("1.2.3-u"))
|
||||||
self.assertTrue(Version("1.2.4") >= ver)
|
self.assertTrue(Version("1.2.4") >= ver)
|
||||||
|
self.assertTrue(ver < Version("1.2.4"))
|
||||||
self.assertFalse(Version("0.0.0") == Version("0.0.0-rc"))
|
self.assertFalse(Version("0.0.0") == Version("0.0.0-rc"))
|
||||||
self.assertEqual(ver.major, 1)
|
self.assertEqual(ver.major, 1)
|
||||||
self.assertEqual(ver.minor, 2)
|
self.assertEqual(ver.minor, 2)
|
||||||
self.assertEqual(ver.revision, 3)
|
self.assertEqual(ver.revision, 3)
|
||||||
self.assertEqual(ver.note, '-test')
|
self.assertEqual(ver.note, '-test')
|
||||||
Version("asdasd")
|
Version("asdasd")
|
||||||
|
Version("1.2.invalid")
|
||||||
|
|
Ładowanie…
Reference in New Issue