From 4496849b7129ee39bcd8a7ddb116fd9a985f354e Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 12 Nov 2019 14:49:43 +0100 Subject: [PATCH 01/84] ignore s1p files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 07a39f92..9fb65b88 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,7 @@ *.egg-info/ *.pyc *.cal +*.s1p settings.json +.vscode .gitignore From 64651b0b559533a509f3f74717a6ad14590dcbad Mon Sep 17 00:00:00 2001 From: Rune Broberg Date: Tue, 12 Nov 2019 15:09:15 +0100 Subject: [PATCH 02/84] Allow LogMag charts to automatically set scaling within -100 to +100 dB --- NanoVNASaver/Chart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index ca8f813b..b1d0dd5a 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -1474,7 +1474,7 @@ def drawValues(self, qp: QtGui.QPainter): else: # Find scaling minValue = 100 - maxValue = 0 + maxValue = -100 for d in self.data: logmag = self.logMag(d) if logmag > maxValue: From 3f3862e7e9cd3be4a3be514e41ad489cec104514 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 12 Nov 2019 15:54:57 +0100 Subject: [PATCH 03/84] sloppy SI parsing to allow common human errors --- NanoVNASaver/RFTools.py | 2 +- NanoVNASaver/SITools.py | 39 ++++++++++++++++++++++++++------------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index c05ae3f2..2f71e65c 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -73,7 +73,7 @@ def capacitanceEquivalent(im50, freq) -> str: return "- pF" capacitance = 1 / (freq * 2 * math.pi * im50) return str(Value(-capacitance, "F", Format(max_nr_digits=5, space_str=" "))) - + @staticmethod def inductanceEquivalent(im50, freq) -> str: if freq == 0: diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py index 2d3f23b2..c768c1f8 100644 --- a/NanoVNASaver/SITools.py +++ b/NanoVNASaver/SITools.py @@ -28,7 +28,9 @@ def __init__(self, space_str: str = "", assume_infinity: bool = True, min_offset: int = -8, - max_offset: int = 8): + max_offset: int = 8, + parse_sloppy_unit: bool = False, + parse_sloppy_kilo: bool = False): assert(min_offset >= -8 and max_offset <= 8 and min_offset < max_offset) self.max_nr_digits = max_nr_digits self.fix_decimals = fix_decimals @@ -36,12 +38,15 @@ def __init__(self, self.assume_infinity = assume_infinity self.min_offset = min_offset self.max_offset = max_offset + self.parse_sloppy_unit = parse_sloppy_unit + self.parse_sloppy_kilo = parse_sloppy_kilo - def __repr__(self): + def __repr__(self) -> str: return (f"{self.__class__.__name__}(" f"{self.max_nr_digits}, {self.fix_decimals}, " f"'{self.space_str}', {self.assume_infinity}, " - f"{self.min_offset}, {self.max_offset})") + f"{self.min_offset}, {self.max_offset}, " + f"{self.parse_sloppy_unit}, {self.parse_sloppy_kilo})") class Value(object): @@ -50,10 +55,10 @@ def __init__(self, value: Number = 0, unit: str = "", fmt=Format()): self._unit = unit self.fmt = fmt - def __repr__(self): + def __repr__(self) -> str: return f"{self.__class__.__name__}({self.value}, '{self._unit}', {self.fmt})" - def __str__(self): + def __str__(self) -> str: fmt = self.fmt if fmt.assume_infinity and abs(self.value) >= 10 ** ((fmt.max_offset + 1) * 3): return ("-" if self.value < 0 else "") + "\N{INFINITY}" + fmt.space_str + self._unit @@ -85,22 +90,30 @@ def __str__(self): return result + fmt.space_str + PREFIXES[offset + 8] + self._unit - def parse(self, value: str): + def parse(self, value: str) -> float: value = value.replace(" ", "") # Ignore spaces - if self._unit and value.endswith(self._unit) or value.lower().endswith(self._unit.lower()): # strip unit + + if self._unit and ( + value.endswith(self._unit) or + (self.fmt.parse_sloppy_unit and + value.lower().endswith(self._unit.lower()))): # strip unit value = value[:-len(self._unit)] factor = 1 + if self.fmt.parse_sloppy_kilo and value[-1] == "K": # fix for e.g. KHz + value = value[:-1] + "k" if value[-1] in PREFIXES: factor = 10 ** ((PREFIXES.index(value[-1]) - 8) * 3) value = value[:-1] - elif value[-1] == 'K': - # Fix for the very common KHz - factor = 10 ** ((PREFIXES.index(value[-1].lower()) - 8) * 3) - value = value[:-1] - self.value = float(value) * factor + + if self.fmt.assume_infinity and value == "\N{INFINITY}": + self.value = math.inf + elif self.fmt.assume_infinity and value == "-\N{INFINITY}": + self.value = -math.inf + else: + self.value = float(value) * factor return self.value @property - def unit(self): + def unit(self) -> str: return self._unit From 8abca5978620a2ef1cefcd4040880f250dbcf3b9 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 12 Nov 2019 19:55:54 +0100 Subject: [PATCH 04/84] Code cleanup / linting --- NanoVNASaver/SITools.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py index c768c1f8..8cbf6abd 100644 --- a/NanoVNASaver/SITools.py +++ b/NanoVNASaver/SITools.py @@ -20,8 +20,7 @@ PREFIXES = ("y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y") - -class Format(object): +class Format(): def __init__(self, max_nr_digits: int = 6, fix_decimals: bool = False, @@ -31,7 +30,7 @@ def __init__(self, max_offset: int = 8, parse_sloppy_unit: bool = False, parse_sloppy_kilo: bool = False): - assert(min_offset >= -8 and max_offset <= 8 and min_offset < max_offset) + assert -8 <= min_offset < max_offset <= 8 self.max_nr_digits = max_nr_digits self.fix_decimals = fix_decimals self.space_str = space_str @@ -49,7 +48,7 @@ def __repr__(self) -> str: f"{self.parse_sloppy_unit}, {self.parse_sloppy_kilo})") -class Value(object): +class Value(): def __init__(self, value: Number = 0, unit: str = "", fmt=Format()): self.value = value self._unit = unit @@ -94,9 +93,9 @@ def parse(self, value: str) -> float: value = value.replace(" ", "") # Ignore spaces if self._unit and ( - value.endswith(self._unit) or - (self.fmt.parse_sloppy_unit and - value.lower().endswith(self._unit.lower()))): # strip unit + value.endswith(self._unit) or + (self.fmt.parse_sloppy_unit and + value.lower().endswith(self._unit.lower()))): # strip unit value = value[:-len(self._unit)] factor = 1 From 57d73fac94a3bc39f4e7c5b74949aef935b377f8 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 12 Nov 2019 20:20:44 +0100 Subject: [PATCH 05/84] RFTools code cleanup linting removed code doublings by adjusting index in group delay method --- NanoVNASaver/RFTools.py | 77 +++++++++++------------------------------ 1 file changed, 20 insertions(+), 57 deletions(-) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index 2f71e65c..b1b01a35 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -15,6 +15,7 @@ # along with this program. If not, see . import collections import math +from numbers import Number from typing import List from NanoVNASaver.SITools import Value, Format @@ -33,7 +34,7 @@ def normalize50(data: Datapoint): return re50, im50 @staticmethod - def gain(data: Datapoint): + def gain(data: Datapoint) -> float: # re50, im50 = normalize50(data) # Calculate the gain / reflection coefficient # mag = math.sqrt((re50 - 50) * (re50 - 50) + im50 * im50) / \ @@ -43,8 +44,7 @@ def gain(data: Datapoint): mag = math.sqrt(data.re**2 + data.im**2) if mag > 0: return 20 * math.log10(mag) - else: - return 0 + return 0 @staticmethod def qualityFactor(data: Datapoint): @@ -82,85 +82,48 @@ def inductanceEquivalent(im50, freq) -> str: return str(Value(inductance, "H", Format(max_nr_digits=5, space_str=" "))) @staticmethod - def formatFrequency(freq): - return str(Value(freq, "Hz", Format(max_nr_digits=6))) + def formatFrequency(freq: Number) -> str: + return str(Value(freq, "Hz")) @staticmethod - def formatShortFrequency(freq): + def formatShortFrequency(freq: Number) -> str: return str(Value(freq, "Hz", Format(max_nr_digits=4))) @staticmethod - def formatSweepFrequency(freq: int, - mindigits: int = 2, - appendHz: bool = True, - insertSpace: bool = False, - countDot: bool = True, - assumeInfinity: bool = True) -> str: - """ Format frequency with SI prefixes - - mindigits count refers to the number of decimal place digits - that will be shown, padded with zeroes if needed. - """ - freqstr = str(freq) - freqlen = len(freqstr) - - # sanity checks - if freqlen > 15: - if assumeInfinity: - return "\N{INFINITY}" - raise ValueError("Frequency too big. More than 15 digits!") - - if freq < 1: - return " - " + (" " if insertSpace else "") + ("Hz" if appendHz else "") - - si_index = (freqlen - 1) // 3 - dot_pos = freqlen % 3 or 3 - intfstr = freqstr[:dot_pos] - decfstr = freqstr[dot_pos:] - nzdecfstr = decfstr.rstrip('0') - if si_index != 0: - while len(nzdecfstr) < mindigits: - nzdecfstr += '0' - freqstr = intfstr + ("." if len(nzdecfstr) > 0 else "") + nzdecfstr - return freqstr + (" " if insertSpace else "") + PREFIXES[si_index] + ("Hz" if appendHz else "") + def formatSweepFrequency(freq: Number) -> str: + return str(Value(freq, "Hz", Format(max_nr_digits=5))) @staticmethod def parseFrequency(freq: str) -> int: - parser = Value(0, "Hz") + parser = Value(0, "Hz", Format(parse_sloppy_unit=True, parse_sloppy_kilo=True)) try: return round(parser.parse(freq)) except (ValueError, IndexError): return -1 @staticmethod - def phaseAngle(data: Datapoint): + def phaseAngle(data: Datapoint) -> float: re = data.re im = data.im return math.degrees(math.atan2(im, re)) @staticmethod - def phaseAngleRadians(data: Datapoint): + def phaseAngleRadians(data: Datapoint) -> float: re = data.re im = data.im return math.atan2(im, re) @staticmethod def groupDelay(data: List[Datapoint], index: int) -> float: - if index == 0: - angle0 = RFTools.phaseAngleRadians(data[0]) - angle1 = RFTools.phaseAngleRadians(data[1]) - freq0 = data[0].freq - freq1 = data[1].freq - elif index == len(data) - 1: - angle0 = RFTools.phaseAngleRadians(data[-2]) - angle1 = RFTools.phaseAngleRadians(data[-1]) - freq0 = data[-2].freq - freq1 = data[-1].freq - else: - angle0 = RFTools.phaseAngleRadians(data[index-1]) - angle1 = RFTools.phaseAngleRadians(data[index+1]) - freq0 = data[index-1].freq - freq1 = data[index+1].freq + if index <= 0: # fix list boundaries + index = 1 + elif index >= len(data) - 1: + index = len(data -2) + + angle0 = RFTools.phaseAngleRadians(data[index-1]) + angle1 = RFTools.phaseAngleRadians(data[index+1]) + freq0 = data[index-1].freq + freq1 = data[index+1].freq delta_angle = (angle1 - angle0) if abs(delta_angle) > math.tau: if delta_angle > 0: From 6e97270ca25889529df4cf4e15d9769674094135 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 12 Nov 2019 21:29:32 +0100 Subject: [PATCH 06/84] Fixed bug in group delay Group delay was misscalculated at upper boundary. Now using a clamp function to make method even more robust against index errors --- NanoVNASaver/RFTools.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index b1b01a35..58318dfa 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -113,17 +113,23 @@ def phaseAngleRadians(data: Datapoint) -> float: im = data.im return math.atan2(im, re) + @staticmethod + def clamp_int(value: int, min: int, max: int) -> int: + assert min <= max + if value < min: + return min + if value > max: + return max + return value + @staticmethod def groupDelay(data: List[Datapoint], index: int) -> float: - if index <= 0: # fix list boundaries - index = 1 - elif index >= len(data) - 1: - index = len(data -2) - - angle0 = RFTools.phaseAngleRadians(data[index-1]) - angle1 = RFTools.phaseAngleRadians(data[index+1]) - freq0 = data[index-1].freq - freq1 = data[index+1].freq + index0 = clamp_int(index - 1, 0, len(data) - 1) + index1 = clamp_int(index + 1, 0, len(data) - 1) + angle0 = RFTools.phaseAngleRadians(data[index0]) + angle1 = RFTools.phaseAngleRadians(data[index1]) + freq0 = data[index0].freq + freq1 = data[index1].freq delta_angle = (angle1 - angle0) if abs(delta_angle) > math.tau: if delta_angle > 0: From 9fc0f9fc2b0cf41327877d7c5a529d3e9bae1aed Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 12 Nov 2019 22:03:56 +0100 Subject: [PATCH 07/84] Fixed: add classname to staticmethod --- NanoVNASaver/RFTools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index 58318dfa..c217936e 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -124,8 +124,8 @@ def clamp_int(value: int, min: int, max: int) -> int: @staticmethod def groupDelay(data: List[Datapoint], index: int) -> float: - index0 = clamp_int(index - 1, 0, len(data) - 1) - index1 = clamp_int(index + 1, 0, len(data) - 1) + index0 = RFTools.clamp_int(index - 1, 0, len(data) - 1) + index1 = RFTools.clamp_int(index + 1, 0, len(data) - 1) angle0 = RFTools.phaseAngleRadians(data[index0]) angle1 = RFTools.phaseAngleRadians(data[index1]) freq0 = data[index0].freq From e32838d8b8142882776e32808eb02f9844fe6355 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 12 Nov 2019 23:15:33 +0100 Subject: [PATCH 08/84] Use NamedTuple for SI Format --- NanoVNASaver/SITools.py | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py index 8cbf6abd..45d5e518 100644 --- a/NanoVNASaver/SITools.py +++ b/NanoVNASaver/SITools.py @@ -15,41 +15,27 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import math +from typing import NamedTuple from numbers import Number PREFIXES = ("y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y") -class Format(): - def __init__(self, - max_nr_digits: int = 6, - fix_decimals: bool = False, - space_str: str = "", - assume_infinity: bool = True, - min_offset: int = -8, - max_offset: int = 8, - parse_sloppy_unit: bool = False, - parse_sloppy_kilo: bool = False): - assert -8 <= min_offset < max_offset <= 8 - self.max_nr_digits = max_nr_digits - self.fix_decimals = fix_decimals - self.space_str = space_str - self.assume_infinity = assume_infinity - self.min_offset = min_offset - self.max_offset = max_offset - self.parse_sloppy_unit = parse_sloppy_unit - self.parse_sloppy_kilo = parse_sloppy_kilo - - def __repr__(self) -> str: - return (f"{self.__class__.__name__}(" - f"{self.max_nr_digits}, {self.fix_decimals}, " - f"'{self.space_str}', {self.assume_infinity}, " - f"{self.min_offset}, {self.max_offset}, " - f"{self.parse_sloppy_unit}, {self.parse_sloppy_kilo})") +class Format(NamedTuple): + max_nr_digits: int = 6 + fix_decimals: bool = False + space_str: str = "" + assume_infinity: bool = True + min_offset: int = -8 + max_offset: int = 8 + parse_sloppy_unit: bool = False + parse_sloppy_kilo: bool = False class Value(): def __init__(self, value: Number = 0, unit: str = "", fmt=Format()): + assert 3 <= fmt.max_nr_digits <= 27 + assert -8 <= fmt.min_offset <= fmt.max_offset <= 8 self.value = value self._unit = unit self.fmt = fmt From 0b73e89d51b8e31841b099997607e20ee1ca0afa Mon Sep 17 00:00:00 2001 From: "Rune B. Broberg" Date: Wed, 13 Nov 2019 07:44:18 +0100 Subject: [PATCH 09/84] Serial port select box from Paul Alfille --- NanoVNASaver/NanoVNASaver.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/NanoVNASaver/NanoVNASaver.py b/NanoVNASaver/NanoVNASaver.py index b24044ae..54a31b13 100644 --- a/NanoVNASaver/NanoVNASaver.py +++ b/NanoVNASaver/NanoVNASaver.py @@ -433,8 +433,9 @@ def __init__(self): serial_control_box.setMaximumWidth(250) serial_control_box.setTitle("Serial port control") serial_control_layout = QtWidgets.QFormLayout(serial_control_box) - self.serialPortInput = QtWidgets.QLineEdit(self.serialPort) - self.serialPortInput.setAlignment(QtCore.Qt.AlignRight) + self.serialPortInput = QtWidgets.QComboBox() + self.rescanSerialPort() + self.serialPortInput.setEditable(True) btn_rescan_serial_port = QtWidgets.QPushButton("Rescan") btn_rescan_serial_port.setFixedWidth(60) btn_rescan_serial_port.clicked.connect(self.rescanSerialPort) @@ -525,21 +526,22 @@ def __init__(self): logger.debug("Finished building interface") def rescanSerialPort(self): - serial_port = self.getPort() - self.serialPort = serial_port - self.serialPortInput.setText(serial_port) + self.serialPortInput.clear() + for port in self.getPort(): + self.serialPortInput.insertItem(1,port) # Get that windows port @staticmethod - def getPort() -> str: + def getPort() -> list: + return_ports = [] device_list = list_ports.comports() for d in device_list: if (d.vid == VID and d.pid == PID): port = d.device logger.info("Found NanoVNA (%04x %04x) on port %s", d.vid, d.pid, d.device) - return port - return "" + return_ports.append(port) + return return_ports def exportFileS1P(self): if len(self.data) == 0: From 43cd92bdcfc69984b2d0337319265d51d3d4f7d5 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Wed, 13 Nov 2019 18:55:06 +0100 Subject: [PATCH 10/84] Use python native complex numbers in RFTools --- NanoVNASaver/RFTools.py | 67 ++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index c217936e..c470e314 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -13,35 +13,41 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import collections import math +import cmath from numbers import Number -from typing import List +from typing import List, NamedTuple from NanoVNASaver.SITools import Value, Format -PREFIXES = ("", "k", "M", "G", "T") -Datapoint = collections.namedtuple('Datapoint', 'freq re im') +def clamp_int(value: int, imin: int, imax: int) -> int: + assert imin <= imax + if value < imin: + return imin + if value > imax: + return imax + return value +class Datapoint(NamedTuple): + freq: int + re: float + im: float + + @property + def z(self): + """ return datapoint impedance as complex number """ + return complex(self.re, self.im) + class RFTools: @staticmethod def normalize50(data: Datapoint): - re = data.re - im = data.im - re50 = 50 * (1 - re * re - im * im) / (1 + re * re + im * im - 2 * re) - im50 = 50 * (2 * im) / (1 + re * re + im * im - 2 * re) - return re50, im50 + result = 50 * ((-data.z - 1) / (data.z - 1)) + return result.real, result.imag @staticmethod def gain(data: Datapoint) -> float: - # re50, im50 = normalize50(data) - # Calculate the gain / reflection coefficient - # mag = math.sqrt((re50 - 50) * (re50 - 50) + im50 * im50) / \ - # math.sqrt((re50 + 50) * (re50 + 50) + im50 * im50) - # - # Magnitude = |Gamma|: - mag = math.sqrt(data.re**2 + data.im**2) + mag = abs(data.z) if mag > 0: return 20 * math.log10(mag) return 0 @@ -59,9 +65,7 @@ def qualityFactor(data: Datapoint): def calculateVSWR(data: Datapoint): # re50, im50 = normalize50(data) try: - # mag = math.sqrt((re50 - 50) * (re50 - 50) + im50 * im50) / \ - # math.sqrt((re50 + 50) * (re50 + 50) + im50 * im50) - mag = math.sqrt(data.re**2 + data.im**2) + mag = abs(data.z) vswr = (1 + mag) / (1 - mag) except ZeroDivisionError: vswr = 1 @@ -103,31 +107,18 @@ def parseFrequency(freq: str) -> int: @staticmethod def phaseAngle(data: Datapoint) -> float: - re = data.re - im = data.im - return math.degrees(math.atan2(im, re)) + return math.degrees(cmath.phase(data.z)) @staticmethod def phaseAngleRadians(data: Datapoint) -> float: - re = data.re - im = data.im - return math.atan2(im, re) - - @staticmethod - def clamp_int(value: int, min: int, max: int) -> int: - assert min <= max - if value < min: - return min - if value > max: - return max - return value + return cmath.phase(data.z) @staticmethod def groupDelay(data: List[Datapoint], index: int) -> float: - index0 = RFTools.clamp_int(index - 1, 0, len(data) - 1) - index1 = RFTools.clamp_int(index + 1, 0, len(data) - 1) - angle0 = RFTools.phaseAngleRadians(data[index0]) - angle1 = RFTools.phaseAngleRadians(data[index1]) + index0 = clamp_int(index - 1, 0, len(data) - 1) + index1 = clamp_int(index + 1, 0, len(data) - 1) + angle0 = cmath.phase(data[index0].z) + angle1 = cmath.phase(data[index1].z) freq0 = data[index0].freq freq1 = data[index1].freq delta_angle = (angle1 - angle0) From 0f4af45bfe28cb37999573202cf451cf49e768f8 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Wed, 13 Nov 2019 20:50:19 +0100 Subject: [PATCH 11/84] RFTools: added generic gamma to impedance funtion --- NanoVNASaver/RFTools.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index c470e314..b5c45abd 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -29,6 +29,10 @@ def clamp_int(value: int, imin: int, imax: int) -> int: return value +def gamma_to_impedance(gamma: complex, impedance: float) -> complex: + return impedance * ((-gamma - 1) / (gamma - 1)) + + class Datapoint(NamedTuple): freq: int re: float @@ -42,7 +46,7 @@ def z(self): class RFTools: @staticmethod def normalize50(data: Datapoint): - result = 50 * ((-data.z - 1) / (data.z - 1)) + result = gamma_to_impedance(data.z, 50) return result.real, result.imag @staticmethod @@ -54,16 +58,13 @@ def gain(data: Datapoint) -> float: @staticmethod def qualityFactor(data: Datapoint): - re50, im50 = RFTools.normalize50(data) - if re50 != 0: - Q = abs(im50 / re50) - else: - Q = -1 - return Q + imp = gamma_to_impedance(data.z, 50) + if imp.real != 0.0: + return abs(imp.imag / imp.real) + return -1 @staticmethod def calculateVSWR(data: Datapoint): - # re50, im50 = normalize50(data) try: mag = abs(data.z) vswr = (1 + mag) / (1 - mag) From ea344eda45ee0ca1c853bec4f7ce3fa442850f82 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Fri, 15 Nov 2019 12:43:18 +0100 Subject: [PATCH 12/84] added methods to Datatapoint added RFTools conversation functions to Datapoint class --- NanoVNASaver/RFTools.py | 69 ++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index b5c45abd..860486f5 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -1,4 +1,5 @@ -# NanoVNASaver - a python program to view and export Touchstone data from a NanoVNA +# NanoVNASaver +# A python program to view and export Touchstone data from a NanoVNA # Copyright (C) 2019. Rune B. Broberg # # This program is free software: you can redistribute it and/or modify @@ -20,6 +21,7 @@ from NanoVNASaver.SITools import Value, Format + def clamp_int(value: int, imin: int, imax: int) -> int: assert imin <= imax if value < imin: @@ -29,10 +31,6 @@ def clamp_int(value: int, imin: int, imax: int) -> int: return value -def gamma_to_impedance(gamma: complex, impedance: float) -> complex: - return impedance * ((-gamma - 1) / (gamma - 1)) - - class Datapoint(NamedTuple): freq: int re: float @@ -43,34 +41,61 @@ def z(self): """ return datapoint impedance as complex number """ return complex(self.re, self.im) + def as_gain(self) -> float: + mag = abs(self.z) + if mag > 0: + return 20 * math.log10(mag) + return 0 + + def as_vswr(self) -> float: + mag = abs(self.z) + if mag == 1: + return 1 + return (1 + mag) / (1 - mag) + + def to_impedance(self, ref_impedance: float = 50) -> complex: + return ref_impedance * ((-self.z - 1) / (self.z - 1)) + + def to_q_factor(self, ref_impedance: float = 50) -> float: + imp = self.to_impedance(ref_impedance) + if imp.real == 0.0: + return -1 + return abs(imp.imag / imp.real) + + def to_capacitive_equivalent(self, ref_impedance: float = 50) -> float: + if self.freq == 0: + return math.inf + imp = self.to_impedance(ref_impedance) + if imp.imag == 0: + return math.inf + return -(1 / (self.freq * 2 * math.pi * imp.imag)) + + def to_inductive_equivalent(self, ref_impedance: float = 50) -> float: + if self.freq == 0: + return math.inf + imp = self.to_impedance(ref_impedance) + if imp.imag == 0: + return 0 + return imp.imag * 1 / (self.freq * 2 * math.pi) + + class RFTools: @staticmethod def normalize50(data: Datapoint): - result = gamma_to_impedance(data.z, 50) + result = data.to_impedance() return result.real, result.imag @staticmethod def gain(data: Datapoint) -> float: - mag = abs(data.z) - if mag > 0: - return 20 * math.log10(mag) - return 0 + return data.as_gain() @staticmethod - def qualityFactor(data: Datapoint): - imp = gamma_to_impedance(data.z, 50) - if imp.real != 0.0: - return abs(imp.imag / imp.real) - return -1 + def qualityFactor(data: Datapoint) -> float: + return data.to_q_factor() @staticmethod - def calculateVSWR(data: Datapoint): - try: - mag = abs(data.z) - vswr = (1 + mag) / (1 - mag) - except ZeroDivisionError: - vswr = 1 - return vswr + def calculateVSWR(data: Datapoint) -> float: + return data.as_vswr() @staticmethod def capacitanceEquivalent(im50, freq) -> str: From ae4fb3dcca77a158172b18c7426bd9a0a822c456 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Fri, 15 Nov 2019 12:55:54 +0100 Subject: [PATCH 13/84] Added phase property to RFTools.Datapoint --- NanoVNASaver/RFTools.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index 860486f5..29606f7d 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -41,6 +41,11 @@ def z(self): """ return datapoint impedance as complex number """ return complex(self.re, self.im) + @property + def phase(self): + """ return datapoints phase value """ + return cmath.phase(self.z) + def as_gain(self) -> float: mag = abs(self.z) if mag > 0: @@ -141,17 +146,13 @@ def phaseAngleRadians(data: Datapoint) -> float: @staticmethod def groupDelay(data: List[Datapoint], index: int) -> float: - index0 = clamp_int(index - 1, 0, len(data) - 1) - index1 = clamp_int(index + 1, 0, len(data) - 1) - angle0 = cmath.phase(data[index0].z) - angle1 = cmath.phase(data[index1].z) - freq0 = data[index0].freq - freq1 = data[index1].freq - delta_angle = (angle1 - angle0) + idx0 = clamp_int(index - 1, 0, len(data) - 1) + idx1 = clamp_int(index + 1, 0, len(data) - 1) + delta_angle = (data[idx1].phase - data[idx0].phase) if abs(delta_angle) > math.tau: if delta_angle > 0: delta_angle = delta_angle % math.tau else: delta_angle = -1 * (delta_angle % math.tau) - val = -delta_angle / math.tau / (freq1 - freq0) + val = -delta_angle / math.tau / (data[idx1].freq - data[idx0].freq) return val From 311b959d8342f6b722fa7a7cdfc59bfe42442f8b Mon Sep 17 00:00:00 2001 From: "Rune B. Broberg" Date: Sat, 16 Nov 2019 12:07:32 +0100 Subject: [PATCH 14/84] Middle button to drag zoomed charts. --- NanoVNASaver/Chart.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index b1d0dd5a..aad4368c 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -62,6 +62,8 @@ class Chart(QtWidgets.QWidget): draggedBox = False draggedBoxStart = (0, 0) draggedBoxCurrent = (-1, -1) + moveStartX = -1 + moveStartY = -1 isPopout = False popoutRequested = pyqtSignal(object) @@ -204,12 +206,18 @@ def mousePressEvent(self, event: QtGui.QMouseEvent) -> None: if event.buttons() == QtCore.Qt.RightButton: event.ignore() return + elif event.buttons() == QtCore.Qt.MiddleButton: + # Drag event + event.accept() + self.moveStartX = event.x() + self.moveStartY = event.y() + return if event.modifiers() == QtCore.Qt.ShiftModifier: self.draggedMarker = self.getNearestMarker(event.x(), event.y()) elif event.modifiers() == QtCore.Qt.ControlModifier: + event.accept() self.draggedBox = True self.draggedBoxStart = (event.x(), event.y()) - event.accept() return self.mouseMoveEvent(event) @@ -593,6 +601,18 @@ def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None: if a0.buttons() == QtCore.Qt.RightButton: a0.ignore() return + if a0.buttons() == QtCore.Qt.MiddleButton: + # Drag the display + a0.accept() + if self.moveStartX != -1 and self.moveStartY != -1: + dx = self.moveStartX - a0.x() + dy = self.moveStartY - a0.y() + self.zoomTo(self.leftMargin + dx, self.topMargin + dy, + self.leftMargin + self.chartWidth + dx, self.topMargin + self.chartHeight + dy) + + self.moveStartX = a0.x() + self.moveStartY = a0.y() + return if a0.modifiers() == QtCore.Qt.ControlModifier: # Dragging a box if not self.draggedBox: From 453b4d31bc7bcd148baf75711c2bbd30a2a32503 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Sat, 16 Nov 2019 14:30:23 +0100 Subject: [PATCH 15/84] added .pylintrc --- .pylintrc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .pylintrc diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 00000000..79967d96 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,15 @@ +[MESSAGES CONTROL] +disable=W0614,C0410,C0321,C0111,I0011,C0103 +# allow _ for ignored variables +# allow generic names like a,b,c and i,j,k,l,m,n and x,y,z +# allow k,v for key/value +# allow e for exceptions, it for iterator +# allow w,h for width, height +# allow op for operation/operator/opcode +# allow t, t0, t1, t2, and t3 for time +# allow dt for delta time +# allow db for database +# allow ls for list +good-names=_,a,b,c,dt,db,e,f,fn,fd,i,j,k,v,kv,kw,l,m,n,ls,t,t0,t1,t2,t3,w,h,x,y,z,it,op +[MASTER] +extension-pkg-whitelist=PyQt5 From 30690cf22b7862f928eac749d31c7f3199383d22 Mon Sep 17 00:00:00 2001 From: "Rune B. Broberg" Date: Sat, 16 Nov 2019 14:59:48 +0100 Subject: [PATCH 16/84] Update version 0.2.1alpha Get text from the right part of the combo box for serial port. --- NanoVNASaver/NanoVNASaver.py | 4 ++-- NanoVNASaver/about.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/NanoVNASaver/NanoVNASaver.py b/NanoVNASaver/NanoVNASaver.py index 54a31b13..e1ede464 100644 --- a/NanoVNASaver/NanoVNASaver.py +++ b/NanoVNASaver/NanoVNASaver.py @@ -528,7 +528,7 @@ def __init__(self): def rescanSerialPort(self): self.serialPortInput.clear() for port in self.getPort(): - self.serialPortInput.insertItem(1,port) + self.serialPortInput.insertItem(1, port) # Get that windows port @staticmethod @@ -625,7 +625,7 @@ def serialButtonClick(self): def startSerial(self): if self.serialLock.acquire(): - self.serialPort = self.serialPortInput.text() + self.serialPort = self.serialPortInput.currentText() logger.info("Opening serial port %s", self.serialPort) try: self.serial = serial.Serial(port=self.serialPort, baudrate=115200) diff --git a/NanoVNASaver/about.py b/NanoVNASaver/about.py index 56c11dc1..69245aa0 100644 --- a/NanoVNASaver/about.py +++ b/NanoVNASaver/about.py @@ -14,5 +14,5 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -version = '0.2.0' +version = '0.2.1alpha' debug = False From d6be12ed0992a9b2c9ae8643a77e878bff0affe0 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Sat, 16 Nov 2019 15:10:32 +0100 Subject: [PATCH 17/84] added .pylintrc --- .pylintrc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .pylintrc diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 00000000..79967d96 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,15 @@ +[MESSAGES CONTROL] +disable=W0614,C0410,C0321,C0111,I0011,C0103 +# allow _ for ignored variables +# allow generic names like a,b,c and i,j,k,l,m,n and x,y,z +# allow k,v for key/value +# allow e for exceptions, it for iterator +# allow w,h for width, height +# allow op for operation/operator/opcode +# allow t, t0, t1, t2, and t3 for time +# allow dt for delta time +# allow db for database +# allow ls for list +good-names=_,a,b,c,dt,db,e,f,fn,fd,i,j,k,v,kv,kw,l,m,n,ls,t,t0,t1,t2,t3,w,h,x,y,z,it,op +[MASTER] +extension-pkg-whitelist=PyQt5 From 9bb42ffc296a68166fdfdc4181b6b9b364d0efcf Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Sat, 16 Nov 2019 15:33:11 +0100 Subject: [PATCH 18/84] refactored phaseAngle methods --- NanoVNASaver/Chart.py | 12 ++++++------ NanoVNASaver/Marker.py | 4 ++-- NanoVNASaver/RFTools.py | 8 -------- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index ca8f813b..028f9838 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -852,11 +852,11 @@ def drawValues(self, qp: QtGui.QPainter): if self.unwrap: rawData = [] for d in self.data: - rawData.append(RFTools.phaseAngleRadians(d)) + rawData.append(d.phase) rawReference = [] for d in self.reference: - rawReference.append(RFTools.phaseAngleRadians(d)) + rawReference.append(d.phase) self.unwrappedData = np.degrees(np.unwrap(rawData)) self.unwrappedReference = np.degrees(np.unwrap(rawReference)) @@ -935,9 +935,9 @@ def getYPosition(self, d: Datapoint) -> int: elif d in self.reference: angle = self.unwrappedReference[self.reference.index(d)] else: - angle = RFTools.phaseAngle(d) + angle = math.degrees(d.phase) else: - angle = RFTools.phaseAngle(d) + angle = math.degrees(d.phase) return self.topMargin + round((self.maxAngle - angle) / self.span * self.chartHeight) def valueAtPosition(self, y) -> List[float]: @@ -3549,11 +3549,11 @@ def setData(self, data): def calculateGroupDelay(self): rawData = [] for d in self.data: - rawData.append(RFTools.phaseAngleRadians(d)) + rawData.append(d.phase) rawReference = [] for d in self.reference: - rawReference.append(RFTools.phaseAngleRadians(d)) + rawReference.append(d.phase) if len(self.data) > 0: self.unwrappedData = np.degrees(np.unwrap(rawData)) diff --git a/NanoVNASaver/Marker.py b/NanoVNASaver/Marker.py index f2857af5..ef79e6db 100644 --- a/NanoVNASaver/Marker.py +++ b/NanoVNASaver/Marker.py @@ -393,14 +393,14 @@ def updateLabels(self, s11data: List[Datapoint], s21data: List[Datapoint]): q_str = str(round(q, 3)) self.quality_factor_label.setText(q_str) self.s11_phase_label.setText( - str(round(RFTools.phaseAngle(s11data[self.location]), 2)) + "\N{DEGREE SIGN}") + str(round(math.degrees(s11data[self.location].phase), 2)) + "\N{DEGREE SIGN}") fmt = SITools.Format(max_nr_digits=5, space_str=" ") self.s11_group_delay_label.setText(str(SITools.Value(RFTools.groupDelay(s11data, self.location), "s", fmt))) if len(s21data) == len(s11data): self.gain_label.setText(str(round(RFTools.gain(s21data[self.location]), 3)) + " dB") self.s21_phase_label.setText( - str(round(RFTools.phaseAngle(s21data[self.location]), 2)) + "\N{DEGREE SIGN}") + str(round(math.degrees(s21data[self.location].phase), 2)) + "\N{DEGREE SIGN}") self.s21_group_delay_label.setText(str(SITools.Value(RFTools.groupDelay(s21data, self.location) / 2, "s", fmt))) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index 29606f7d..13e570b7 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -136,14 +136,6 @@ def parseFrequency(freq: str) -> int: except (ValueError, IndexError): return -1 - @staticmethod - def phaseAngle(data: Datapoint) -> float: - return math.degrees(cmath.phase(data.z)) - - @staticmethod - def phaseAngleRadians(data: Datapoint) -> float: - return cmath.phase(data.z) - @staticmethod def groupDelay(data: List[Datapoint], index: int) -> float: idx0 = clamp_int(index - 1, 0, len(data) - 1) From 2f9749bd60ef96297ea9894e2b6dfe16d8e0ae6c Mon Sep 17 00:00:00 2001 From: dhunt1342 Date: Sat, 16 Nov 2019 10:49:20 -0500 Subject: [PATCH 19/84] Add unittest framework Adding a unittest framework to the project to enhance debugging capabilities, document expected (and unexpected) behaviors, and to prevent regression failures. --- test/__init__.py | 16 ++++ test/test_formatSweepFrequency.py | 83 ++++++++++++++++++ test/test_parseFrequency.py | 141 ++++++++++++++++++++++++++++++ test_master.py | 37 ++++++++ 4 files changed, 277 insertions(+) create mode 100644 test/__init__.py create mode 100644 test/test_formatSweepFrequency.py create mode 100644 test/test_parseFrequency.py create mode 100644 test_master.py diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 00000000..77f85680 --- /dev/null +++ b/test/__init__.py @@ -0,0 +1,16 @@ +# NanoVNASaver - a python program to view and export Touchstone data from a NanoVNA +# Copyright (C) 2019. Rune B. Broberg +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + diff --git a/test/test_formatSweepFrequency.py b/test/test_formatSweepFrequency.py new file mode 100644 index 00000000..4a6de5fc --- /dev/null +++ b/test/test_formatSweepFrequency.py @@ -0,0 +1,83 @@ +# NanoVNASaver - a python program to view and export Touchstone data from a NanoVNA +# Copyright (C) 2019. Rune B. Broberg +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +import sys +import unittest + +# Import targets to be tested +from NanoVNASaver import RFTools +rft = RFTools.RFTools() + +class TestCases(unittest.TestCase): + + ''' + def formatSweepFrequency(freq: int, + mindigits: int = 2, + appendHz: bool = True, + insertSpace: bool = False, + countDot: bool = True, + assumeInfinity: bool = True) -> str: + ''' + + def test_basicIntegerValues(self): + # simple well-formed integers with no trailing zeros. Most importantly + # even with default mindigits, there is no loss of accuracy in the + # result. Returned values are not truncated if result would lose + # meaningful data. + self.assertEqual(rft.formatSweepFrequency(1), '1Hz') + self.assertEqual(rft.formatSweepFrequency(12), '12Hz') + self.assertEqual(rft.formatSweepFrequency(123), '123Hz') + self.assertEqual(rft.formatSweepFrequency(1234), '1.234kHz') + self.assertEqual(rft.formatSweepFrequency(12345), '12.345kHz') + self.assertEqual(rft.formatSweepFrequency(123456), '123.456kHz') + self.assertEqual(rft.formatSweepFrequency(1234567), '1.234567MHz') + self.assertEqual(rft.formatSweepFrequency(12345678), '12.345678MHz') + self.assertEqual(rft.formatSweepFrequency(123456789), '123.456789MHz') + + def test_defaultMinDigits(self): + # simple integers with trailing zeros. + # DEFAULT behavior retains 2 digits after the period, mindigits=2. + self.assertEqual(rft.formatSweepFrequency(1000), '1.00kHz') + self.assertEqual(rft.formatSweepFrequency(10000), '10.00kHz') + self.assertEqual(rft.formatSweepFrequency(100000), '100.00kHz') + self.assertEqual(rft.formatSweepFrequency(1000000), '1.00MHz') + + def test_nonDefaultMinDigits(self): + # simple integers with trailing zeros. setting mindigit value to something + # other than default, where trailing zeros >= mindigits, the number of + # zeros shown is equal to mindigits value. + self.assertEqual(rft.formatSweepFrequency(1000000, mindigits=0), '1MHz') + self.assertEqual(rft.formatSweepFrequency(1000000, mindigits=1), '1.0MHz') + self.assertEqual(rft.formatSweepFrequency(1000000, mindigits=3), '1.000MHz') + self.assertEqual(rft.formatSweepFrequency(10000000, mindigits=4), '10.0000MHz') + self.assertEqual(rft.formatSweepFrequency(100000000, mindigits=5), '100.00000MHz') + self.assertEqual(rft.formatSweepFrequency(1000000000, mindigits=6), '1.000000GHz') + # where trailing zeros < mindigits, only available zeros are shown, if the + # result includes no decimal places (i.e. Hz values). + self.assertEqual(rft.formatSweepFrequency(1, mindigits=4), '1Hz') + self.assertEqual(rft.formatSweepFrequency(10, mindigits=4), '10Hz') + self.assertEqual(rft.formatSweepFrequency(100, mindigits=4), '100Hz') + # but where a decimal exists, and mindigits > number of available zeroes, + # this results in extra zeroes being padded into result, even into sub-Hz + # resolution. This is not useful for this application. + # 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=10), '1.0000000000kHz') + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/test/test_parseFrequency.py b/test/test_parseFrequency.py new file mode 100644 index 00000000..baffbeee --- /dev/null +++ b/test/test_parseFrequency.py @@ -0,0 +1,141 @@ +# NanoVNASaver - a python program to view and export Touchstone data from a NanoVNA +# Copyright (C) 2019. Rune B. Broberg +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import sys +import unittest + +# Import targets to be tested +from NanoVNASaver import RFTools +rft = RFTools.RFTools() + +class TestCases(unittest.TestCase): + + def test_basicSIUnits(self): + # simple well-formed integers with correct SI units + self.assertEqual(rft.parseFrequency('123Hz'), 123) + self.assertEqual(rft.parseFrequency('123456Hz'), 123456) + self.assertEqual(rft.parseFrequency('123kHz'), 123000) + self.assertEqual(rft.parseFrequency('123456kHz'), 123456000) + self.assertEqual(rft.parseFrequency('123MHz'), 123000000) + self.assertEqual(rft.parseFrequency('123456MHz'), 123456000000) + self.assertEqual(rft.parseFrequency('123GHz'), 123000000000) + self.assertEqual(rft.parseFrequency('123456GHz'), 123456000000000) + + def test_commonMistakeKHz_vs_kHz(self): + # some poorly formatted values that still work as expected + self.assertEqual(rft.parseFrequency('123kHz'), 123000) + self.assertEqual(rft.parseFrequency('123KHz'), 123000) + + def test_illegalInputValues(self): + # poorly formatted inputs that are identified as illegal + self.assertEqual(rft.parseFrequency('Junk'), -1) + self.assertEqual(rft.parseFrequency('Garbage'), -1) + self.assertEqual(rft.parseFrequency('123.Junk'), -1) + + def test_missingDigitsAfterPeriod(self): + # some poorly formatted values that still work as expected + self.assertEqual(rft.parseFrequency('123.'), 123) + self.assertEqual(rft.parseFrequency('123.Hz'), 123) + self.assertEqual(rft.parseFrequency('123.kHz'), 123000) + self.assertEqual(rft.parseFrequency('123.MHz'), 123000000) + self.assertEqual(rft.parseFrequency('123.GHz'), 123000000000) + + def test_unusualSIUnits(self): + ####################################################################### + # Original behavior: unusual SI values that were legal, but inappropriate + # for this application provided unexpected outputs. This behavior was + # based on the full set of SI prefixes previously defined in SITools (below). + #PREFIXES = ("y", "z", "a", "f", "p", "n", "µ", "m", + # "", "k", "M", "G", "T", "P", "E", "Z", "Y") + # New Behavior: After the reduction of legal SI values defined in SITools + # (below), these now return a -1 failure code. + # PREFIXES = ("", "k", "M", "G") + ####################################################################### + ''' + self.assertEqual(rft.parseFrequency('123EHz'), 123000000000000000000) + self.assertEqual(rft.parseFrequency('123PHz'), 123000000000000000) + self.assertEqual(rft.parseFrequency('123THz'), 123000000000000) + self.assertEqual(rft.parseFrequency('123YHz'), 122999999999999998473273344) + self.assertEqual(rft.parseFrequency('123ZHz'), 123000000000000002097152) + self.assertEqual(rft.parseFrequency('123aHz'), 0) + self.assertEqual(rft.parseFrequency('123fHz'), 0) + self.assertEqual(rft.parseFrequency('123mHz'), 0) + self.assertEqual(rft.parseFrequency('123nHz'), 0) + self.assertEqual(rft.parseFrequency('123pHz'), 0) + self.assertEqual(rft.parseFrequency('123yHz'), 0) + self.assertEqual(rft.parseFrequency('123zHz'), 0) + ''' + self.assertEqual(rft.parseFrequency('123EHz'), -1) + self.assertEqual(rft.parseFrequency('123PHz'), -1) + self.assertEqual(rft.parseFrequency('123THz'), -1) + self.assertEqual(rft.parseFrequency('123YHz'), -1) + self.assertEqual(rft.parseFrequency('123ZHz'), -1) + self.assertEqual(rft.parseFrequency('123aHz'), -1) + self.assertEqual(rft.parseFrequency('123fHz'), -1) + self.assertEqual(rft.parseFrequency('123mHz'), -1) + self.assertEqual(rft.parseFrequency('123nHz'), -1) + self.assertEqual(rft.parseFrequency('123pHz'), -1) + self.assertEqual(rft.parseFrequency('123yHz'), -1) + self.assertEqual(rft.parseFrequency('123zHz'), -1) + + def test_partialHzText(self): + ####################################################################### + # Previous behavior for accidentally missing the H in Hz, resulted in + # detection of 'z' SI unit (zepto = 10^-21), which then rounded to 0. + # After reduction of legal SI values in SITools, this now returns a -1 + # failure code. + ####################################################################### + ''' + self.assertEqual(rft.parseFrequency('123z'), 0) + self.assertEqual(rft.parseFrequency('123.z'), 0) + self.assertEqual(rft.parseFrequency('1.23z'), 0) + ''' + self.assertEqual(rft.parseFrequency('123z'), -1) + self.assertEqual(rft.parseFrequency('123.z'), -1) + self.assertEqual(rft.parseFrequency('1.23z'), -1) + + def test_basicExponentialNotation(self): + # check basic exponential notation + self.assertEqual(rft.parseFrequency('123e3'), 123000) + self.assertEqual(rft.parseFrequency('123e6'), 123000000) + self.assertEqual(rft.parseFrequency('123e9'), 123000000000) + self.assertEqual(rft.parseFrequency('123e4'), 1230000) + self.assertEqual(rft.parseFrequency('123e12'), 123000000000000) + self.assertEqual(rft.parseFrequency('123e18'), 123000000000000000000) + + def test_negativeExponentialNotation(self): + # negative exponential values resulting in N < 0, return 0 + self.assertEqual(rft.parseFrequency('123e-3'), 0) + self.assertEqual(rft.parseFrequency('1234e-4'), 0) + self.assertEqual(rft.parseFrequency('12345e-5'), 0) + self.assertEqual(rft.parseFrequency('12345678e-8'), 0) + # negative exponential values resulting in N > 0, return N + self.assertEqual(rft.parseFrequency('100000e-5'), 1) + self.assertEqual(rft.parseFrequency('100000e-4'), 10) + self.assertEqual(rft.parseFrequency('100000e-3'), 100) + self.assertEqual(rft.parseFrequency('100000e-2'), 1000) + self.assertEqual(rft.parseFrequency('100000e-1'), 10000) + + def test_multiplePeriods(self): + # multiple periods are properly detected as bad + self.assertEqual(rft.parseFrequency('123..Hz'), -1) + self.assertEqual(rft.parseFrequency('123...Hz'), -1) + self.assertEqual(rft.parseFrequency('123....Hz'), -1) + self.assertEqual(rft.parseFrequency('1.23.Hz'), -1) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/test_master.py b/test_master.py new file mode 100644 index 00000000..df17a272 --- /dev/null +++ b/test_master.py @@ -0,0 +1,37 @@ +# NanoVNASaver - a python program to view and export Touchstone data from a NanoVNA +# Copyright (C) 2019. Rune B. Broberg +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import sys +import unittest + + +############################################################################### +# +# Executing this file initiates the discovery and execution of all unittests +# in the "test" directory, with filenames starting with "test" that have a +# TestCases(unittest.TestCase) Class, which has class functions starting with +# "test_". This provides a simple test framework that is easily expandable by +# simply adding new "test_xxx.py" files into the test directory. +# +############################################################################### + +if __name__ == '__main__': + loader = unittest.TestLoader() + tests = loader.discover('.') + testRunner = unittest.runner.TextTestRunner( + failfast=False, + verbosity=2) + testRunner.run(tests) From 7aac6ce2b3dd84fcc3a6a6152788ef9c136105e6 Mon Sep 17 00:00:00 2001 From: dhunt1342 Date: Sat, 16 Nov 2019 10:51:51 -0500 Subject: [PATCH 20/84] Reduce set of legal SI PREFIXES The full set of SI PREFIXES in SITools.py can result in unexpected behavior when typos are made in the sweep control entries. Reduced the set to just Hz, kHz, MHz, and GHz. --- NanoVNASaver/SITools.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py index 2d3f23b2..9705499a 100644 --- a/NanoVNASaver/SITools.py +++ b/NanoVNASaver/SITools.py @@ -17,9 +17,14 @@ import math from numbers import Number -PREFIXES = ("y", "z", "a", "f", "p", "n", "µ", "m", - "", "k", "M", "G", "T", "P", "E", "Z", "Y") +########################################################################## +# Full set of SI prefixes +#PREFIXES = ("y", "z", "a", "f", "p", "n", "µ", "m", +# "", "k", "M", "G", "T", "P", "E", "Z", "Y") +########################################################################## +# Limited SI prefix set that makes sense for this application +PREFIXES = ("", "k", "M", "G") class Format(object): def __init__(self, @@ -92,11 +97,11 @@ def parse(self, value: str): factor = 1 if value[-1] in PREFIXES: - factor = 10 ** ((PREFIXES.index(value[-1]) - 8) * 3) + factor = 10 ** ((PREFIXES.index(value[-1])) * 3) value = value[:-1] elif value[-1] == 'K': # Fix for the very common KHz - factor = 10 ** ((PREFIXES.index(value[-1].lower()) - 8) * 3) + factor = 10 ** ((PREFIXES.index(value[-1].lower())) * 3) value = value[:-1] self.value = float(value) * factor return self.value From f9fea4817b7ebc6d3abcea25b36e9686254f59cc Mon Sep 17 00:00:00 2001 From: dhunt1342 Date: Sat, 16 Nov 2019 11:13:16 -0500 Subject: [PATCH 21/84] Revert "Merge branch 'Development' of https://github.com/mihtjel/nanovna-saver into Development" This reverts commit 4555c37adb4d71effb3e36c5ca1d8ff2b21f1de6, reversing changes made to 7aac6ce2b3dd84fcc3a6a6152788ef9c136105e6. --- NanoVNASaver/Chart.py | 22 +--------- NanoVNASaver/NanoVNASaver.py | 4 +- NanoVNASaver/RFTools.py | 85 ++++++++++++++++++++++++------------ NanoVNASaver/SITools.py | 62 ++++++++++++-------------- NanoVNASaver/about.py | 2 +- 5 files changed, 91 insertions(+), 84 deletions(-) diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index aad4368c..b1d0dd5a 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -62,8 +62,6 @@ class Chart(QtWidgets.QWidget): draggedBox = False draggedBoxStart = (0, 0) draggedBoxCurrent = (-1, -1) - moveStartX = -1 - moveStartY = -1 isPopout = False popoutRequested = pyqtSignal(object) @@ -206,18 +204,12 @@ def mousePressEvent(self, event: QtGui.QMouseEvent) -> None: if event.buttons() == QtCore.Qt.RightButton: event.ignore() return - elif event.buttons() == QtCore.Qt.MiddleButton: - # Drag event - event.accept() - self.moveStartX = event.x() - self.moveStartY = event.y() - return if event.modifiers() == QtCore.Qt.ShiftModifier: self.draggedMarker = self.getNearestMarker(event.x(), event.y()) elif event.modifiers() == QtCore.Qt.ControlModifier: - event.accept() self.draggedBox = True self.draggedBoxStart = (event.x(), event.y()) + event.accept() return self.mouseMoveEvent(event) @@ -601,18 +593,6 @@ def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None: if a0.buttons() == QtCore.Qt.RightButton: a0.ignore() return - if a0.buttons() == QtCore.Qt.MiddleButton: - # Drag the display - a0.accept() - if self.moveStartX != -1 and self.moveStartY != -1: - dx = self.moveStartX - a0.x() - dy = self.moveStartY - a0.y() - self.zoomTo(self.leftMargin + dx, self.topMargin + dy, - self.leftMargin + self.chartWidth + dx, self.topMargin + self.chartHeight + dy) - - self.moveStartX = a0.x() - self.moveStartY = a0.y() - return if a0.modifiers() == QtCore.Qt.ControlModifier: # Dragging a box if not self.draggedBox: diff --git a/NanoVNASaver/NanoVNASaver.py b/NanoVNASaver/NanoVNASaver.py index e1ede464..54a31b13 100644 --- a/NanoVNASaver/NanoVNASaver.py +++ b/NanoVNASaver/NanoVNASaver.py @@ -528,7 +528,7 @@ def __init__(self): def rescanSerialPort(self): self.serialPortInput.clear() for port in self.getPort(): - self.serialPortInput.insertItem(1, port) + self.serialPortInput.insertItem(1,port) # Get that windows port @staticmethod @@ -625,7 +625,7 @@ def serialButtonClick(self): def startSerial(self): if self.serialLock.acquire(): - self.serialPort = self.serialPortInput.currentText() + self.serialPort = self.serialPortInput.text() logger.info("Opening serial port %s", self.serialPort) try: self.serial = serial.Serial(port=self.serialPort, baudrate=115200) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index c217936e..c05ae3f2 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -15,7 +15,6 @@ # along with this program. If not, see . import collections import math -from numbers import Number from typing import List from NanoVNASaver.SITools import Value, Format @@ -34,7 +33,7 @@ def normalize50(data: Datapoint): return re50, im50 @staticmethod - def gain(data: Datapoint) -> float: + def gain(data: Datapoint): # re50, im50 = normalize50(data) # Calculate the gain / reflection coefficient # mag = math.sqrt((re50 - 50) * (re50 - 50) + im50 * im50) / \ @@ -44,7 +43,8 @@ def gain(data: Datapoint) -> float: mag = math.sqrt(data.re**2 + data.im**2) if mag > 0: return 20 * math.log10(mag) - return 0 + else: + return 0 @staticmethod def qualityFactor(data: Datapoint): @@ -73,7 +73,7 @@ def capacitanceEquivalent(im50, freq) -> str: return "- pF" capacitance = 1 / (freq * 2 * math.pi * im50) return str(Value(-capacitance, "F", Format(max_nr_digits=5, space_str=" "))) - + @staticmethod def inductanceEquivalent(im50, freq) -> str: if freq == 0: @@ -82,54 +82,85 @@ def inductanceEquivalent(im50, freq) -> str: return str(Value(inductance, "H", Format(max_nr_digits=5, space_str=" "))) @staticmethod - def formatFrequency(freq: Number) -> str: - return str(Value(freq, "Hz")) + def formatFrequency(freq): + return str(Value(freq, "Hz", Format(max_nr_digits=6))) @staticmethod - def formatShortFrequency(freq: Number) -> str: + def formatShortFrequency(freq): return str(Value(freq, "Hz", Format(max_nr_digits=4))) @staticmethod - def formatSweepFrequency(freq: Number) -> str: - return str(Value(freq, "Hz", Format(max_nr_digits=5))) + def formatSweepFrequency(freq: int, + mindigits: int = 2, + appendHz: bool = True, + insertSpace: bool = False, + countDot: bool = True, + assumeInfinity: bool = True) -> str: + """ Format frequency with SI prefixes + + mindigits count refers to the number of decimal place digits + that will be shown, padded with zeroes if needed. + """ + freqstr = str(freq) + freqlen = len(freqstr) + + # sanity checks + if freqlen > 15: + if assumeInfinity: + return "\N{INFINITY}" + raise ValueError("Frequency too big. More than 15 digits!") + + if freq < 1: + return " - " + (" " if insertSpace else "") + ("Hz" if appendHz else "") + + si_index = (freqlen - 1) // 3 + dot_pos = freqlen % 3 or 3 + intfstr = freqstr[:dot_pos] + decfstr = freqstr[dot_pos:] + nzdecfstr = decfstr.rstrip('0') + if si_index != 0: + while len(nzdecfstr) < mindigits: + nzdecfstr += '0' + freqstr = intfstr + ("." if len(nzdecfstr) > 0 else "") + nzdecfstr + return freqstr + (" " if insertSpace else "") + PREFIXES[si_index] + ("Hz" if appendHz else "") @staticmethod def parseFrequency(freq: str) -> int: - parser = Value(0, "Hz", Format(parse_sloppy_unit=True, parse_sloppy_kilo=True)) + parser = Value(0, "Hz") try: return round(parser.parse(freq)) except (ValueError, IndexError): return -1 @staticmethod - def phaseAngle(data: Datapoint) -> float: + def phaseAngle(data: Datapoint): re = data.re im = data.im return math.degrees(math.atan2(im, re)) @staticmethod - def phaseAngleRadians(data: Datapoint) -> float: + def phaseAngleRadians(data: Datapoint): re = data.re im = data.im return math.atan2(im, re) - @staticmethod - def clamp_int(value: int, min: int, max: int) -> int: - assert min <= max - if value < min: - return min - if value > max: - return max - return value - @staticmethod def groupDelay(data: List[Datapoint], index: int) -> float: - index0 = RFTools.clamp_int(index - 1, 0, len(data) - 1) - index1 = RFTools.clamp_int(index + 1, 0, len(data) - 1) - angle0 = RFTools.phaseAngleRadians(data[index0]) - angle1 = RFTools.phaseAngleRadians(data[index1]) - freq0 = data[index0].freq - freq1 = data[index1].freq + if index == 0: + angle0 = RFTools.phaseAngleRadians(data[0]) + angle1 = RFTools.phaseAngleRadians(data[1]) + freq0 = data[0].freq + freq1 = data[1].freq + elif index == len(data) - 1: + angle0 = RFTools.phaseAngleRadians(data[-2]) + angle1 = RFTools.phaseAngleRadians(data[-1]) + freq0 = data[-2].freq + freq1 = data[-1].freq + else: + angle0 = RFTools.phaseAngleRadians(data[index-1]) + angle1 = RFTools.phaseAngleRadians(data[index+1]) + freq0 = data[index-1].freq + freq1 = data[index+1].freq delta_angle = (angle1 - angle0) if abs(delta_angle) > math.tau: if delta_angle > 0: diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py index ca13819b..9705499a 100644 --- a/NanoVNASaver/SITools.py +++ b/NanoVNASaver/SITools.py @@ -15,7 +15,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import math -from typing import NamedTuple from numbers import Number ########################################################################## @@ -27,29 +26,39 @@ # Limited SI prefix set that makes sense for this application PREFIXES = ("", "k", "M", "G") -class Format(NamedTuple): - max_nr_digits: int = 6 - fix_decimals: bool = False - space_str: str = "" - assume_infinity: bool = True - min_offset: int = -8 - max_offset: int = 8 - parse_sloppy_unit: bool = False - parse_sloppy_kilo: bool = False - - -class Value(): +class Format(object): + def __init__(self, + max_nr_digits: int = 6, + fix_decimals: bool = False, + space_str: str = "", + assume_infinity: bool = True, + min_offset: int = -8, + max_offset: int = 8): + assert(min_offset >= -8 and max_offset <= 8 and min_offset < max_offset) + self.max_nr_digits = max_nr_digits + self.fix_decimals = fix_decimals + self.space_str = space_str + self.assume_infinity = assume_infinity + self.min_offset = min_offset + self.max_offset = max_offset + + def __repr__(self): + return (f"{self.__class__.__name__}(" + f"{self.max_nr_digits}, {self.fix_decimals}, " + f"'{self.space_str}', {self.assume_infinity}, " + f"{self.min_offset}, {self.max_offset})") + + +class Value(object): def __init__(self, value: Number = 0, unit: str = "", fmt=Format()): - assert 3 <= fmt.max_nr_digits <= 27 - assert -8 <= fmt.min_offset <= fmt.max_offset <= 8 self.value = value self._unit = unit self.fmt = fmt - def __repr__(self) -> str: + def __repr__(self): return f"{self.__class__.__name__}({self.value}, '{self._unit}', {self.fmt})" - def __str__(self) -> str: + def __str__(self): fmt = self.fmt if fmt.assume_infinity and abs(self.value) >= 10 ** ((fmt.max_offset + 1) * 3): return ("-" if self.value < 0 else "") + "\N{INFINITY}" + fmt.space_str + self._unit @@ -81,18 +90,12 @@ def __str__(self) -> str: return result + fmt.space_str + PREFIXES[offset + 8] + self._unit - def parse(self, value: str) -> float: + def parse(self, value: str): value = value.replace(" ", "") # Ignore spaces - - if self._unit and ( - value.endswith(self._unit) or - (self.fmt.parse_sloppy_unit and - value.lower().endswith(self._unit.lower()))): # strip unit + if self._unit and value.endswith(self._unit) or value.lower().endswith(self._unit.lower()): # strip unit value = value[:-len(self._unit)] factor = 1 - if self.fmt.parse_sloppy_kilo and value[-1] == "K": # fix for e.g. KHz - value = value[:-1] + "k" if value[-1] in PREFIXES: factor = 10 ** ((PREFIXES.index(value[-1])) * 3) value = value[:-1] @@ -101,15 +104,8 @@ def parse(self, value: str) -> float: factor = 10 ** ((PREFIXES.index(value[-1].lower())) * 3) value = value[:-1] self.value = float(value) * factor - - if self.fmt.assume_infinity and value == "\N{INFINITY}": - self.value = math.inf - elif self.fmt.assume_infinity and value == "-\N{INFINITY}": - self.value = -math.inf - else: - self.value = float(value) * factor return self.value @property - def unit(self) -> str: + def unit(self): return self._unit diff --git a/NanoVNASaver/about.py b/NanoVNASaver/about.py index 69245aa0..56c11dc1 100644 --- a/NanoVNASaver/about.py +++ b/NanoVNASaver/about.py @@ -14,5 +14,5 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -version = '0.2.1alpha' +version = '0.2.0' debug = False From fd10ef0b4dd860737ed023bc6d4e686b1652cb4b Mon Sep 17 00:00:00 2001 From: dhunt1342 Date: Sat, 16 Nov 2019 11:13:28 -0500 Subject: [PATCH 22/84] Revert "Reduce set of legal SI PREFIXES" This reverts commit 7aac6ce2b3dd84fcc3a6a6152788ef9c136105e6. --- NanoVNASaver/SITools.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py index 9705499a..2d3f23b2 100644 --- a/NanoVNASaver/SITools.py +++ b/NanoVNASaver/SITools.py @@ -17,14 +17,9 @@ import math from numbers import Number -########################################################################## -# Full set of SI prefixes -#PREFIXES = ("y", "z", "a", "f", "p", "n", "µ", "m", -# "", "k", "M", "G", "T", "P", "E", "Z", "Y") -########################################################################## +PREFIXES = ("y", "z", "a", "f", "p", "n", "µ", "m", + "", "k", "M", "G", "T", "P", "E", "Z", "Y") -# Limited SI prefix set that makes sense for this application -PREFIXES = ("", "k", "M", "G") class Format(object): def __init__(self, @@ -97,11 +92,11 @@ def parse(self, value: str): factor = 1 if value[-1] in PREFIXES: - factor = 10 ** ((PREFIXES.index(value[-1])) * 3) + factor = 10 ** ((PREFIXES.index(value[-1]) - 8) * 3) value = value[:-1] elif value[-1] == 'K': # Fix for the very common KHz - factor = 10 ** ((PREFIXES.index(value[-1].lower())) * 3) + factor = 10 ** ((PREFIXES.index(value[-1].lower()) - 8) * 3) value = value[:-1] self.value = float(value) * factor return self.value From dcc1800c92914bb97a5786745fc3c7d41239e7f3 Mon Sep 17 00:00:00 2001 From: dhunt1342 Date: Sat, 16 Nov 2019 11:29:25 -0500 Subject: [PATCH 23/84] ReSync to mihtjel ReSync to https://github.com/mihtjel/nanovna-saver.git Development stream --- NanoVNASaver/Chart.py | 22 +++++++++- NanoVNASaver/NanoVNASaver.py | 4 +- NanoVNASaver/RFTools.py | 85 ++++++++++++------------------------ NanoVNASaver/SITools.py | 66 ++++++++++++++-------------- NanoVNASaver/about.py | 2 +- 5 files changed, 83 insertions(+), 96 deletions(-) diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index b1d0dd5a..aad4368c 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -62,6 +62,8 @@ class Chart(QtWidgets.QWidget): draggedBox = False draggedBoxStart = (0, 0) draggedBoxCurrent = (-1, -1) + moveStartX = -1 + moveStartY = -1 isPopout = False popoutRequested = pyqtSignal(object) @@ -204,12 +206,18 @@ def mousePressEvent(self, event: QtGui.QMouseEvent) -> None: if event.buttons() == QtCore.Qt.RightButton: event.ignore() return + elif event.buttons() == QtCore.Qt.MiddleButton: + # Drag event + event.accept() + self.moveStartX = event.x() + self.moveStartY = event.y() + return if event.modifiers() == QtCore.Qt.ShiftModifier: self.draggedMarker = self.getNearestMarker(event.x(), event.y()) elif event.modifiers() == QtCore.Qt.ControlModifier: + event.accept() self.draggedBox = True self.draggedBoxStart = (event.x(), event.y()) - event.accept() return self.mouseMoveEvent(event) @@ -593,6 +601,18 @@ def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None: if a0.buttons() == QtCore.Qt.RightButton: a0.ignore() return + if a0.buttons() == QtCore.Qt.MiddleButton: + # Drag the display + a0.accept() + if self.moveStartX != -1 and self.moveStartY != -1: + dx = self.moveStartX - a0.x() + dy = self.moveStartY - a0.y() + self.zoomTo(self.leftMargin + dx, self.topMargin + dy, + self.leftMargin + self.chartWidth + dx, self.topMargin + self.chartHeight + dy) + + self.moveStartX = a0.x() + self.moveStartY = a0.y() + return if a0.modifiers() == QtCore.Qt.ControlModifier: # Dragging a box if not self.draggedBox: diff --git a/NanoVNASaver/NanoVNASaver.py b/NanoVNASaver/NanoVNASaver.py index 54a31b13..e1ede464 100644 --- a/NanoVNASaver/NanoVNASaver.py +++ b/NanoVNASaver/NanoVNASaver.py @@ -528,7 +528,7 @@ def __init__(self): def rescanSerialPort(self): self.serialPortInput.clear() for port in self.getPort(): - self.serialPortInput.insertItem(1,port) + self.serialPortInput.insertItem(1, port) # Get that windows port @staticmethod @@ -625,7 +625,7 @@ def serialButtonClick(self): def startSerial(self): if self.serialLock.acquire(): - self.serialPort = self.serialPortInput.text() + self.serialPort = self.serialPortInput.currentText() logger.info("Opening serial port %s", self.serialPort) try: self.serial = serial.Serial(port=self.serialPort, baudrate=115200) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index c05ae3f2..c217936e 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -15,6 +15,7 @@ # along with this program. If not, see . import collections import math +from numbers import Number from typing import List from NanoVNASaver.SITools import Value, Format @@ -33,7 +34,7 @@ def normalize50(data: Datapoint): return re50, im50 @staticmethod - def gain(data: Datapoint): + def gain(data: Datapoint) -> float: # re50, im50 = normalize50(data) # Calculate the gain / reflection coefficient # mag = math.sqrt((re50 - 50) * (re50 - 50) + im50 * im50) / \ @@ -43,8 +44,7 @@ def gain(data: Datapoint): mag = math.sqrt(data.re**2 + data.im**2) if mag > 0: return 20 * math.log10(mag) - else: - return 0 + return 0 @staticmethod def qualityFactor(data: Datapoint): @@ -73,7 +73,7 @@ def capacitanceEquivalent(im50, freq) -> str: return "- pF" capacitance = 1 / (freq * 2 * math.pi * im50) return str(Value(-capacitance, "F", Format(max_nr_digits=5, space_str=" "))) - + @staticmethod def inductanceEquivalent(im50, freq) -> str: if freq == 0: @@ -82,85 +82,54 @@ def inductanceEquivalent(im50, freq) -> str: return str(Value(inductance, "H", Format(max_nr_digits=5, space_str=" "))) @staticmethod - def formatFrequency(freq): - return str(Value(freq, "Hz", Format(max_nr_digits=6))) + def formatFrequency(freq: Number) -> str: + return str(Value(freq, "Hz")) @staticmethod - def formatShortFrequency(freq): + def formatShortFrequency(freq: Number) -> str: return str(Value(freq, "Hz", Format(max_nr_digits=4))) @staticmethod - def formatSweepFrequency(freq: int, - mindigits: int = 2, - appendHz: bool = True, - insertSpace: bool = False, - countDot: bool = True, - assumeInfinity: bool = True) -> str: - """ Format frequency with SI prefixes - - mindigits count refers to the number of decimal place digits - that will be shown, padded with zeroes if needed. - """ - freqstr = str(freq) - freqlen = len(freqstr) - - # sanity checks - if freqlen > 15: - if assumeInfinity: - return "\N{INFINITY}" - raise ValueError("Frequency too big. More than 15 digits!") - - if freq < 1: - return " - " + (" " if insertSpace else "") + ("Hz" if appendHz else "") - - si_index = (freqlen - 1) // 3 - dot_pos = freqlen % 3 or 3 - intfstr = freqstr[:dot_pos] - decfstr = freqstr[dot_pos:] - nzdecfstr = decfstr.rstrip('0') - if si_index != 0: - while len(nzdecfstr) < mindigits: - nzdecfstr += '0' - freqstr = intfstr + ("." if len(nzdecfstr) > 0 else "") + nzdecfstr - return freqstr + (" " if insertSpace else "") + PREFIXES[si_index] + ("Hz" if appendHz else "") + def formatSweepFrequency(freq: Number) -> str: + return str(Value(freq, "Hz", Format(max_nr_digits=5))) @staticmethod def parseFrequency(freq: str) -> int: - parser = Value(0, "Hz") + parser = Value(0, "Hz", Format(parse_sloppy_unit=True, parse_sloppy_kilo=True)) try: return round(parser.parse(freq)) except (ValueError, IndexError): return -1 @staticmethod - def phaseAngle(data: Datapoint): + def phaseAngle(data: Datapoint) -> float: re = data.re im = data.im return math.degrees(math.atan2(im, re)) @staticmethod - def phaseAngleRadians(data: Datapoint): + def phaseAngleRadians(data: Datapoint) -> float: re = data.re im = data.im return math.atan2(im, re) + @staticmethod + def clamp_int(value: int, min: int, max: int) -> int: + assert min <= max + if value < min: + return min + if value > max: + return max + return value + @staticmethod def groupDelay(data: List[Datapoint], index: int) -> float: - if index == 0: - angle0 = RFTools.phaseAngleRadians(data[0]) - angle1 = RFTools.phaseAngleRadians(data[1]) - freq0 = data[0].freq - freq1 = data[1].freq - elif index == len(data) - 1: - angle0 = RFTools.phaseAngleRadians(data[-2]) - angle1 = RFTools.phaseAngleRadians(data[-1]) - freq0 = data[-2].freq - freq1 = data[-1].freq - else: - angle0 = RFTools.phaseAngleRadians(data[index-1]) - angle1 = RFTools.phaseAngleRadians(data[index+1]) - freq0 = data[index-1].freq - freq1 = data[index+1].freq + index0 = RFTools.clamp_int(index - 1, 0, len(data) - 1) + index1 = RFTools.clamp_int(index + 1, 0, len(data) - 1) + angle0 = RFTools.phaseAngleRadians(data[index0]) + angle1 = RFTools.phaseAngleRadians(data[index1]) + freq0 = data[index0].freq + freq1 = data[index1].freq delta_angle = (angle1 - angle0) if abs(delta_angle) > math.tau: if delta_angle > 0: diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py index 2d3f23b2..45d5e518 100644 --- a/NanoVNASaver/SITools.py +++ b/NanoVNASaver/SITools.py @@ -15,45 +15,35 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import math +from typing import NamedTuple from numbers import Number PREFIXES = ("y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y") +class Format(NamedTuple): + max_nr_digits: int = 6 + fix_decimals: bool = False + space_str: str = "" + assume_infinity: bool = True + min_offset: int = -8 + max_offset: int = 8 + parse_sloppy_unit: bool = False + parse_sloppy_kilo: bool = False -class Format(object): - def __init__(self, - max_nr_digits: int = 6, - fix_decimals: bool = False, - space_str: str = "", - assume_infinity: bool = True, - min_offset: int = -8, - max_offset: int = 8): - assert(min_offset >= -8 and max_offset <= 8 and min_offset < max_offset) - self.max_nr_digits = max_nr_digits - self.fix_decimals = fix_decimals - self.space_str = space_str - self.assume_infinity = assume_infinity - self.min_offset = min_offset - self.max_offset = max_offset - - def __repr__(self): - return (f"{self.__class__.__name__}(" - f"{self.max_nr_digits}, {self.fix_decimals}, " - f"'{self.space_str}', {self.assume_infinity}, " - f"{self.min_offset}, {self.max_offset})") - - -class Value(object): + +class Value(): def __init__(self, value: Number = 0, unit: str = "", fmt=Format()): + assert 3 <= fmt.max_nr_digits <= 27 + assert -8 <= fmt.min_offset <= fmt.max_offset <= 8 self.value = value self._unit = unit self.fmt = fmt - def __repr__(self): + def __repr__(self) -> str: return f"{self.__class__.__name__}({self.value}, '{self._unit}', {self.fmt})" - def __str__(self): + def __str__(self) -> str: fmt = self.fmt if fmt.assume_infinity and abs(self.value) >= 10 ** ((fmt.max_offset + 1) * 3): return ("-" if self.value < 0 else "") + "\N{INFINITY}" + fmt.space_str + self._unit @@ -85,22 +75,30 @@ def __str__(self): return result + fmt.space_str + PREFIXES[offset + 8] + self._unit - def parse(self, value: str): + def parse(self, value: str) -> float: value = value.replace(" ", "") # Ignore spaces - if self._unit and value.endswith(self._unit) or value.lower().endswith(self._unit.lower()): # strip unit + + if self._unit and ( + value.endswith(self._unit) or + (self.fmt.parse_sloppy_unit and + value.lower().endswith(self._unit.lower()))): # strip unit value = value[:-len(self._unit)] factor = 1 + if self.fmt.parse_sloppy_kilo and value[-1] == "K": # fix for e.g. KHz + value = value[:-1] + "k" if value[-1] in PREFIXES: factor = 10 ** ((PREFIXES.index(value[-1]) - 8) * 3) value = value[:-1] - elif value[-1] == 'K': - # Fix for the very common KHz - factor = 10 ** ((PREFIXES.index(value[-1].lower()) - 8) * 3) - value = value[:-1] - self.value = float(value) * factor + + if self.fmt.assume_infinity and value == "\N{INFINITY}": + self.value = math.inf + elif self.fmt.assume_infinity and value == "-\N{INFINITY}": + self.value = -math.inf + else: + self.value = float(value) * factor return self.value @property - def unit(self): + def unit(self) -> str: return self._unit diff --git a/NanoVNASaver/about.py b/NanoVNASaver/about.py index 56c11dc1..69245aa0 100644 --- a/NanoVNASaver/about.py +++ b/NanoVNASaver/about.py @@ -14,5 +14,5 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -version = '0.2.0' +version = '0.2.1alpha' debug = False From 1aa4e22d5a437d4e91ad71ecfe86155046845449 Mon Sep 17 00:00:00 2001 From: dhunt1342 Date: Sat, 16 Nov 2019 11:49:59 -0500 Subject: [PATCH 24/84] Modified unittests to match regression behavior Modified unittests to match regression behavior. There appears to be a loss of accuracy regression in the formatSweepFrequency changes. --- test/test_formatSweepFrequency.py | 32 +++++++++++++++++-------------- test/test_parseFrequency.py | 26 ++++++++++++++----------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/test/test_formatSweepFrequency.py b/test/test_formatSweepFrequency.py index 4a6de5fc..4225be81 100644 --- a/test/test_formatSweepFrequency.py +++ b/test/test_formatSweepFrequency.py @@ -24,20 +24,12 @@ class TestCases(unittest.TestCase): - ''' - def formatSweepFrequency(freq: int, - mindigits: int = 2, - appendHz: bool = True, - insertSpace: bool = False, - countDot: bool = True, - assumeInfinity: bool = True) -> str: - ''' - def test_basicIntegerValues(self): # simple well-formed integers with no trailing zeros. Most importantly - # even with default mindigits, there is no loss of accuracy in the - # result. Returned values are not truncated if result would lose - # meaningful data. + # there is no loss of accuracy in the result. Returned values are not + # truncated if result would lose meaningful data. + ''' + Original Behavior: self.assertEqual(rft.formatSweepFrequency(1), '1Hz') self.assertEqual(rft.formatSweepFrequency(12), '12Hz') self.assertEqual(rft.formatSweepFrequency(123), '123Hz') @@ -47,7 +39,19 @@ def test_basicIntegerValues(self): self.assertEqual(rft.formatSweepFrequency(1234567), '1.234567MHz') self.assertEqual(rft.formatSweepFrequency(12345678), '12.345678MHz') self.assertEqual(rft.formatSweepFrequency(123456789), '123.456789MHz') + ''' + # New Behavior: results in loss of accuracy again. + self.assertEqual(rft.formatSweepFrequency(1), '1.0000Hz') + self.assertEqual(rft.formatSweepFrequency(12), '12.000Hz') + self.assertEqual(rft.formatSweepFrequency(123), '123.00Hz') + self.assertEqual(rft.formatSweepFrequency(1234), '1.2340kHz') + self.assertEqual(rft.formatSweepFrequency(12345), '12.345kHz') + self.assertEqual(rft.formatSweepFrequency(123456), '123.46kHz') + self.assertEqual(rft.formatSweepFrequency(1234567), '1.2346MHz') + self.assertEqual(rft.formatSweepFrequency(12345678), '12.346MHz') + self.assertEqual(rft.formatSweepFrequency(123456789), '123.46MHz') + ''' def test_defaultMinDigits(self): # simple integers with trailing zeros. # DEFAULT behavior retains 2 digits after the period, mindigits=2. @@ -55,7 +59,7 @@ def test_defaultMinDigits(self): self.assertEqual(rft.formatSweepFrequency(10000), '10.00kHz') self.assertEqual(rft.formatSweepFrequency(100000), '100.00kHz') self.assertEqual(rft.formatSweepFrequency(1000000), '1.00MHz') - + def test_nonDefaultMinDigits(self): # simple integers with trailing zeros. setting mindigit value to something # other than default, where trailing zeros >= mindigits, the number of @@ -77,7 +81,7 @@ def test_nonDefaultMinDigits(self): # 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=10), '1.0000000000kHz') - + ''' if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/test/test_parseFrequency.py b/test/test_parseFrequency.py index baffbeee..8a2e6310 100644 --- a/test/test_parseFrequency.py +++ b/test/test_parseFrequency.py @@ -55,16 +55,12 @@ def test_missingDigitsAfterPeriod(self): def test_unusualSIUnits(self): ####################################################################### - # Original behavior: unusual SI values that were legal, but inappropriate - # for this application provided unexpected outputs. This behavior was - # based on the full set of SI prefixes previously defined in SITools (below). + # Current behavior: unusual SI values that are legal, but inappropriate + # for this application provide unexpected outputs. This behavior is + # based on the FULL set of SI prefixes defined in SITools (below). #PREFIXES = ("y", "z", "a", "f", "p", "n", "µ", "m", # "", "k", "M", "G", "T", "P", "E", "Z", "Y") - # New Behavior: After the reduction of legal SI values defined in SITools - # (below), these now return a -1 failure code. - # PREFIXES = ("", "k", "M", "G") ####################################################################### - ''' self.assertEqual(rft.parseFrequency('123EHz'), 123000000000000000000) self.assertEqual(rft.parseFrequency('123PHz'), 123000000000000000) self.assertEqual(rft.parseFrequency('123THz'), 123000000000000) @@ -77,6 +73,13 @@ def test_unusualSIUnits(self): self.assertEqual(rft.parseFrequency('123pHz'), 0) self.assertEqual(rft.parseFrequency('123yHz'), 0) self.assertEqual(rft.parseFrequency('123zHz'), 0) + + ####################################################################### + # Recommend: Reducing the legal SI values defined in SITools (see + # below). This makes it more likely that typos will result in a -1 + # failure code instead of being interpreted as an SI unit. + # PREFIXES = ("", "k", "M", "G") + ####################################################################### ''' self.assertEqual(rft.parseFrequency('123EHz'), -1) self.assertEqual(rft.parseFrequency('123PHz'), -1) @@ -90,15 +93,15 @@ def test_unusualSIUnits(self): self.assertEqual(rft.parseFrequency('123pHz'), -1) self.assertEqual(rft.parseFrequency('123yHz'), -1) self.assertEqual(rft.parseFrequency('123zHz'), -1) + ''' def test_partialHzText(self): ####################################################################### - # Previous behavior for accidentally missing the H in Hz, resulted in + # The current behavior for accidentally missing the H in Hz, is a # detection of 'z' SI unit (zepto = 10^-21), which then rounded to 0. - # After reduction of legal SI values in SITools, this now returns a -1 - # failure code. + # After reduction of legal SI values in SITools, this would return + # a -1 failure code instead. ####################################################################### - ''' self.assertEqual(rft.parseFrequency('123z'), 0) self.assertEqual(rft.parseFrequency('123.z'), 0) self.assertEqual(rft.parseFrequency('1.23z'), 0) @@ -106,6 +109,7 @@ def test_partialHzText(self): self.assertEqual(rft.parseFrequency('123z'), -1) self.assertEqual(rft.parseFrequency('123.z'), -1) self.assertEqual(rft.parseFrequency('1.23z'), -1) + ''' def test_basicExponentialNotation(self): # check basic exponential notation From f6d614b247b901b04170172c6e253787aaee6996 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Sun, 17 Nov 2019 12:33:31 +0100 Subject: [PATCH 25/84] Replaced RFTools.calculateVSWR by Datapoint method --- NanoVNASaver/Analysis.py | 12 +++--------- NanoVNASaver/Chart.py | 5 ++--- NanoVNASaver/Marker.py | 2 +- NanoVNASaver/NanoVNASaver.py | 2 +- NanoVNASaver/RFTools.py | 6 ++---- 5 files changed, 9 insertions(+), 18 deletions(-) diff --git a/NanoVNASaver/Analysis.py b/NanoVNASaver/Analysis.py index 10816f01..57e045c7 100644 --- a/NanoVNASaver/Analysis.py +++ b/NanoVNASaver/Analysis.py @@ -1030,10 +1030,7 @@ def runAnalysis(self): suffix = "" data = [] for d in self.app.data: - vswr = RFTools.calculateVSWR(d) - if vswr < 1: - vswr = float('inf') - data.append(vswr) + data.append(d.as_vswr()) elif self.rbtn_data_resistance.isChecked(): suffix = " \N{OHM SIGN}" data = [] @@ -1137,7 +1134,7 @@ def runAnalysis(self): if self.rbtn_data_vswr.isChecked(): data = [] for d in self.app.data: - data.append(RFTools.calculateVSWR(d)) + data.append(d.as_vswr()) elif self.rbtn_data_s21_gain.isChecked(): data = [] for d in self.app.data21: @@ -1228,10 +1225,7 @@ def runAnalysis(self): max_dips_shown = 3 data = [] for d in self.app.data: - vswr = RFTools.calculateVSWR(d) - if vswr < 1: - vswr = float('inf') - data.append(vswr) + data.append(d.as_vswr()) # min_idx = np.argmin(data) # # logger.debug("Minimum at %d", min_idx) diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index c3a83a75..8dbe6a80 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -1054,7 +1054,7 @@ def drawValues(self, qp: QtGui.QPainter): minVSWR = 1 maxVSWR = 3 for d in self.data: - vswr = RFTools.calculateVSWR(d) + vswr = d.as_vswr() if vswr > maxVSWR: maxVSWR = vswr maxVSWR = min(self.maxDisplayValue, math.ceil(maxVSWR)) @@ -1136,8 +1136,7 @@ def getYPositionFromValue(self, vswr) -> int: return self.topMargin + round((self.maxVSWR - vswr) / self.span * self.chartHeight) def getYPosition(self, d: Datapoint) -> int: - vswr = RFTools.calculateVSWR(d) - return self.getYPositionFromValue(vswr) + return self.getYPositionFromValue(d.as_vswr()) def valueAtPosition(self, y) -> List[float]: absy = y - self.topMargin diff --git a/NanoVNASaver/Marker.py b/NanoVNASaver/Marker.py index ef79e6db..db3e773f 100644 --- a/NanoVNASaver/Marker.py +++ b/NanoVNASaver/Marker.py @@ -307,7 +307,7 @@ def resetLabels(self): def updateLabels(self, s11data: List[Datapoint], s21data: List[Datapoint]): if self.location != -1: re50, im50 = RFTools.normalize50(s11data[self.location]) - vswr = RFTools.calculateVSWR(s11data[self.location]) + vswr = s11data[self.location].as_vswr() if re50 > 0: rp = (re50 ** 2 + im50 ** 2) / re50 rp = round(rp, 3 - max(0, math.floor(math.log10(abs(rp))))) diff --git a/NanoVNASaver/NanoVNASaver.py b/NanoVNASaver/NanoVNASaver.py index e1ede464..c928bdc4 100644 --- a/NanoVNASaver/NanoVNASaver.py +++ b/NanoVNASaver/NanoVNASaver.py @@ -750,7 +750,7 @@ def dataUpdated(self): min_vswr = 100 min_vswr_freq = -1 for d in self.data: - vswr = RFTools.calculateVSWR(d) + vswr = d.as_vswr() if min_vswr > vswr > 0: min_vswr = vswr min_vswr_freq = d.freq diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index 13e570b7..8a2ecc8e 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -56,6 +56,8 @@ def as_vswr(self) -> float: mag = abs(self.z) if mag == 1: return 1 + elif mag > 1: + return math.inf return (1 + mag) / (1 - mag) def to_impedance(self, ref_impedance: float = 50) -> complex: @@ -98,10 +100,6 @@ def gain(data: Datapoint) -> float: def qualityFactor(data: Datapoint) -> float: return data.to_q_factor() - @staticmethod - def calculateVSWR(data: Datapoint) -> float: - return data.as_vswr() - @staticmethod def capacitanceEquivalent(im50, freq) -> str: if im50 == 0 or freq == 0: From b52fa930a6d898363231c15580b32316f98ed1cb Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Sun, 17 Nov 2019 12:53:59 +0100 Subject: [PATCH 26/84] Make vswr a datapoint property --- NanoVNASaver/Analysis.py | 6 +++--- NanoVNASaver/Chart.py | 4 ++-- NanoVNASaver/Marker.py | 2 +- NanoVNASaver/NanoVNASaver.py | 2 +- NanoVNASaver/RFTools.py | 5 +++-- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/NanoVNASaver/Analysis.py b/NanoVNASaver/Analysis.py index 57e045c7..0b453e7e 100644 --- a/NanoVNASaver/Analysis.py +++ b/NanoVNASaver/Analysis.py @@ -1030,7 +1030,7 @@ def runAnalysis(self): suffix = "" data = [] for d in self.app.data: - data.append(d.as_vswr()) + data.append(d.vswr) elif self.rbtn_data_resistance.isChecked(): suffix = " \N{OHM SIGN}" data = [] @@ -1134,7 +1134,7 @@ def runAnalysis(self): if self.rbtn_data_vswr.isChecked(): data = [] for d in self.app.data: - data.append(d.as_vswr()) + data.append(d.vswr) elif self.rbtn_data_s21_gain.isChecked(): data = [] for d in self.app.data21: @@ -1225,7 +1225,7 @@ def runAnalysis(self): max_dips_shown = 3 data = [] for d in self.app.data: - data.append(d.as_vswr()) + data.append(d.vswr) # min_idx = np.argmin(data) # # logger.debug("Minimum at %d", min_idx) diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index 8dbe6a80..9d195e0a 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -1054,7 +1054,7 @@ def drawValues(self, qp: QtGui.QPainter): minVSWR = 1 maxVSWR = 3 for d in self.data: - vswr = d.as_vswr() + vswr = d.vswr if vswr > maxVSWR: maxVSWR = vswr maxVSWR = min(self.maxDisplayValue, math.ceil(maxVSWR)) @@ -1136,7 +1136,7 @@ def getYPositionFromValue(self, vswr) -> int: return self.topMargin + round((self.maxVSWR - vswr) / self.span * self.chartHeight) def getYPosition(self, d: Datapoint) -> int: - return self.getYPositionFromValue(d.as_vswr()) + return self.getYPositionFromValue(d.vswr) def valueAtPosition(self, y) -> List[float]: absy = y - self.topMargin diff --git a/NanoVNASaver/Marker.py b/NanoVNASaver/Marker.py index db3e773f..b10da545 100644 --- a/NanoVNASaver/Marker.py +++ b/NanoVNASaver/Marker.py @@ -307,7 +307,7 @@ def resetLabels(self): def updateLabels(self, s11data: List[Datapoint], s21data: List[Datapoint]): if self.location != -1: re50, im50 = RFTools.normalize50(s11data[self.location]) - vswr = s11data[self.location].as_vswr() + vswr = s11data[self.location].vswr if re50 > 0: rp = (re50 ** 2 + im50 ** 2) / re50 rp = round(rp, 3 - max(0, math.floor(math.log10(abs(rp))))) diff --git a/NanoVNASaver/NanoVNASaver.py b/NanoVNASaver/NanoVNASaver.py index c928bdc4..771cfc5d 100644 --- a/NanoVNASaver/NanoVNASaver.py +++ b/NanoVNASaver/NanoVNASaver.py @@ -750,7 +750,7 @@ def dataUpdated(self): min_vswr = 100 min_vswr_freq = -1 for d in self.data: - vswr = d.as_vswr() + vswr = d.vswr if min_vswr > vswr > 0: min_vswr = vswr min_vswr_freq = d.freq diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index 8a2ecc8e..c0606dd9 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -51,8 +51,9 @@ def as_gain(self) -> float: if mag > 0: return 20 * math.log10(mag) return 0 - - def as_vswr(self) -> float: + + @property + def vswr(self) -> float: mag = abs(self.z) if mag == 1: return 1 From 2405408eec06399a326e0b81218f10dd0df74fdb Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Sun, 17 Nov 2019 12:55:45 +0100 Subject: [PATCH 27/84] Make gain a datapoint property --- NanoVNASaver/RFTools.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index c0606dd9..2e8865db 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -46,7 +46,8 @@ def phase(self): """ return datapoints phase value """ return cmath.phase(self.z) - def as_gain(self) -> float: + @property + def gain(self) -> float: mag = abs(self.z) if mag > 0: return 20 * math.log10(mag) @@ -95,7 +96,7 @@ def normalize50(data: Datapoint): @staticmethod def gain(data: Datapoint) -> float: - return data.as_gain() + return data.gain @staticmethod def qualityFactor(data: Datapoint) -> float: From 8dd67ac85ec91d6a9d3fc8ef5dbaa840f8eb1c3d Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Sun, 17 Nov 2019 13:51:20 +0100 Subject: [PATCH 28/84] Replaced RFTools.gain by Datapoint property --- NanoVNASaver/Analysis.py | 138 ++++++++++++++--------------------- NanoVNASaver/Chart.py | 12 +-- NanoVNASaver/Marker.py | 6 +- NanoVNASaver/NanoVNASaver.py | 2 +- NanoVNASaver/RFTools.py | 4 - 5 files changed, 65 insertions(+), 97 deletions(-) diff --git a/NanoVNASaver/Analysis.py b/NanoVNASaver/Analysis.py index 0b453e7e..83b11e98 100644 --- a/NanoVNASaver/Analysis.py +++ b/NanoVNASaver/Analysis.py @@ -46,8 +46,8 @@ def calculateRolloff(self, location1, location2): return 0, 0 frequency1 = self.app.data21[location1].freq frequency2 = self.app.data21[location2].freq - gain1 = RFTools.gain(self.app.data21[location1]) - gain2 = RFTools.gain(self.app.data21[location2]) + gain1 = self.app.data21[location1].gain + gain2 = self.app.data21[location2].gain frequency_factor = frequency2 / frequency1 if frequency_factor < 1: frequency_factor = 1 / frequency_factor @@ -105,13 +105,13 @@ def runAnalysis(self): self.result_label.setText("Please place " + self.app.markers[0].name + " in the passband.") return - pass_band_db = RFTools.gain(self.app.data21[pass_band_location]) + pass_band_db = self.app.data21[pass_band_location].gain logger.debug("Initial passband gain: %d", pass_band_db) initial_cutoff_location = -1 for i in range(pass_band_location, len(self.app.data21)): - db = RFTools.gain(self.app.data21[i]) + db = self.app.data21[i].gain if (pass_band_db - db) > 3: # We found a cutoff location initial_cutoff_location = i @@ -126,9 +126,9 @@ def runAnalysis(self): logger.debug("Found initial cutoff frequency at %d", initial_cutoff_frequency) peak_location = -1 - peak_db = RFTools.gain(self.app.data21[initial_cutoff_location]) + peak_db = self.app.data21[initial_cutoff_location].gain for i in range(0, initial_cutoff_location): - db = RFTools.gain(self.app.data21[i]) + db = self.app.data21[i].gain if db > peak_db: peak_db = db peak_location = i @@ -141,14 +141,14 @@ def runAnalysis(self): cutoff_location = -1 pass_band_db = peak_db for i in range(peak_location, len(self.app.data21)): - db = RFTools.gain(self.app.data21[i]) + db = self.app.data21[i].gain if (pass_band_db - db) > 3: # We found the cutoff location cutoff_location = i break cutoff_frequency = self.app.data21[cutoff_location].freq - cutoff_gain = RFTools.gain(self.app.data21[cutoff_location]) - pass_band_db + cutoff_gain = self.app.data21[cutoff_location].gain - pass_band_db if cutoff_gain < -4: logger.debug("Cutoff frequency found at %f dB - insufficient data points for true -3 dB point.", cutoff_gain) @@ -161,7 +161,7 @@ def runAnalysis(self): six_db_location = -1 for i in range(cutoff_location, len(self.app.data21)): - db = RFTools.gain(self.app.data21[i]) + db = self.app.data21[i].gain if (pass_band_db - db) > 6: # We found 6dB location six_db_location = i @@ -175,7 +175,7 @@ def runAnalysis(self): ten_db_location = -1 for i in range(cutoff_location, len(self.app.data21)): - db = RFTools.gain(self.app.data21[i]) + db = self.app.data21[i].gain if (pass_band_db - db) > 10: # We found 6dB location ten_db_location = i @@ -183,7 +183,7 @@ def runAnalysis(self): twenty_db_location = -1 for i in range(cutoff_location, len(self.app.data21)): - db = RFTools.gain(self.app.data21[i]) + db = self.app.data21[i].gain if (pass_band_db - db) > 20: # We found 6dB location twenty_db_location = i @@ -191,7 +191,7 @@ def runAnalysis(self): sixty_db_location = -1 for i in range(six_db_location, len(self.app.data21)): - db = RFTools.gain(self.app.data21[i]) + db = self.app.data21[i].gain if (pass_band_db - db) > 60: # We found 60dB location! Wow. sixty_db_location = i @@ -265,13 +265,13 @@ def runAnalysis(self): self.result_label.setText("Please place " + self.app.markers[0].name + " in the passband.") return - pass_band_db = RFTools.gain(self.app.data21[pass_band_location]) + pass_band_db = self.app.data21[pass_band_location].gain logger.debug("Initial passband gain: %d", pass_band_db) initial_cutoff_location = -1 for i in range(pass_band_location, -1, -1): - db = RFTools.gain(self.app.data21[i]) + db = self.app.data21[i].gain if (pass_band_db - db) > 3: # We found a cutoff location initial_cutoff_location = i @@ -286,10 +286,9 @@ def runAnalysis(self): logger.debug("Found initial cutoff frequency at %d", initial_cutoff_frequency) peak_location = -1 - peak_db = RFTools.gain(self.app.data21[initial_cutoff_location]) + peak_db = self.app.data21[initial_cutoff_location].gain for i in range(len(self.app.data21) - 1, initial_cutoff_location - 1, -1): - db = RFTools.gain(self.app.data21[i]) - if db > peak_db: + if self.app.data21[i].gain > peak_db: peak_db = db peak_location = i @@ -301,14 +300,13 @@ def runAnalysis(self): cutoff_location = -1 pass_band_db = peak_db for i in range(peak_location, -1, -1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 3: + if (pass_band_db - self.app.data21[i].gain) > 3: # We found the cutoff location cutoff_location = i break cutoff_frequency = self.app.data21[cutoff_location].freq - cutoff_gain = RFTools.gain(self.app.data21[cutoff_location]) - pass_band_db + cutoff_gain = self.app.data21[cutoff_location].gain - pass_band_db if cutoff_gain < -4: logger.debug("Cutoff frequency found at %f dB - insufficient data points for true -3 dB point.", cutoff_gain) @@ -322,8 +320,7 @@ def runAnalysis(self): six_db_location = -1 for i in range(cutoff_location, -1, -1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 6: + if (pass_band_db - self.app.data21[i].gain) > 6: # We found 6dB location six_db_location = i break @@ -336,24 +333,21 @@ def runAnalysis(self): ten_db_location = -1 for i in range(cutoff_location, -1, -1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 10: + if (pass_band_db - self.app.data21[i].gain) > 10: # We found 6dB location ten_db_location = i break twenty_db_location = -1 for i in range(cutoff_location, -1, -1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 20: + if (pass_band_db - self.app.data21[i].gain) > 20: # We found 6dB location twenty_db_location = i break sixty_db_location = -1 for i in range(six_db_location, -1, -1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 60: + if (pass_band_db - self.app.data21[i].gain) > 60: # We found 60dB location! Wow. sixty_db_location = i break @@ -469,14 +463,13 @@ def runAnalysis(self): self.result_label.setText("Please place " + self.app.markers[0].name + " in the passband.") return - pass_band_db = RFTools.gain(self.app.data21[pass_band_location]) + pass_band_db = self.app.data21[pass_band_location].gain logger.debug("Initial passband gain: %d", pass_band_db) initial_lower_cutoff_location = -1 for i in range(pass_band_location, -1, -1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 3: + if (pass_band_db - self.app.data21[i].gain) > 3: # We found a cutoff location initial_lower_cutoff_location = i break @@ -491,8 +484,7 @@ def runAnalysis(self): initial_upper_cutoff_location = -1 for i in range(pass_band_location, len(self.app.data21), 1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 3: + if (pass_band_db - self.app.data21[i].gain) > 3: # We found a cutoff location initial_upper_cutoff_location = i break @@ -506,9 +498,9 @@ def runAnalysis(self): logger.debug("Found initial upper cutoff frequency at %d", initial_upper_cutoff_frequency) peak_location = -1 - peak_db = RFTools.gain(self.app.data21[initial_lower_cutoff_location]) + peak_db = self.app.data21[initial_lower_cutoff_location].gain for i in range(initial_lower_cutoff_location, initial_upper_cutoff_location, 1): - db = RFTools.gain(self.app.data21[i]) + db = self.app.data21[i].gain if db > peak_db: peak_db = db peak_location = i @@ -518,14 +510,13 @@ def runAnalysis(self): lower_cutoff_location = -1 pass_band_db = peak_db for i in range(peak_location, -1, -1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 3: + if (pass_band_db - self.app.data21[i].gain) > 3: # We found the cutoff location lower_cutoff_location = i break lower_cutoff_frequency = self.app.data21[lower_cutoff_location].freq - lower_cutoff_gain = RFTools.gain(self.app.data21[lower_cutoff_location]) - pass_band_db + lower_cutoff_gain = self.app.data21[lower_cutoff_location].gain - pass_band_db if lower_cutoff_gain < -4: logger.debug("Lower cutoff frequency found at %f dB - insufficient data points for true -3 dB point.", @@ -542,14 +533,13 @@ def runAnalysis(self): upper_cutoff_location = -1 pass_band_db = peak_db for i in range(peak_location, len(self.app.data21), 1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 3: + if (pass_band_db - self.app.data21[i].gain) > 3: # We found the cutoff location upper_cutoff_location = i break upper_cutoff_frequency = self.app.data21[upper_cutoff_location].freq - upper_cutoff_gain = RFTools.gain(self.app.data21[upper_cutoff_location]) - pass_band_db + upper_cutoff_gain = self.app.data21[upper_cutoff_location].gain - pass_band_db if upper_cutoff_gain < -4: logger.debug("Upper cutoff frequency found at %f dB - insufficient data points for true -3 dB point.", upper_cutoff_gain) @@ -576,8 +566,7 @@ def runAnalysis(self): lower_six_db_location = -1 for i in range(lower_cutoff_location, -1, -1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 6: + if (pass_band_db - self.app.data21[i].gain) > 6: # We found 6dB location lower_six_db_location = i break @@ -590,24 +579,21 @@ def runAnalysis(self): ten_db_location = -1 for i in range(lower_cutoff_location, -1, -1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 10: + if (pass_band_db - self.app.data21[i].gain) > 10: # We found 6dB location ten_db_location = i break twenty_db_location = -1 for i in range(lower_cutoff_location, -1, -1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 20: + if (pass_band_db - self.app.data21[i].gain) > 20: # We found 6dB location twenty_db_location = i break sixty_db_location = -1 for i in range(lower_six_db_location, -1, -1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 60: + if (pass_band_db - self.app.data21[i].gain) > 60: # We found 60dB location! Wow. sixty_db_location = i break @@ -636,8 +622,7 @@ def runAnalysis(self): upper_six_db_location = -1 for i in range(upper_cutoff_location, len(self.app.data21), 1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 6: + if (pass_band_db - self.app.data21[i].gain) > 6: # We found 6dB location upper_six_db_location = i break @@ -654,24 +639,21 @@ def runAnalysis(self): ten_db_location = -1 for i in range(upper_cutoff_location, len(self.app.data21), 1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 10: + if (pass_band_db - self.app.data21[i].gain) > 10: # We found 6dB location ten_db_location = i break twenty_db_location = -1 for i in range(upper_cutoff_location, len(self.app.data21), 1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 20: + if (pass_band_db - self.app.data21[i].gain) > 20: # We found 6dB location twenty_db_location = i break sixty_db_location = -1 for i in range(upper_six_db_location, len(self.app.data21), 1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 60: + if (pass_band_db - self.app.data21[i].gain) > 60: # We found 60dB location! Wow. sixty_db_location = i break @@ -782,9 +764,9 @@ def runAnalysis(self): return peak_location = -1 - peak_db = RFTools.gain(self.app.data21[0]) + peak_db = self.app.data21[0].gain for i in range(len(self.app.data21)): - db = RFTools.gain(self.app.data21[i]) + db = self.app.data21[i].gain if db > peak_db: peak_db = db peak_location = i @@ -794,14 +776,13 @@ def runAnalysis(self): lower_cutoff_location = -1 pass_band_db = peak_db for i in range(len(self.app.data21)): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 3: + if (pass_band_db - self.app.data21[i].gain) > 3: # We found the cutoff location lower_cutoff_location = i break lower_cutoff_frequency = self.app.data21[lower_cutoff_location].freq - lower_cutoff_gain = RFTools.gain(self.app.data21[lower_cutoff_location]) - pass_band_db + lower_cutoff_gain = self.app.data21[lower_cutoff_location].gain - pass_band_db if lower_cutoff_gain < -4: logger.debug("Lower cutoff frequency found at %f dB - insufficient data points for true -3 dB point.", @@ -817,14 +798,13 @@ def runAnalysis(self): upper_cutoff_location = -1 for i in range(len(self.app.data21)-1, -1, -1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 3: + if (pass_band_db - self.app.data21[i].gain) > 3: # We found the cutoff location upper_cutoff_location = i break upper_cutoff_frequency = self.app.data21[upper_cutoff_location].freq - upper_cutoff_gain = RFTools.gain(self.app.data21[upper_cutoff_location]) - pass_band_db + upper_cutoff_gain = self.app.data21[upper_cutoff_location].gain - pass_band_db if upper_cutoff_gain < -4: logger.debug("Upper cutoff frequency found at %f dB - insufficient data points for true -3 dB point.", upper_cutoff_gain) @@ -851,8 +831,7 @@ def runAnalysis(self): lower_six_db_location = -1 for i in range(lower_cutoff_location, len(self.app.data21)): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 6: + if (pass_band_db - self.app.data21[i].gain) > 6: # We found 6dB location lower_six_db_location = i break @@ -865,24 +844,21 @@ def runAnalysis(self): ten_db_location = -1 for i in range(lower_cutoff_location, len(self.app.data21)): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 10: + if (pass_band_db - self.app.data21[i].gain) > 10: # We found 6dB location ten_db_location = i break twenty_db_location = -1 for i in range(lower_cutoff_location, len(self.app.data21)): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 20: + if (pass_band_db - self.app.data21[i].gain) > 20: # We found 6dB location twenty_db_location = i break sixty_db_location = -1 for i in range(lower_six_db_location, len(self.app.data21)): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 60: + if (pass_band_db - self.app.data21[i].gain) > 60: # We found 60dB location! Wow. sixty_db_location = i break @@ -910,8 +886,7 @@ def runAnalysis(self): upper_six_db_location = -1 for i in range(upper_cutoff_location, -1, -1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 6: + if (pass_band_db - self.app.data21[i].gain) > 6: # We found 6dB location upper_six_db_location = i break @@ -928,24 +903,21 @@ def runAnalysis(self): ten_db_location = -1 for i in range(upper_cutoff_location, -1, -1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 10: + if (pass_band_db - self.app.data21[i].gain) > 10: # We found 6dB location ten_db_location = i break twenty_db_location = -1 for i in range(upper_cutoff_location, -1, -1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 20: + if (pass_band_db - self.app.data21[i].gain) > 20: # We found 6dB location twenty_db_location = i break sixty_db_location = -1 for i in range(upper_six_db_location, -1, -1): - db = RFTools.gain(self.app.data21[i]) - if (pass_band_db - db) > 60: + if (pass_band_db - self.app.data21[i].gain) > 60: # We found 60dB location! Wow. sixty_db_location = i break @@ -1047,7 +1019,7 @@ def runAnalysis(self): suffix = " dB" data = [] for d in self.app.data21: - data.append(RFTools.gain(d)) + data.append(d.gain) else: logger.warning("Searching for peaks on unknown data") return @@ -1138,7 +1110,7 @@ def runAnalysis(self): elif self.rbtn_data_s21_gain.isChecked(): data = [] for d in self.app.data21: - data.append(RFTools.gain(d)) + data.append(d.gain) else: logger.warning("Searching for peaks on unknown data") return diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index 9d195e0a..d02902e0 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -1609,9 +1609,9 @@ def valueAtPosition(self, y) -> List[float]: def logMag(self, p: Datapoint) -> float: if self.isInverted: - return -RFTools.gain(p) + return -p.gain else: - return RFTools.gain(p) + return p.gain def copy(self): new_chart: LogMagChart = super().copy() @@ -1760,9 +1760,9 @@ def valueAtPosition(self, y) -> List[float]: def logMag(self, p: Datapoint) -> float: if self.isInverted: - return -RFTools.gain(p) + return -p.gain else: - return RFTools.gain(p) + return p.gain def copy(self): new_chart: LogMagChart = super().copy() @@ -2019,9 +2019,9 @@ def valueAtPosition(self, y) -> List[float]: def logMag(self, p: Datapoint) -> float: if self.isInverted: - return -RFTools.gain(p) + return -p.gain else: - return RFTools.gain(p) + return p.gain def copy(self): new_chart: LogMagChart = super().copy() diff --git a/NanoVNASaver/Marker.py b/NanoVNASaver/Marker.py index b10da545..6ebcaba1 100644 --- a/NanoVNASaver/Marker.py +++ b/NanoVNASaver/Marker.py @@ -362,9 +362,9 @@ def updateLabels(self, s11data: List[Datapoint], s21data: List[Datapoint]): self.parallel_r_label.setText(rpstr + " \N{OHM SIGN}") self.parallel_x_label.setText(xpstr) if self.returnloss_is_positive: - returnloss = -round(RFTools.gain(s11data[self.location]), 3) + returnloss = -round(s11data[self.location].gain, 3) else: - returnloss = round(RFTools.gain(s11data[self.location]), 3) + returnloss = round(s11data[self.location].gain, 3) self.returnloss_label.setText(str(returnloss) + " dB") capacitance = RFTools.capacitanceEquivalent(im50, s11data[self.location].freq) inductance = RFTools.inductanceEquivalent(im50, s11data[self.location].freq) @@ -398,7 +398,7 @@ def updateLabels(self, s11data: List[Datapoint], s21data: List[Datapoint]): self.s11_group_delay_label.setText(str(SITools.Value(RFTools.groupDelay(s11data, self.location), "s", fmt))) if len(s21data) == len(s11data): - self.gain_label.setText(str(round(RFTools.gain(s21data[self.location]), 3)) + " dB") + self.gain_label.setText(str(round(s21data[self.location].gain, 3)) + " dB") self.s21_phase_label.setText( str(round(math.degrees(s21data[self.location].phase), 2)) + "\N{DEGREE SIGN}") self.s21_group_delay_label.setText(str(SITools.Value(RFTools.groupDelay(s21data, self.location) / 2, diff --git a/NanoVNASaver/NanoVNASaver.py b/NanoVNASaver/NanoVNASaver.py index 771cfc5d..df258551 100644 --- a/NanoVNASaver/NanoVNASaver.py +++ b/NanoVNASaver/NanoVNASaver.py @@ -770,7 +770,7 @@ def dataUpdated(self): max_gain = -100 max_gain_freq = -1 for d in self.data21: - gain = RFTools.gain(d) + gain = d.gain if gain > max_gain: max_gain = gain max_gain_freq = d.freq diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index 2e8865db..8506f266 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -94,10 +94,6 @@ def normalize50(data: Datapoint): result = data.to_impedance() return result.real, result.imag - @staticmethod - def gain(data: Datapoint) -> float: - return data.gain - @staticmethod def qualityFactor(data: Datapoint) -> float: return data.to_q_factor() From 10399ad8c4c3646573128872bb819fe68e7e1670 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Sun, 17 Nov 2019 14:02:12 +0100 Subject: [PATCH 29/84] Replaced RFTools.qualityFactor by q_factor method --- NanoVNASaver/Chart.py | 4 ++-- NanoVNASaver/Marker.py | 2 +- NanoVNASaver/RFTools.py | 6 +----- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index d02902e0..c7497efa 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -2073,7 +2073,7 @@ def drawChart(self, qp: QtGui.QPainter): minQ = 0 maxQ = 0 for d in self.data: - Q = RFTools.qualityFactor(d) + Q = d.q_factor() if Q > maxQ: maxQ = Q scale = 0 @@ -2146,7 +2146,7 @@ def drawValues(self, qp: QtGui.QPainter): self.drawMarkers(qp) def getYPosition(self, d: Datapoint) -> int: - Q = RFTools.qualityFactor(d) + Q = d.q_factor() return self.topMargin + round((self.maxQ - Q) / self.span * self.chartHeight) def valueAtPosition(self, y) -> List[float]: diff --git a/NanoVNASaver/Marker.py b/NanoVNASaver/Marker.py index 6ebcaba1..46fc841b 100644 --- a/NanoVNASaver/Marker.py +++ b/NanoVNASaver/Marker.py @@ -380,7 +380,7 @@ def updateLabels(self, s11data: List[Datapoint], s21data: List[Datapoint]): if vswr < 0: vswr = "-" self.vswr_label.setText(str(vswr)) - q = RFTools.qualityFactor(s11data[self.location]) + q = s11data[self.location].q_factor() if q > 10000 or q < 0: q_str = "\N{INFINITY}" elif q > 1000: diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index 8506f266..844618b2 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -65,7 +65,7 @@ def vswr(self) -> float: def to_impedance(self, ref_impedance: float = 50) -> complex: return ref_impedance * ((-self.z - 1) / (self.z - 1)) - def to_q_factor(self, ref_impedance: float = 50) -> float: + def q_factor(self, ref_impedance: float = 50) -> float: imp = self.to_impedance(ref_impedance) if imp.real == 0.0: return -1 @@ -94,10 +94,6 @@ def normalize50(data: Datapoint): result = data.to_impedance() return result.real, result.imag - @staticmethod - def qualityFactor(data: Datapoint) -> float: - return data.to_q_factor() - @staticmethod def capacitanceEquivalent(im50, freq) -> str: if im50 == 0 or freq == 0: From 6b2506ff4bcb6e774c9f263a4564c1a546c0088a Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Sun, 17 Nov 2019 14:13:37 +0100 Subject: [PATCH 30/84] Replaced RFTools.normalize50 by Datapoint method --- NanoVNASaver/Analysis.py | 6 ++---- NanoVNASaver/Chart.py | 23 +++++++++++++---------- NanoVNASaver/Marker.py | 3 ++- NanoVNASaver/RFTools.py | 13 ++++--------- 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/NanoVNASaver/Analysis.py b/NanoVNASaver/Analysis.py index 83b11e98..d9b6d0fb 100644 --- a/NanoVNASaver/Analysis.py +++ b/NanoVNASaver/Analysis.py @@ -1007,14 +1007,12 @@ def runAnalysis(self): suffix = " \N{OHM SIGN}" data = [] for d in self.app.data: - re, im = RFTools.normalize50(d) - data.append(re) + data.append(d.impedance().real) elif self.rbtn_data_reactance.isChecked(): suffix = " \N{OHM SIGN}" data = [] for d in self.app.data: - re, im = RFTools.normalize50(d) - data.append(im) + data.append(d.impedance().imag) elif self.rbtn_data_s21_gain.isChecked(): suffix = " dB" data = [] diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index c7497efa..892be8c5 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -2580,7 +2580,8 @@ def drawValues(self, qp: QtGui.QPainter): max_real = 0 max_imag = -1000 for d in self.data: - re, im = RFTools.normalize50(d) + imp = d.impedance() + re, im = imp.real, imp.imag if re > max_real: max_real = re if re < min_real: @@ -2592,7 +2593,8 @@ def drawValues(self, qp: QtGui.QPainter): for d in self.reference: # Also check min/max for the reference sweep if d.freq < fstart or d.freq > fstop: continue - re, im = RFTools.normalize50(d) + imp = d.impedance() + re, im = imp.real, imp.imag if re > max_real: max_real = re if re < min_real: @@ -2792,11 +2794,11 @@ def drawValues(self, qp: QtGui.QPainter): self.drawMarker(x, y_im, qp, m.color, self.markers.index(m)+1) def getImYPosition(self, d: Datapoint) -> int: - _, im = RFTools.normalize50(d) + im = d.impedance().imag return self.topMargin + round((self.max_imag - im) / self.span_imag * self.chartHeight) def getReYPosition(self, d: Datapoint) -> int: - re, _ = RFTools.normalize50(d) + re = d.impedance().real return self.topMargin + round((self.max_real - re) / self.span_real * self.chartHeight) def valueAtPosition(self, y) -> List[float]: @@ -3179,8 +3181,7 @@ def valueAtPosition(self, y) -> List[float]: @staticmethod def magnitude(p: Datapoint) -> float: - re, im = RFTools.normalize50(p) - return math.sqrt(re**2 + im**2) + return abs(p.impedance()) def copy(self): new_chart: LogMagChart = super().copy() @@ -3282,7 +3283,8 @@ def drawValues(self, qp: QtGui.QPainter): min_val = 1000 max_val = -1000 for d in self.data: - re, im = RFTools.normalize50(d) + imp = d.impedance() + re, im = imp.real, imp.imag re = re * 10e6 / d.freq im = im * 10e6 / d.freq if re > max_val: @@ -3296,7 +3298,8 @@ def drawValues(self, qp: QtGui.QPainter): for d in self.reference: # Also check min/max for the reference sweep if d.freq < fstart or d.freq > fstop: continue - re, im = RFTools.normalize50(d) + imp = d.impedance() + re, im = imp.real, imp.imag re = re * 10e6 / d.freq im = im * 10e6 / d.freq if re > max_val: @@ -3461,7 +3464,7 @@ def drawValues(self, qp: QtGui.QPainter): self.drawMarker(x, y_im, qp, m.color, self.markers.index(m)+1) def getImYPosition(self, d: Datapoint) -> int: - _, im = RFTools.normalize50(d) + im = d.impedance().imag im = im * 10e6 / d.freq if self.logarithmicY: min_val = self.max - self.span @@ -3474,7 +3477,7 @@ def getImYPosition(self, d: Datapoint) -> int: return self.topMargin + round((self.max - im) / self.span * self.chartHeight) def getReYPosition(self, d: Datapoint) -> int: - re, _ = RFTools.normalize50(d) + re = d.impedance().real re = re * 10e6 / d.freq if self.logarithmicY: min_val = self.max - self.span diff --git a/NanoVNASaver/Marker.py b/NanoVNASaver/Marker.py index 46fc841b..4c77e60b 100644 --- a/NanoVNASaver/Marker.py +++ b/NanoVNASaver/Marker.py @@ -306,7 +306,8 @@ def resetLabels(self): def updateLabels(self, s11data: List[Datapoint], s21data: List[Datapoint]): if self.location != -1: - re50, im50 = RFTools.normalize50(s11data[self.location]) + imp = s11data[self.location].impedance() + re50, im50 = imp.real, imp.imag vswr = s11data[self.location].vswr if re50 > 0: rp = (re50 ** 2 + im50 ** 2) / re50 diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index 844618b2..018177e3 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -62,11 +62,11 @@ def vswr(self) -> float: return math.inf return (1 + mag) / (1 - mag) - def to_impedance(self, ref_impedance: float = 50) -> complex: + def impedance(self, ref_impedance: float = 50) -> complex: return ref_impedance * ((-self.z - 1) / (self.z - 1)) def q_factor(self, ref_impedance: float = 50) -> float: - imp = self.to_impedance(ref_impedance) + imp = self.impedance(ref_impedance) if imp.real == 0.0: return -1 return abs(imp.imag / imp.real) @@ -74,7 +74,7 @@ def q_factor(self, ref_impedance: float = 50) -> float: def to_capacitive_equivalent(self, ref_impedance: float = 50) -> float: if self.freq == 0: return math.inf - imp = self.to_impedance(ref_impedance) + imp = self.impedance(ref_impedance) if imp.imag == 0: return math.inf return -(1 / (self.freq * 2 * math.pi * imp.imag)) @@ -82,18 +82,13 @@ def to_capacitive_equivalent(self, ref_impedance: float = 50) -> float: def to_inductive_equivalent(self, ref_impedance: float = 50) -> float: if self.freq == 0: return math.inf - imp = self.to_impedance(ref_impedance) + imp = self.impedance(ref_impedance) if imp.imag == 0: return 0 return imp.imag * 1 / (self.freq * 2 * math.pi) class RFTools: - @staticmethod - def normalize50(data: Datapoint): - result = data.to_impedance() - return result.real, result.imag - @staticmethod def capacitanceEquivalent(im50, freq) -> str: if im50 == 0 or freq == 0: From 50dcf944b808db0c0d3043f69a424503e897c931 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Sun, 17 Nov 2019 14:42:12 +0100 Subject: [PATCH 31/84] Renamed to_methods to methods --- NanoVNASaver/RFTools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index 018177e3..85abdd26 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -71,7 +71,7 @@ def q_factor(self, ref_impedance: float = 50) -> float: return -1 return abs(imp.imag / imp.real) - def to_capacitive_equivalent(self, ref_impedance: float = 50) -> float: + def capacitive_equivalent(self, ref_impedance: float = 50) -> float: if self.freq == 0: return math.inf imp = self.impedance(ref_impedance) @@ -79,7 +79,7 @@ def to_capacitive_equivalent(self, ref_impedance: float = 50) -> float: return math.inf return -(1 / (self.freq * 2 * math.pi * imp.imag)) - def to_inductive_equivalent(self, ref_impedance: float = 50) -> float: + def inductive_equivalent(self, ref_impedance: float = 50) -> float: if self.freq == 0: return math.inf imp = self.impedance(ref_impedance) From d00ef61aeef0d622c466e35f5083789bf2f464f8 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Sun, 17 Nov 2019 15:56:43 +0100 Subject: [PATCH 32/84] RFTools: renamed clamp function renamed and allow numbers.Real as parameters --- NanoVNASaver/RFTools.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index 85abdd26..2ca1cd59 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -16,18 +16,18 @@ # along with this program. If not, see . import math import cmath -from numbers import Number +from numbers import Number, Real from typing import List, NamedTuple from NanoVNASaver.SITools import Value, Format -def clamp_int(value: int, imin: int, imax: int) -> int: - assert imin <= imax - if value < imin: - return imin - if value > imax: - return imax +def clamp_value(value: Real, rmin: Real, rmax: Real) -> Real: + assert rmin <= rmax + if value < rmin: + return rmin + if value > rmax: + return rmax return value @@ -52,13 +52,13 @@ def gain(self) -> float: if mag > 0: return 20 * math.log10(mag) return 0 - + @property def vswr(self) -> float: mag = abs(self.z) if mag == 1: return 1 - elif mag > 1: + if mag > 1: return math.inf return (1 + mag) / (1 - mag) @@ -125,8 +125,8 @@ def parseFrequency(freq: str) -> int: @staticmethod def groupDelay(data: List[Datapoint], index: int) -> float: - idx0 = clamp_int(index - 1, 0, len(data) - 1) - idx1 = clamp_int(index + 1, 0, len(data) - 1) + idx0 = 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) if abs(delta_angle) > math.tau: if delta_angle > 0: From 28b8db1bfe6fa801ea096c1a6cd9691ebf872cd8 Mon Sep 17 00:00:00 2001 From: "Rune B. Broberg" Date: Sun, 17 Nov 2019 21:45:08 +0100 Subject: [PATCH 33/84] Finished capacitance chart. Added inductance chart. --- NanoVNASaver/Chart.py | 250 ++++++++++++++++++++++++++++++++++- NanoVNASaver/NanoVNASaver.py | 6 +- 2 files changed, 253 insertions(+), 3 deletions(-) diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index c3a83a75..3ff1ae01 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -583,8 +583,8 @@ def zoomTo(self, x1, y1, x2, y2): val2 = self.valueAtPosition(y2) if len(val1) == len(val2) == 1 and val1[0] != val2[0]: - self.minDisplayValue = round(min(val1[0], val2[0]), 2) - self.maxDisplayValue = round(max(val1[0], val2[0]), 2) + self.minDisplayValue = round(min(val1[0], val2[0]), 3) + self.maxDisplayValue = round(max(val1[0], val2[0]), 3) self.setFixedValues(True) freq1 = max(1, self.frequencyAtPosition(x1, limit=False)) @@ -3707,3 +3707,249 @@ def valueAtPosition(self, y) -> List[float]: absy = y - self.topMargin val = -1 * ((absy / self.chartHeight * self.span) - self.maxDelay) return [val] + + +class CapacitanceChart(FrequencyChart): + def __init__(self, name=""): + super().__init__(name) + self.leftMargin = 30 + self.chartWidth = 250 + self.chartHeight = 250 + self.minDisplayValue = 0 + self.maxDisplayValue = 100 + + self.minValue = -1 + self.maxValue = 1 + self.span = 1 + + self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, self.chartHeight + self.topMargin + self.bottomMargin) + self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)) + pal = QtGui.QPalette() + pal.setColor(QtGui.QPalette.Background, self.backgroundColor) + self.setPalette(pal) + self.setAutoFillBackground(True) + + def drawChart(self, qp: QtGui.QPainter): + qp.setPen(QtGui.QPen(self.textColor)) + qp.drawText(3, 15, self.name + " (F)") + qp.setPen(QtGui.QPen(self.foregroundColor)) + qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.chartHeight+5) + qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) + + def drawValues(self, qp: QtGui.QPainter): + if len(self.data) == 0 and len(self.reference) == 0: + return + pen = QtGui.QPen(self.sweepColor) + pen.setWidth(self.pointSize) + line_pen = QtGui.QPen(self.sweepColor) + line_pen.setWidth(self.lineThickness) + highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255)) + highlighter.setWidth(1) + if not self.fixedSpan: + if len(self.data) > 0: + fstart = self.data[0].freq + fstop = self.data[len(self.data)-1].freq + else: + fstart = self.reference[0].freq + fstop = self.reference[len(self.reference) - 1].freq + self.fstart = fstart + self.fstop = fstop + else: + fstart = self.fstart = self.minFrequency + fstop = self.fstop = self.maxFrequency + + # Draw bands if required + if self.bands.enabled: + self.drawBands(qp, fstart, fstop) + + if self.fixedValues: + maxValue = self.maxDisplayValue / 10e11 + minValue = self.minDisplayValue / 10e11 + self.maxValue = maxValue + self.minValue = minValue + else: + # Find scaling + minValue = 1 + maxValue = -1 + for d in self.data: + val = d.to_capacitive_equivalent() + if val > maxValue: + maxValue = val + if val < minValue: + minValue = val + for d in self.reference: # Also check min/max for the reference sweep + if d.freq < self.fstart or d.freq > self.fstop: + continue + val = d.to_capacitive_equivalent() + if val > maxValue: + maxValue = val + if val < minValue: + minValue = val + self.maxValue = maxValue + self.minValue = minValue + + span = maxValue - minValue + if span == 0: + logger.info("Span is zero for CapacitanceChart, setting to a small value.") + span = 1e-15 + self.span = span + + target_ticks = math.floor(self.chartHeight / 60) + fmt = Format(max_nr_digits=3) + for i in range(target_ticks): + val = minValue + (i / target_ticks) * span + y = self.topMargin + round((self.maxValue - val) / self.span * self.chartHeight) + qp.setPen(self.textColor) + if val != minValue: + valstr = str(Value(val, fmt=fmt)) + qp.drawText(3, y + 3, valstr) + qp.setPen(QtGui.QPen(self.foregroundColor)) + qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.chartWidth, y) + + qp.setPen(QtGui.QPen(self.foregroundColor)) + qp.drawLine(self.leftMargin - 5, self.topMargin, + self.leftMargin + self.chartWidth, self.topMargin) + qp.setPen(self.textColor) + qp.drawText(3, self.topMargin + 4, str(Value(maxValue, fmt=fmt))) + qp.drawText(3, self.chartHeight+self.topMargin, str(Value(minValue, fmt=fmt))) + self.drawFrequencyTicks(qp) + + self.drawData(qp, self.data, self.sweepColor) + self.drawData(qp, self.reference, self.referenceColor) + self.drawMarkers(qp) + + def getYPosition(self, d: Datapoint) -> int: + return self.topMargin + round((self.maxValue - d.to_capacitive_equivalent()) / self.span * self.chartHeight) + + def valueAtPosition(self, y) -> List[float]: + absy = y - self.topMargin + val = -1 * ((absy / self.chartHeight * self.span) - self.maxValue) + return [val * 10e11] + + def copy(self): + new_chart: CapacitanceChart = super().copy() + new_chart.span = self.span + return new_chart + + +class InductanceChart(FrequencyChart): + def __init__(self, name=""): + super().__init__(name) + self.leftMargin = 30 + self.chartWidth = 250 + self.chartHeight = 250 + self.minDisplayValue = 0 + self.maxDisplayValue = 100 + + self.minValue = -1 + self.maxValue = 1 + self.span = 1 + + self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, self.chartHeight + self.topMargin + self.bottomMargin) + self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)) + pal = QtGui.QPalette() + pal.setColor(QtGui.QPalette.Background, self.backgroundColor) + self.setPalette(pal) + self.setAutoFillBackground(True) + + def drawChart(self, qp: QtGui.QPainter): + qp.setPen(QtGui.QPen(self.textColor)) + qp.drawText(3, 15, self.name + " (H)") + qp.setPen(QtGui.QPen(self.foregroundColor)) + qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.chartHeight+5) + qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) + + def drawValues(self, qp: QtGui.QPainter): + if len(self.data) == 0 and len(self.reference) == 0: + return + pen = QtGui.QPen(self.sweepColor) + pen.setWidth(self.pointSize) + line_pen = QtGui.QPen(self.sweepColor) + line_pen.setWidth(self.lineThickness) + highlighter = QtGui.QPen(QtGui.QColor(20, 0, 255)) + highlighter.setWidth(1) + if not self.fixedSpan: + if len(self.data) > 0: + fstart = self.data[0].freq + fstop = self.data[len(self.data)-1].freq + else: + fstart = self.reference[0].freq + fstop = self.reference[len(self.reference) - 1].freq + self.fstart = fstart + self.fstop = fstop + else: + fstart = self.fstart = self.minFrequency + fstop = self.fstop = self.maxFrequency + + # Draw bands if required + if self.bands.enabled: + self.drawBands(qp, fstart, fstop) + + if self.fixedValues: + maxValue = self.maxDisplayValue / 10e11 + minValue = self.minDisplayValue / 10e11 + self.maxValue = maxValue + self.minValue = minValue + else: + # Find scaling + minValue = 1 + maxValue = -1 + for d in self.data: + val = d.to_inductive_equivalent() + if val > maxValue: + maxValue = val + if val < minValue: + minValue = val + for d in self.reference: # Also check min/max for the reference sweep + if d.freq < self.fstart or d.freq > self.fstop: + continue + val = d.to_inductive_equivalent() + if val > maxValue: + maxValue = val + if val < minValue: + minValue = val + self.maxValue = maxValue + self.minValue = minValue + + span = maxValue - minValue + if span == 0: + logger.info("Span is zero for CapacitanceChart, setting to a small value.") + span = 1e-15 + self.span = span + + target_ticks = math.floor(self.chartHeight / 60) + fmt = Format(max_nr_digits=3) + for i in range(target_ticks): + val = minValue + (i / target_ticks) * span + y = self.topMargin + round((self.maxValue - val) / self.span * self.chartHeight) + qp.setPen(self.textColor) + if val != minValue: + valstr = str(Value(val, fmt=fmt)) + qp.drawText(3, y + 3, valstr) + qp.setPen(QtGui.QPen(self.foregroundColor)) + qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.chartWidth, y) + + qp.setPen(QtGui.QPen(self.foregroundColor)) + qp.drawLine(self.leftMargin - 5, self.topMargin, + self.leftMargin + self.chartWidth, self.topMargin) + qp.setPen(self.textColor) + qp.drawText(3, self.topMargin + 4, str(Value(maxValue, fmt=fmt))) + qp.drawText(3, self.chartHeight+self.topMargin, str(Value(minValue, fmt=fmt))) + self.drawFrequencyTicks(qp) + + self.drawData(qp, self.data, self.sweepColor) + self.drawData(qp, self.reference, self.referenceColor) + self.drawMarkers(qp) + + def getYPosition(self, d: Datapoint) -> int: + return self.topMargin + round((self.maxValue - d.to_inductive_equivalent()) / self.span * self.chartHeight) + + def valueAtPosition(self, y) -> List[float]: + absy = y - self.topMargin + val = -1 * ((absy / self.chartHeight * self.span) - self.maxValue) + return [val * 10e11] + + def copy(self): + new_chart: InductanceChart = super().copy() + new_chart.span = self.span + return new_chart diff --git a/NanoVNASaver/NanoVNASaver.py b/NanoVNASaver/NanoVNASaver.py index e1ede464..412daf84 100644 --- a/NanoVNASaver/NanoVNASaver.py +++ b/NanoVNASaver/NanoVNASaver.py @@ -32,7 +32,7 @@ from .RFTools import RFTools, Datapoint from .Chart import Chart, PhaseChart, VSWRChart, PolarChart, SmithChart, LogMagChart, QualityFactorChart, TDRChart, \ RealImaginaryChart, MagnitudeChart, MagnitudeZChart, CombinedLogMagChart, SParameterChart, PermeabilityChart, \ - GroupDelayChart + GroupDelayChart, CapacitanceChart, InductanceChart from .Calibration import CalibrationWindow, Calibration from .Marker import Marker from .SweepWorker import SweepWorker @@ -135,6 +135,8 @@ def __init__(self): self.s11Phase = PhaseChart("S11 Phase") self.s21Phase = PhaseChart("S21 Phase") self.s11GroupDelay = GroupDelayChart("S11 Group Delay") + self.s11CapacitanceChart = CapacitanceChart("S11 Serial C") + self.s11InductanceChart = InductanceChart("S11 Serial L") self.s21GroupDelay = GroupDelayChart("S21 Group Delay", reflective=False) self.permabilityChart = PermeabilityChart("S11 R/\N{GREEK SMALL LETTER OMEGA} & X/\N{GREEK SMALL LETTER OMEGA}") self.s11VSWR = VSWRChart("S11 VSWR") @@ -156,6 +158,8 @@ def __init__(self): self.s11charts.append(self.s11RealImaginary) self.s11charts.append(self.s11QualityFactor) self.s11charts.append(self.s11SParameterChart) + self.s11charts.append(self.s11CapacitanceChart) + self.s11charts.append(self.s11InductanceChart) self.s11charts.append(self.permabilityChart) # List of all the S21 charts, for selecting From 51059506e0351721cf61110837877b355f9e17d9 Mon Sep 17 00:00:00 2001 From: "Rune B. Broberg" Date: Sun, 17 Nov 2019 22:26:00 +0100 Subject: [PATCH 34/84] Control markers with the arrow keys while a chart is selected. Up and right increase frequency, left and down reduce. --- NanoVNASaver/Chart.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index 3ff1ae01..49f7fba6 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -143,7 +143,7 @@ def setMarkerSize(self, size): self.markerSize = size self.update() - def getActiveMarker(self, event: QtGui.QMouseEvent) -> Marker: + def getActiveMarker(self) -> Marker: if self.draggedMarker is not None: return self.draggedMarker for m in self.markers: @@ -409,6 +409,7 @@ def __init__(self, name): self.action_popout = QtWidgets.QAction("Popout chart") self.action_popout.triggered.connect(lambda: self.popoutRequested.emit(self)) self.menu.addAction(self.action_popout) + self.setFocusPolicy(QtCore.Qt.ClickFocus) def contextMenuEvent(self, event): self.action_set_fixed_start.setText("Start (" + Chart.shortenFrequency(self.minFrequency) + ")") @@ -628,7 +629,7 @@ def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None: return else: a0.accept() - m = self.getActiveMarker(a0) + m = self.getActiveMarker() if m is not None: m.setFrequency(str(f)) m.frequencyInput.setText(str(f)) @@ -792,6 +793,16 @@ def copy(self): new_chart.action_set_linear_x.setChecked(not self.logarithmicX) return new_chart + def keyPressEvent(self, a0: QtGui.QKeyEvent) -> None: + m = self.getActiveMarker() + if m is not None and a0.modifiers() == QtCore.Qt.NoModifier: + if a0.key() == QtCore.Qt.Key_Down or a0.key() == QtCore.Qt.Key_Left: + m.frequencyInput.keyPressEvent(QtGui.QKeyEvent(a0.type(), QtCore.Qt.Key_Down, a0.modifiers())) + elif a0.key() == QtCore.Qt.Key_Up or a0.key() == QtCore.Qt.Key_Right: + m.frequencyInput.keyPressEvent(QtGui.QKeyEvent(a0.type(), QtCore.Qt.Key_Up, a0.modifiers())) + else: + super().keyPressEvent(a0) + class SquareChart(Chart): def __init__(self, name): @@ -1278,7 +1289,7 @@ def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None: positions.append(math.sqrt((x - thisx)**2 + (y - thisy)**2)) minimum_position = positions.index(min(positions)) - m = self.getActiveMarker(a0) + m = self.getActiveMarker() if m is not None: m.setFrequency(str(round(target[minimum_position].freq))) m.frequencyInput.setText(str(round(target[minimum_position].freq))) @@ -1424,7 +1435,7 @@ def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None: positions.append(math.sqrt((x - thisx)**2 + (y - thisy)**2)) minimum_position = positions.index(min(positions)) - m = self.getActiveMarker(a0) + m = self.getActiveMarker() if m is not None: m.setFrequency(str(round(target[minimum_position].freq))) m.frequencyInput.setText(str(round(target[minimum_position].freq))) From c8afac2dc6a050ef58b81c501ddb5e6d16d11241 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Mon, 18 Nov 2019 08:00:45 +0100 Subject: [PATCH 35/84] Full Precission sweep format --- NanoVNASaver/RFTools.py | 2 +- NanoVNASaver/SITools.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index 13e570b7..21e7d751 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -126,7 +126,7 @@ def formatShortFrequency(freq: Number) -> str: @staticmethod def formatSweepFrequency(freq: Number) -> str: - return str(Value(freq, "Hz", Format(max_nr_digits=5))) + return str(Value(freq, "Hz", Format(max_nr_digits=9, allow_strip=True))) @staticmethod def parseFrequency(freq: str) -> int: diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py index 45d5e518..102cc9fa 100644 --- a/NanoVNASaver/SITools.py +++ b/NanoVNASaver/SITools.py @@ -28,6 +28,7 @@ class Format(NamedTuple): assume_infinity: bool = True min_offset: int = -8 max_offset: int = 8 + allow_strip: bool = False parse_sloppy_unit: bool = False parse_sloppy_kilo: bool = False @@ -73,6 +74,9 @@ def __str__(self) -> str: if float(result) == 0.0: offset = 0 + if self.fmt.allow_strip and "." in result: + result = result.rstrip("0").rstrip(".") + return result + fmt.space_str + PREFIXES[offset + 8] + self._unit def parse(self, value: str) -> float: From ba5da125e247b38375217329dd677f7aebfe7284 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Mon, 18 Nov 2019 23:40:26 +0100 Subject: [PATCH 36/84] Reimplementation of touchstone parser --- NanoVNASaver/Touchstone.py | 196 +++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 108 deletions(-) diff --git a/NanoVNASaver/Touchstone.py b/NanoVNASaver/Touchstone.py index 43949655..784d834b 100644 --- a/NanoVNASaver/Touchstone.py +++ b/NanoVNASaver/Touchstone.py @@ -1,4 +1,5 @@ -# NanoVNASaver - a python program to view and export Touchstone data from a NanoVNA +# NanoVNASaver +# A python program to view and export Touchstone data from a NanoVNA # Copyright (C) 2019. Rune B. Broberg # # This program is free software: you can redistribute it and/or modify @@ -8,137 +9,116 @@ # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theen.DM00296349 # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import collections import logging -import math -import re -from typing import List -from .RFTools import Datapoint +import cmath +from NanoVNASaver.RFTools import Datapoint logger = logging.getLogger(__name__) +class Options: + # Fun fact: In Touchstone 1.1 spec all params are optional unordered. + # Just the line has to start with "#" + UNIT_TO_FACTOR = { + "ghz": 10**9, + "mhz": 10**6, + "khz": 10**3, + "hz": 10**0, + } + + def __init__(self): + # set defaults + self.factor = Options.UNIT_TO_FACTOR["ghz"] + self.parameter = "s" + self.format = "ma" + self.resistance = 50 + + def parse(self, line): + if not line.startswith("#"): + raise TypeError("Not an option line: " + line) + pfact = pparam = pformat = presist = False + params = iter(line[1:].lower().split()) + for p in params: + if p in ("ghz", "mhz", "khz", "hz") and not pfact: + self.factor = Options.UNIT_TO_FACTOR[p] + pfact = True + elif p in "syzgh" and not pparam: + self.parameter = p + pparam = True + elif p in ("ma", "db", "ri") and not pformat: + self.format = p + pformat = True + elif p == "r" and not presist: + self.resistance = int(next(params)) + else: + raise TypeError("Illegial option line: " + line) + + class Touchstone: - s11data: List[Datapoint] = [] - s21data: List[Datapoint] = [] - comments = [] - filename = "" def __init__(self, filename): self.filename = filename - - def load(self): self.s11data = [] self.s21data = [] + self.s12data = [] + self.s22data = [] + self.comments = [] + self.opts = Options() - realimaginary = False - magnitudeangle = False - - factor = 1 - try: - logger.info("Attempting to open file %s", self.filename) - file = open(self.filename, "r") + def load(self): + logger.info("Attempting to open file %s", self.filename) + sdata = [[], [], [], []] # at max 4 data pairs - lines = file.readlines() - parsed_header = False - for line in lines: + with open(self.filename, "r") as file: + for line in file: line = line.strip() if line.startswith("!"): logger.info(line) self.comments.append(line) continue - if line.startswith("#") and not parsed_header: - pattern = "^# (.?HZ) (S )?RI( R 50)?$" - match = re.match(pattern, line.upper()) - if match: - logger.debug("Found header for RealImaginary and %s", match.group(1)) - match = match.group(1) - parsed_header = True - realimaginary = True - if match == "HZ": - factor = 1 - elif match == "KHZ": - factor = 10**3 - elif match == "MHZ": - factor = 10**6 - elif match == "GHZ": - factor = 10**9 - else: - factor = 10**9 # Default Touchstone frequency unit is GHz - continue - - pattern = "^# (.?HZ) (S )?MA( R 50)?$" - match = re.match(pattern, line.upper()) - if match: - logger.debug("Found header for MagnitudeAngle and %s", match.group(1)) - match = match.group(1) - parsed_header = True - magnitudeangle = True - if match == "HZ": - factor = 1 - elif match == "KHZ": - factor = 10**3 - elif match == "MHZ": - factor = 10**6 - elif match == "GHZ": - factor = 10**9 - else: - factor = 10**9 # Default Touchstone frequency unit is GHz - continue - - # else: - # This is some other comment line - logger.debug("Comment line: %s", line) - continue - if not parsed_header: - logger.warning("Read line without having read header: %s", line) + break + self.opts.parse(line) + + prev_freq = prev_len = 0 + for line in file: + # ignore empty lines (even if not specified) + if not line.strip(): continue - try: - if realimaginary: - values = line.split(maxsplit=5) - freq = values[0] - re11 = values[1] - im11 = values[2] - freq = int(float(freq) * factor) - re11 = float(re11) - im11 = float(im11) - self.s11data.append(Datapoint(freq, re11, im11)) - if len(values) > 3: - re21 = values[3] - im21 = values[4] - re21 = float(re21) - im21 = float(im21) - self.s21data.append(Datapoint(freq, re21, im21)) - elif magnitudeangle: - values = line.split(maxsplit=5) - freq = values[0] - mag11 = float(values[1]) - angle11 = float(values[2]) - freq = int(float(freq) * factor) - re11 = float(mag11) * math.cos(math.radians(angle11)) - im11 = float(mag11) * math.sin(math.radians(angle11)) - self.s11data.append(Datapoint(freq, re11, im11)) - if len(values) > 3: - mag21 = float(values[3]) - angle21 = float(values[4]) - re21 = float(mag21) * math.cos(math.radians(angle21)) - im21 = float(mag21) * math.sin(math.radians(angle21)) - self.s21data.append(Datapoint(freq, re21, im21)) - - continue - except ValueError as e: - logger.exception("Failed to parse line: %s (%s)", line, e) - - file.close() - except IOError as e: - logger.exception("Failed to open %s: %s", self.filename, e) - return + # ignore comments at data end + data = line.split('!')[0] + data = data.split() + freq, data = float(data[0]) * self.opts.factor, data[1:] + data_len = len(data) + + # consistency checks + if freq <= prev_freq: + raise TypeError("Frequeny not ascending: " + line) + prev_freq = freq + + if prev_len == 0: + prev_len = data_len + if data_len % 2: + raise TypeError("Data values aren't pairs: " + line) + elif data_len != prev_len: + raise TypeError("Inconsistent number of pairs: " + line) + + data_list = iter(sdata) + vals = iter(data) + for v in vals: + if self.opts.format == "ri": + next(data_list).append( + Datapoint(freq, float(v), float(next(vals)))) + if self.opts.format == "ma": + z = cmath.polar(float(v), float(next(vals))) + next(data_list).append(Datapoint(freq, z.real, z.imag)) + + self.s11data, self.s21data, self.s12data, self.s22data = sdata[:] def setFilename(self, filename): self.filename = filename From d12cdd4b242d37970db7f12258d3613c3d153275 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 19 Nov 2019 09:00:52 +0100 Subject: [PATCH 37/84] added sample touchstone files for test purposes --- test/data/valid_common.s1p | 1011 +++++++++++++++++++++++++++++++++++ test/data/valid_common.s2p | 1027 ++++++++++++++++++++++++++++++++++++ 2 files changed, 2038 insertions(+) create mode 100644 test/data/valid_common.s1p create mode 100644 test/data/valid_common.s2p diff --git a/test/data/valid_common.s1p b/test/data/valid_common.s1p new file mode 100644 index 00000000..a95469fa --- /dev/null +++ b/test/data/valid_common.s1p @@ -0,0 +1,1011 @@ +# Hz S RI R 50 +140000000 -0.720544874 -0.074467673 +140307234 -0.707615315 -0.045678697 +140614468 -0.694235622 -0.017205553 +140921702 -0.679476678 0.011064857 +141228936 -0.662805676 0.037949264 +141536169 -0.645231842 0.06495472 +141843404 -0.625548779 0.090901531 +142150638 -0.605278372 0.116493001 +142457872 -0.583680212 0.140287563 +142765106 -0.560637235 0.16401714 +143072339 -0.536502182 0.186390563 +143379574 -0.50999391 0.206914424 +143686808 -0.482005447 0.226587563 +143994042 -0.451889097 0.244326263 +144301276 -0.412791699 0.258505284 +144608510 -0.381861805 0.270367473 +144915744 -0.35076934 0.280763506 +145222978 -0.320665925 0.290113508 +145530212 -0.291915833 0.298507958 +145837446 -0.266808092 0.307998478 +146144679 -0.244605809 0.320691823 +146451914 -0.22971031 0.338380515 +146759148 -0.225328207 0.36332336 +147066382 -0.220169663 0.38489589 +147373616 -0.207271277 0.40113902 +147680850 -0.190175205 0.413439959 +147988084 -0.167479559 0.422725945 +148295318 -0.141715452 0.428624361 +148602552 -0.114004857 0.432276844 +148909786 -0.086346656 0.434814631 +149217020 -0.057837899 0.435445755 +149524254 -0.030264718 0.434343039 +149831488 -0.001762108 0.431715399 +150138722 0.025084368 0.428845942 +150445956 0.051782496 0.423674762 +150753190 0.078461259 0.417960464 +151060424 0.104382678 0.411199837 +151367658 0.129571959 0.403289407 +151674892 0.15397191 0.395591259 +151982126 0.177955478 0.38656187 +152289359 0.20108807 0.377004921 +152596594 0.22411789 0.36677733 +152903828 0.246041879 0.356630831 +153211062 0.267716497 0.345134258 +153518296 0.289141952 0.333465933 +153825530 0.31033805 0.321257799 +154132764 0.330586194 0.308034658 +154439997 0.351202487 0.294181048 +154747232 0.371071428 0.279891073 +155054467 0.389829933 0.264377415 +155361700 0.408545076 0.24809423 +155668934 0.426588505 0.230902105 +155976168 0.443256765 0.212807908 +156283402 0.459070712 0.194613248 +156590636 0.473812222 0.174680903 +156897870 0.487293988 0.154679819 +157205104 0.499842941 0.134171023 +157512338 0.510887265 0.112559743 +157819572 0.520686388 0.09155514 +158126806 0.528951704 0.070024006 +158434040 0.536567747 0.048550873 +158741274 0.542390346 0.027685804 +159048508 0.547006845 0.006310601 +159355742 0.550576746 -0.014943165 +159662976 0.552930533 -0.034638434 +159970210 0.554484248 -0.054076246 +160277444 0.555763959 -0.07384359 +160584678 0.555409014 -0.092197977 +160891912 0.554677009 -0.109931141 +161199146 0.553250908 -0.127695485 +161506380 0.551547348 -0.144864901 +161813614 0.549216151 -0.161434262 +162120848 0.546656966 -0.17747493 +162428084 0.543926835 -0.19356732 +162735316 0.540142118 -0.20908463 +163042550 0.535726189 -0.224698171 +163349784 0.53119874 -0.239871665 +163657018 0.525409877 -0.254406183 +163964252 0.519489824 -0.267861336 +164271486 0.513450741 -0.281184017 +164578718 0.506054997 -0.294188529 +164885954 0.499395698 -0.307556122 +165193188 0.491569399 -0.319305658 +165500422 0.483663618 -0.330961287 +165807656 0.474808752 -0.341798186 +166114890 0.465727895 -0.352245688 +166422124 0.456238299 -0.361744403 +166729356 0.446757644 -0.370852261 +167036592 0.436648696 -0.378991305 +167343826 0.426689416 -0.38653028 +167651060 0.415621966 -0.393678396 +167958296 0.405458092 -0.399722427 +168265528 0.394803017 -0.40510568 +168572762 0.384611666 -0.409746617 +168879994 0.37447068 -0.413902401 +169187230 0.364109516 -0.417464792 +169494464 0.353996425 -0.420265555 +169801698 0.343396246 -0.422774851 +170108934 0.33343923 -0.425018608 +170416166 0.322917878 -0.426525205 +170723400 0.313114881 -0.426934331 +171030634 0.309383213 -0.428050071 +171337868 0.298936933 -0.427905261 +171645102 0.289812266 -0.426126837 +171952336 0.27962929 -0.424824088 +172259570 0.270215243 -0.422804892 +172566803 0.261616647 -0.419209092 +172874038 0.25302127 -0.415518373 +173181272 0.245926797 -0.410754144 +173488506 0.237932384 -0.406170994 +173795740 0.231718748 -0.40117529 +174102973 0.225590631 -0.39547345 +174410208 0.219842687 -0.390546143 +174717442 0.214152246 -0.384849339 +175024676 0.209022805 -0.378548949 +175331910 0.204317331 -0.371830284 +175639144 0.199363991 -0.365143775 +175946378 0.195393458 -0.357483923 +176253612 0.191851422 -0.349257171 +176560846 0.188828185 -0.341070264 +176868080 0.18664892 -0.331734478 +177175313 0.18539004 -0.322167843 +177482548 0.185844138 -0.312490731 +177789782 0.18688935 -0.302213281 +178097016 0.189018517 -0.292712241 +178404250 0.192159146 -0.283158928 +178711484 0.196074783 -0.273979991 +179018718 0.201207876 -0.265363335 +179325952 0.206594347 -0.257167428 +179633186 0.212825357 -0.248881548 +179940420 0.220210894 -0.24154438 +180247654 0.228592365 -0.234952598 +180554888 0.237354084 -0.229025736 +180862122 0.246295556 -0.224034965 +181169356 0.255830228 -0.219538688 +181476590 0.265077561 -0.216392159 +181783824 0.27433601 -0.213506206 +182091058 0.283642917 -0.211301714 +182398292 0.293163895 -0.209127992 +182705526 0.302429854 -0.207254111 +183012760 0.312017828 -0.205934837 +183319993 0.322525113 -0.204777851 +183627228 0.333259671 -0.204293623 +183934462 0.343680918 -0.20360817 +184241696 0.354926019 -0.203955337 +184548930 0.366088986 -0.204991444 +184856164 0.377985447 -0.206203684 +185163398 0.389825463 -0.208791926 +185470631 0.402032136 -0.212671026 +185777866 0.414779841 -0.216756045 +186085101 0.426767379 -0.221451044 +186392334 0.438592433 -0.22856526 +186699568 0.450236618 -0.234613865 +187006802 0.461347877 -0.242237955 +187314036 0.472004532 -0.251673042 +187621270 0.482712537 -0.26020947 +187928504 0.492527961 -0.269887357 +188235738 0.501537859 -0.280174762 +188542972 0.509818136 -0.291024714 +188850206 0.517757058 -0.302300184 +189157440 0.52482587 -0.313783377 +189464674 0.531294524 -0.325532525 +189771908 0.537709534 -0.337906867 +190079142 0.54349929 -0.350638002 +190386376 0.548171401 -0.362311899 +190693610 0.552814066 -0.375516176 +191000844 0.556566834 -0.38886547 +191308078 0.558765709 -0.40177989 +191615312 0.561678528 -0.414764702 +191922546 0.564038276 -0.428119033 +192229780 0.565467774 -0.44111818 +192537014 0.566401064 -0.454788237 +192844248 0.566089034 -0.467960238 +193151482 0.566060304 -0.481036961 +193458718 0.564627468 -0.493314057 +193765950 0.563714802 -0.505672335 +194073184 0.561375796 -0.518240034 +194380418 0.559619665 -0.530349493 +194687652 0.556529402 -0.542583048 +194994886 0.553792059 -0.554005742 +195302120 0.550337612 -0.565433382 +195609352 0.546528995 -0.576836109 +195916588 0.543008208 -0.588021218 +196223822 0.538507282 -0.598448753 +196531056 0.534669637 -0.608619272 +196838290 0.529401779 -0.618696153 +197145524 0.524888098 -0.628849267 +197452758 0.519340574 -0.6381675 +197759990 0.51428467 -0.647799372 +198067226 0.509456992 -0.657329738 +198374460 0.504129707 -0.665923953 +198681694 0.497905582 -0.674751818 +198988930 0.49236387 -0.683665335 +199296162 0.486705601 -0.69176656 +199603396 0.480667144 -0.699386954 +199910628 0.474548667 -0.708767533 +200217864 0.468046844 -0.716237902 +200525098 0.462222456 -0.724126875 +200832332 0.454748183 -0.731734275 +201139568 0.448005497 -0.738878965 +201446800 0.441546171 -0.745889782 +201754034 0.434442341 -0.752933263 +202061268 0.427109777 -0.759089827 +202368502 0.420044779 -0.764947175 +202675736 0.413361072 -0.771532714 +202982970 0.406063258 -0.776731967 +203290204 0.398535013 -0.782259047 +203597437 0.391966372 -0.787551462 +203904672 0.385206133 -0.793883323 +204211906 0.378553241 -0.79900366 +204519140 0.371784687 -0.804847359 +204826374 0.364233642 -0.810363471 +205133607 0.356939613 -0.815402686 +205440842 0.350186675 -0.821036577 +205748076 0.342950075 -0.82634288 +206055310 0.335152417 -0.831421494 +206362544 0.327318578 -0.836219251 +206669778 0.319360375 -0.841145992 +206977012 0.311830133 -0.845238685 +207284246 0.304142802 -0.850752532 +207591480 0.2953538 -0.854333102 +207898714 0.287017583 -0.858365893 +208205947 0.27873218 -0.862207472 +208513182 0.270723313 -0.865977346 +208820416 0.261890083 -0.869453251 +209127650 0.253588438 -0.873033761 +209434884 0.24545671 -0.875273287 +209742118 0.23610872 -0.878586232 +210049352 0.227947369 -0.881349921 +210356586 0.219101518 -0.883996009 +210663820 0.210906699 -0.885824561 +210971054 0.202103897 -0.887488543 +211278288 0.19392471 -0.890163958 +211585522 0.185131549 -0.891037642 +211892756 0.176955729 -0.892904758 +212199990 0.168666452 -0.894396662 +212507224 0.160335943 -0.895673036 +212814458 0.151759371 -0.896834433 +213121692 0.143479093 -0.897952079 +213428926 0.135391607 -0.899019122 +213736160 0.127160742 -0.899432182 +214043394 0.119095399 -0.900022029 +214350627 0.110525906 -0.901187002 +214657862 0.102360054 -0.900755465 +214965096 0.094007715 -0.900821805 +215272330 0.08556845 -0.901367306 +215579564 0.078083314 -0.900794506 +215886798 0.069483436 -0.900924324 +216194032 0.060808736 -0.900772988 +216501265 0.05260244 -0.899642765 +216808500 0.044649798 -0.89939028 +217115735 0.037751082 -0.897785842 +217422968 0.028944812 -0.89734143 +217730202 0.020439717 -0.89553219 +218037436 0.013162659 -0.89506191 +218344670 0.005409372 -0.893381416 +218651904 -0.001962979 -0.891607344 +218959138 -0.009654009 -0.890131711 +219266372 -0.017304318 -0.887754738 +219573606 -0.025764886 -0.886493444 +219880840 -0.032465111 -0.884340941 +220188074 -0.040296394 -0.882605373 +220495308 -0.047947756 -0.880485355 +220802542 -0.054992813 -0.878214061 +221109776 -0.062826626 -0.875472128 +221417010 -0.069768942 -0.872972548 +221724244 -0.077182918 -0.869929909 +222031478 -0.08534526 -0.866897702 +222338712 -0.092740498 -0.863485753 +222645946 -0.100141502 -0.860659956 +222953180 -0.106707595 -0.856700181 +223260414 -0.113779768 -0.85360521 +223567648 -0.121083952 -0.849183201 +223874882 -0.127620398 -0.845345199 +224182116 -0.135321661 -0.840975701 +224489352 -0.141569435 -0.83622992 +224796584 -0.148180574 -0.831842422 +225103818 -0.154826939 -0.826501727 +225411052 -0.161072537 -0.821584284 +225718286 -0.167469441 -0.816235661 +226025520 -0.173694863 -0.811362147 +226332754 -0.180000245 -0.805815935 +226639986 -0.186006754 -0.799482882 +226947222 -0.191339865 -0.793425858 +227254456 -0.196616813 -0.787111222 +227561690 -0.201544165 -0.780077815 +227868924 -0.206872448 -0.773252606 +228176158 -0.211609557 -0.766366183 +228483392 -0.215882346 -0.759336709 +228790624 -0.219699263 -0.75180602 +229097860 -0.223467424 -0.744185388 +229405094 -0.227245226 -0.736600637 +229712328 -0.230611681 -0.728374361 +230019564 -0.233549714 -0.72003293 +230326796 -0.235507965 -0.711248159 +230634030 -0.236758038 -0.702257454 +230941262 -0.238270252 -0.69286102 +231248498 -0.239042982 -0.684143483 +231555732 -0.237911477 -0.674824655 +231862966 -0.237094283 -0.665688097 +232170202 -0.235474064 -0.655830085 +232477434 -0.232791543 -0.646884024 +232784668 -0.229612424 -0.638507246 +233091902 -0.224113345 -0.631963849 +233399136 -0.21918179 -0.623693287 +233706370 -0.212830424 -0.616634547 +234013604 -0.205752745 -0.609905302 +234320838 -0.197987213 -0.604949891 +234628071 -0.189559578 -0.599981009 +234935306 -0.180442571 -0.597054183 +235242540 -0.170896843 -0.594879806 +235549774 -0.161977976 -0.594342052 +235857008 -0.152213186 -0.595045447 +236164241 -0.142262265 -0.597444474 +236471476 -0.133724287 -0.600611269 +236778710 -0.125034764 -0.60557419 +237085944 -0.117461979 -0.611900568 +237393178 -0.110610842 -0.619200527 +237700412 -0.105227537 -0.627581477 +238007646 -0.100863195 -0.636348545 +238314880 -0.097896151 -0.646345138 +238622114 -0.095635436 -0.656025707 +238929348 -0.095561787 -0.666305959 +239236581 -0.096592918 -0.67661488 +239543816 -0.098119862 -0.686175823 +239851050 -0.101253353 -0.696257948 +240158284 -0.105404153 -0.705455303 +240465518 -0.110330179 -0.714241266 +240772752 -0.116464264 -0.722561419 +241079986 -0.123405188 -0.730498552 +241387220 -0.130726441 -0.737371742 +241694454 -0.138746455 -0.744073808 +242001688 -0.147201955 -0.749473929 +242308922 -0.156216561 -0.754500448 +242616156 -0.165334969 -0.758516132 +242923390 -0.175009936 -0.762610435 +243230624 -0.184407919 -0.765486776 +243537858 -0.19363445 -0.768185973 +243845092 -0.20344977 -0.769399523 +244152326 -0.21360132 -0.770685613 +244459560 -0.223220244 -0.771749913 +244766794 -0.232181563 -0.771442055 +245074028 -0.241754785 -0.771514177 +245381261 -0.25128895 -0.770565211 +245688496 -0.260109722 -0.769354045 +245995730 -0.268368333 -0.767545461 +246302964 -0.277909666 -0.765769898 +246610198 -0.286529839 -0.763796865 +246917432 -0.295122355 -0.760985553 +247224666 -0.303480893 -0.758591711 +247531899 -0.312235832 -0.755390882 +247839134 -0.319880247 -0.752278029 +248146369 -0.328108429 -0.748061418 +248453602 -0.335709244 -0.744457602 +248760836 -0.34345436 -0.74031794 +249068070 -0.350502878 -0.735688447 +249375304 -0.35754776 -0.73139888 +249682538 -0.364448308 -0.726103961 +249989772 -0.37106803 -0.721649825 +250297006 -0.377072513 -0.71627146 +250604240 -0.383415043 -0.711282253 +250911474 -0.388791829 -0.705999314 +251218708 -0.394813567 -0.700864434 +251525942 -0.399501681 -0.695687472 +251833176 -0.404717057 -0.689982414 +252140410 -0.410242348 -0.684521853 +252447644 -0.41471365 -0.679595172 +252754878 -0.419774055 -0.673905491 +253062112 -0.424723714 -0.668620467 +253369346 -0.428615033 -0.66287738 +253676580 -0.433148145 -0.658454656 +253983814 -0.437322437 -0.652969479 +254291048 -0.441554158 -0.6475811 +254598282 -0.445529758 -0.641687631 +254905516 -0.449236214 -0.636736512 +255212750 -0.453501135 -0.631189465 +255519986 -0.457041174 -0.625312328 +255827218 -0.460377812 -0.619865179 +256134452 -0.464220792 -0.614572584 +256441686 -0.467616617 -0.608792841 +256748920 -0.470996528 -0.603281557 +257056154 -0.474337577 -0.597755849 +257363388 -0.477346748 -0.591959834 +257670620 -0.480445891 -0.586885571 +257977856 -0.483592182 -0.580872237 +258285090 -0.486395269 -0.574660658 +258592324 -0.489085644 -0.569250226 +258899558 -0.491889059 -0.563780784 +259206792 -0.494458526 -0.558424353 +259514026 -0.497013032 -0.552385985 +259821258 -0.499607026 -0.546662628 +260128494 -0.502062082 -0.541466295 +260435728 -0.50411123 -0.535668313 +260742962 -0.506370306 -0.529416084 +261050198 -0.508782684 -0.524118483 +261357430 -0.510410964 -0.51803404 +261664664 -0.512022674 -0.51307404 +261971896 -0.514577805 -0.506917893 +262279132 -0.515612304 -0.501558244 +262586366 -0.517577648 -0.495918095 +262893600 -0.519160628 -0.49004969 +263200836 -0.520951509 -0.484939903 +263508068 -0.522050321 -0.47957158 +263815302 -0.523682296 -0.473701477 +264122536 -0.526415109 -0.467507302 +264429770 -0.527692437 -0.46245855 +264737004 -0.529555797 -0.456708699 +265044238 -0.530858218 -0.451504796 +265351472 -0.53281188 -0.44593513 +265658705 -0.534469187 -0.439815819 +265965940 -0.535592317 -0.434991955 +266273174 -0.537013471 -0.428794205 +266580408 -0.53862828 -0.422849088 +266887642 -0.539797782 -0.417023241 +267194875 -0.540962219 -0.411043882 +267502110 -0.541949391 -0.405382543 +267809344 -0.543270707 -0.398819625 +268116578 -0.543647527 -0.393440127 +268423812 -0.544686615 -0.387427806 +268731046 -0.545364916 -0.381478756 +269038280 -0.546309113 -0.375303596 +269345514 -0.547061026 -0.369552075 +269652748 -0.54763776 -0.363328367 +269959982 -0.548439621 -0.357404619 +270267215 -0.548313796 -0.350935876 +270574450 -0.548788011 -0.344784736 +270881684 -0.548901677 -0.339054912 +271188918 -0.549303352 -0.332568466 +271496152 -0.548764169 -0.326193988 +271803386 -0.548359513 -0.319975048 +272110620 -0.54795295 -0.313980281 +272417854 -0.547585844 -0.307839035 +272725088 -0.546915054 -0.30222097 +273032322 -0.546164929 -0.295384138 +273339556 -0.545851647 -0.289677888 +273646790 -0.544476449 -0.283723205 +273954024 -0.543427169 -0.277519553 +274261258 -0.542781591 -0.271449178 +274568492 -0.541737675 -0.26609531 +274875726 -0.540780782 -0.259709119 +275182960 -0.539190292 -0.254450619 +275490194 -0.538588345 -0.248785778 +275797428 -0.537282288 -0.242761164 +276104662 -0.535778522 -0.237283468 +276411895 -0.534473598 -0.23102191 +276719130 -0.533464789 -0.225931107 +277026364 -0.532205164 -0.219861045 +277333598 -0.530566513 -0.213981211 +277640832 -0.52935934 -0.208669379 +277948066 -0.527178764 -0.202768296 +278255300 -0.525767445 -0.197356507 +278562533 -0.524271667 -0.192004665 +278869768 -0.523084104 -0.186119168 +279177003 -0.521032929 -0.180648386 +279484236 -0.519267678 -0.175265178 +279791470 -0.517660856 -0.169159501 +280098704 -0.515894114 -0.163792759 +280405938 -0.514007687 -0.157745704 +280713172 -0.511822521 -0.152312338 +281020406 -0.509251832 -0.14665091 +281327640 -0.506862282 -0.141145691 +281634874 -0.505178868 -0.135393649 +281942108 -0.503098607 -0.130084976 +282249342 -0.500118732 -0.124543815 +282556576 -0.49783945 -0.118902169 +282863810 -0.495216071 -0.113477326 +283171044 -0.492336213 -0.108579479 +283478278 -0.489296227 -0.102683566 +283785512 -0.486703395 -0.097417466 +284092746 -0.48380506 -0.091916061 +284399980 -0.480933099 -0.087093368 +284707214 -0.477742463 -0.081715345 +285014448 -0.474802017 -0.076475627 +285321682 -0.471661686 -0.070840254 +285628916 -0.468887537 -0.065928071 +285936150 -0.465906649 -0.061350177 +286243384 -0.461863756 -0.055866178 +286550620 -0.458607286 -0.05043013 +286857852 -0.455549031 -0.045645032 +287165086 -0.45155555 -0.040882755 +287472320 -0.447465986 -0.035264462 +287779554 -0.443931043 -0.029745355 +288086788 -0.439934641 -0.024774778 +288394022 -0.436197847 -0.020021386 +288701254 -0.431959956 -0.015013962 +289008490 -0.427578032 -0.010605479 +289315724 -0.423828274 -0.005265513 +289622958 -0.418648093 -0.001015597 +289930192 -0.414591252 0.004003537 +290237426 -0.409809559 0.008439064 +290544660 -0.405255675 0.01276547 +290851892 -0.39965409 0.017221221 +291159128 -0.395296752 0.021493824 +291466362 -0.390108168 0.02590033 +291773596 -0.385191023 0.030091928 +292080832 -0.379876852 0.033840294 +292388064 -0.374741733 0.037781257 +292695298 -0.36923623 0.04097012 +293002530 -0.364293336 0.045143079 +293309766 -0.359098225 0.048435423 +293617000 -0.353837192 0.051173914 +293924234 -0.34824723 0.054473843 +294231470 -0.343492746 0.058362241 +294538702 -0.337800383 0.060658514 +294845936 -0.331986874 0.063676476 +295153170 -0.327601045 0.065386958 +295460404 -0.322849333 0.06812565 +295767638 -0.317867189 0.070542186 +296074872 -0.31179279 0.073497921 +296382106 -0.306153357 0.076434701 +296689339 -0.301116794 0.078869417 +296996574 -0.295280784 0.081720538 +297303808 -0.28973931 0.083908028 +297611042 -0.284551739 0.086093887 +297918276 -0.278555184 0.088601596 +298225509 -0.273749947 0.09041997 +298532744 -0.267449796 0.09309598 +298839978 -0.262523293 0.095738738 +299147212 -0.256011009 0.09808503 +299454446 -0.250353157 0.100595057 +299761680 -0.244020372 0.102110177 +300068914 -0.281908929 0.131197333 +300376148 -0.27315706 0.13438782 +300683382 -0.266562372 0.13586533 +300990616 -0.259558349 0.138872325 +301297849 -0.252486258 0.140640765 +301605084 -0.244293302 0.142799153 +301912318 -0.2369657 0.143745228 +302219552 -0.228306725 0.145925 +302526786 -0.220833957 0.145206913 +302834020 -0.213503584 0.147272944 +303141254 -0.205865189 0.148496061 +303448488 -0.198276787 0.149247452 +303755722 -0.191526353 0.149417906 +304062956 -0.183093607 0.148920461 +304370190 -0.175830036 0.1508587 +304677424 -0.168572083 0.149220407 +304984658 -0.161049425 0.149222746 +305291892 -0.153603434 0.149473041 +305599126 -0.146718978 0.149297744 +305906360 -0.139164417 0.149031028 +306213594 -0.13254702 0.148135378 +306520828 -0.125445082 0.147586107 +306828062 -0.118951223 0.147320404 +307135296 -0.112064555 0.147520348 +307442529 -0.105627678 0.14754635 +307749764 -0.097580112 0.145733356 +308056998 -0.091351322 0.144378021 +308364232 -0.084506593 0.143624454 +308671466 -0.077683858 0.142921626 +308978700 -0.070093169 0.14265421 +309285934 -0.063272051 0.141863286 +309593167 -0.056773819 0.140300258 +309900402 -0.049546733 0.139516025 +310207637 -0.042794372 0.138279587 +310514870 -0.03518781 0.135735973 +310822104 -0.028595736 0.134260192 +311129338 -0.02018802 0.13241665 +311436572 -0.013772861 0.130612418 +311743806 -0.007696078 0.127634227 +312051040 -0.000633327 0.1245737 +312358274 0.005943323 0.121017262 +312665508 0.01224513 0.119415938 +312972742 0.019459825 0.115979 +313279976 0.025634812 0.112828984 +313587210 0.031763367 0.110853269 +313894444 0.038404583 0.107830174 +314201678 0.04422805 0.104382388 +314508912 0.05027648 0.101722292 +314816146 0.056206125 0.097607195 +315123380 0.061992473 0.094749055 +315430614 0.068681307 0.091542989 +315737848 0.074524059 0.086819924 +316045082 0.079555816 0.08311633 +316352316 0.085454106 0.078783862 +316659550 0.090403616 0.074620716 +316966784 0.096503347 0.070632793 +317274018 0.101016975 0.066929958 +317581254 0.105501875 0.062584832 +317888486 0.110014006 0.058565329 +318195720 0.114840097 0.054103646 +318502954 0.118642769 0.050576217 +318810188 0.12458942 0.046235065 +319117422 0.128695756 0.041638068 +319424656 0.132498964 0.037589345 +319731888 0.136515364 0.033306751 +320039124 0.14124076 0.02906648 +320346358 0.145149648 0.024618981 +320653592 0.148645013 0.020593402 +320960826 0.151914939 0.016546661 +321268060 0.156172052 0.012489466 +321575294 0.160119265 0.008374655 +321882526 0.164269521 0.004012623 +322189762 0.167267635 -0.000545038 +322496996 0.170979529 -0.004828844 +322804230 0.174135684 -0.009401686 +323111466 0.176585048 -0.014179083 +323418698 0.18071258 -0.018854744 +323725932 0.182732537 -0.023164799 +324033164 0.186408147 -0.027405923 +324340400 0.188379988 -0.031735878 +324647634 0.192742034 -0.035587981 +324954868 0.194542929 -0.040664982 +325262104 0.196586355 -0.043590929 +325569336 0.200475573 -0.047839902 +325876570 0.202873677 -0.052698407 +326183804 0.20554006 -0.055307816 +326491038 0.208009645 -0.059931959 +326798272 0.210224032 -0.063391536 +327105506 0.213449761 -0.067373126 +327412740 0.216203615 -0.072460338 +327719973 0.21844542 -0.075931563 +328027208 0.220860391 -0.080728068 +328334442 0.223240986 -0.08562012 +328641676 0.226203292 -0.088994592 +328948910 0.229381233 -0.093181647 +329256143 0.231664404 -0.096460469 +329563378 0.234412789 -0.103231698 +329870612 0.236873418 -0.106700718 +330177846 0.238100335 -0.112019896 +330485080 0.241145178 -0.116927348 +330792314 0.243046045 -0.122050777 +331099548 0.245411068 -0.12505865 +331406782 0.247035697 -0.131522461 +331714016 0.249161779 -0.137099295 +332021250 0.250358402 -0.141853123 +332328483 0.252388954 -0.147231906 +332635718 0.254583746 -0.149022117 +332942952 0.255740195 -0.154192551 +333250186 0.257969468 -0.159170582 +333557420 0.257919251 -0.163664445 +333864654 0.259370177 -0.168288514 +334171888 0.260117143 -0.17204371 +334479122 0.2607764 -0.176328063 +334786356 0.261568725 -0.180388599 +335093590 0.264311701 -0.185788795 +335400824 0.264785856 -0.189291387 +335708058 0.265216678 -0.194572433 +336015292 0.26612398 -0.197979807 +336322526 0.267038673 -0.202083691 +336629760 0.268535286 -0.206977859 +336936994 0.269487768 -0.210943132 +337244228 0.270255327 -0.213963568 +337551462 0.270424574 -0.219048365 +337858696 0.270450085 -0.225374177 +338165930 0.271297633 -0.227300047 +338473163 0.271307408 -0.232138916 +338780398 0.271877527 -0.235452756 +339087632 0.270323991 -0.239816784 +339394866 0.272010505 -0.245398849 +339702100 0.271954059 -0.246803879 +340009334 0.272577255 -0.249967977 +340316568 0.272408038 -0.253338247 +340623801 0.273320049 -0.257804781 +340931036 0.274579107 -0.262044757 +341238271 0.274721324 -0.262544274 +341545504 0.27578476 -0.270115345 +341852738 0.276025414 -0.274213552 +342159972 0.276624977 -0.276834219 +342467206 0.277802824 -0.281919568 +342774440 0.276326179 -0.286005616 +343081674 0.275987654 -0.289610177 +343388908 0.276774466 -0.2926445 +343696142 0.277043461 -0.298408001 +344003376 0.276031702 -0.300052136 +344310610 0.276601195 -0.302973091 +344617844 0.276540547 -0.308145821 +344925078 0.277645885 -0.311217069 +345232312 0.276989698 -0.314697146 +345539546 0.277318239 -0.317075312 +345846780 0.275987625 -0.318971574 +346154014 0.275542646 -0.323990613 +346461248 0.277501344 -0.32641986 +346768482 0.278523921 -0.330894231 +347075716 0.279843002 -0.33387506 +347382950 0.278705328 -0.33602485 +347690184 0.279899209 -0.340001165 +347997418 0.28029257 -0.341565757 +348304652 0.281575769 -0.347765415 +348611888 0.28198415 -0.350974917 +348919120 0.283674538 -0.354550868 +349226354 0.283723473 -0.35863322 +349533588 0.287379354 -0.36197704 +349840822 0.284759849 -0.366860121 +350148056 0.285806149 -0.372169375 +350455290 0.286550223 -0.376398801 +350762522 0.286723434 -0.379182755 +351069758 0.286611855 -0.383348584 +351376992 0.28499934 -0.388753414 +351684226 0.286077976 -0.390664696 +351991460 0.287443518 -0.396266937 +352298694 0.28818348 -0.400082975 +352605928 0.287856727 -0.403338283 +352913160 0.287033259 -0.410229146 +353220396 0.286989897 -0.414316624 +353527630 0.287218064 -0.419990092 +353834864 0.286724418 -0.421835273 +354142100 0.286426454 -0.42683947 +354449332 0.286111474 -0.431699156 +354756566 0.286174416 -0.437353432 +355063798 0.283983081 -0.442229419 +355371034 0.28545016 -0.44447866 +355678268 0.285756736 -0.450424253 +355985502 0.2851758 -0.457364171 +356292738 0.284931361 -0.460646867 +356599970 0.283026844 -0.464671105 +356907204 0.282563626 -0.469548165 +357214438 0.28590396 -0.476054012 +357521672 0.285185992 -0.483184754 +357828906 0.283355176 -0.488861799 +358136140 0.281841188 -0.493905544 +358443374 0.280294477 -0.499854564 +358750607 0.278184622 -0.503504872 +359057842 0.274967044 -0.509154498 +359365076 0.273747742 -0.512856364 +359672310 0.272953808 -0.517042517 +359979544 0.270319223 -0.522843599 +360286777 0.267872065 -0.527585268 +360594012 0.267258048 -0.532815039 +360901246 0.263092786 -0.537637412 +361208480 0.261455893 -0.54227668 +361515714 0.260956525 -0.544527292 +361822948 0.257505238 -0.548387289 +362130182 0.252687036 -0.5560382 +362437416 0.250859141 -0.561547338 +362744650 0.247667282 -0.564722895 +363051884 0.244765192 -0.569115281 +363359117 0.243445634 -0.574381113 +363666352 0.239781439 -0.577185094 +363973586 0.23777166 -0.584368705 +364280820 0.232842251 -0.586789369 +364588054 0.230015084 -0.590789973 +364895288 0.228349268 -0.595370411 +365202522 0.222132414 -0.596906781 +365509756 0.219983652 -0.599785745 +365816990 0.216021865 -0.603411853 +366124224 0.217496186 -0.607020735 +366431458 0.209589004 -0.610150039 +366738692 0.206551611 -0.612612366 +367045926 0.201033473 -0.61430782 +367353160 0.200950697 -0.618375957 +367660394 0.197432339 -0.620910346 +367967628 0.195360541 -0.625308692 +368274862 0.191613167 -0.625410676 +368582096 0.18629007 -0.628514111 +368889330 0.184691429 -0.631337165 +369196564 0.181228294 -0.633144438 +369503797 0.180118829 -0.634798228 +369811032 0.175087019 -0.638273596 +370118266 0.172325238 -0.638650774 +370425500 0.168771117 -0.642502486 +370732734 0.167411178 -0.645644664 +371039968 0.162512421 -0.64496535 +371347202 0.160510212 -0.648602902 +371654435 0.155778929 -0.650744736 +371961670 0.155242249 -0.653963685 +372268905 0.15309976 -0.654725074 +372576138 0.149685561 -0.656344652 +372883372 0.146651342 -0.657550573 +373190606 0.144109919 -0.662417054 +373497840 0.143326729 -0.661373198 +373805074 0.136431962 -0.665636837 +374112308 0.132503524 -0.666078686 +374419542 0.132606923 -0.669084846 +374726776 0.130048155 -0.672062397 +375034010 0.125350177 -0.672393739 +375341244 0.123938702 -0.674126625 +375648478 0.120570786 -0.676313817 +375955712 0.116941854 -0.67654103 +376262946 0.118225157 -0.680025458 +376570180 0.113091059 -0.682311713 +376877414 0.11091873 -0.683408796 +377184648 0.107743039 -0.683544278 +377491882 0.104852065 -0.687655389 +377799116 0.102410078 -0.690790176 +378106350 0.100063048 -0.690523922 +378413584 0.097848676 -0.693260252 +378720818 0.09342204 -0.69585371 +379028052 0.094485476 -0.698201656 +379335286 0.08849579 -0.700498521 +379642522 0.085036732 -0.700380206 +379949754 0.084417797 -0.703331649 +380256988 0.081381067 -0.704723119 +380564222 0.079620353 -0.708032429 +380871456 0.075261376 -0.709510624 +381178690 0.070795148 -0.711845755 +381485924 0.071182772 -0.714910387 +381793156 0.066529437 -0.716409385 +382100392 0.063721753 -0.717728197 +382407626 0.059718716 -0.720799326 +382714860 0.059397153 -0.721378803 +383022094 0.053581148 -0.722331941 +383329328 0.052200298 -0.726614058 +383636562 0.045966345 -0.726853966 +383943794 0.046221155 -0.730797111 +384251030 0.040742963 -0.732317626 +384558264 0.036966234 -0.732907652 +384865498 0.034990999 -0.735400497 +385172734 0.029816579 -0.734947144 +385479966 0.028723228 -0.738989472 +385787200 0.022255994 -0.741573333 +386094432 0.016884302 -0.74176532 +386401668 0.014397382 -0.745248496 +386708902 0.013599448 -0.745862007 +387016136 0.010853674 -0.746502101 +387323372 0.007635433 -0.748281359 +387630604 0.00258793 -0.749557435 +387937838 0.001069887 -0.751913309 +388245072 -0.014857373 -0.731388211 +388552306 -0.00589338 -0.753075659 +388859540 -0.010875249 -0.754234254 +389166774 -0.014112978 -0.754308283 +389474008 -0.015393489 -0.755782961 +389781241 -0.02042469 -0.75689125 +390088476 -0.025181809 -0.758602559 +390395710 -0.028684196 -0.75970292 +390702944 -0.031486798 -0.76025623 +391010178 -0.034594152 -0.76027888 +391317411 -0.03934627 -0.761133968 +391624646 -0.041806835 -0.763755857 +391931880 -0.046926781 -0.764225959 +392239114 -0.049442984 -0.765295624 +392546348 -0.05368169 -0.765176355 +392853582 -0.05738369 -0.766043305 +393160816 -0.060563284 -0.76675266 +393468050 -0.06437385 -0.770093977 +393775284 -0.068572267 -0.767835617 +394082518 -0.072145819 -0.76797986 +394389751 -0.076424494 -0.770108282 +394696986 -0.080107413 -0.772556126 +395004220 -0.084669701 -0.772627115 +395311454 -0.087675958 -0.771035492 +395618688 -0.093073971 -0.77240628 +395925922 -0.095184922 -0.771814644 +396233156 -0.097153335 -0.771739184 +396540390 -0.103713594 -0.773753643 +396847624 -0.10770417 -0.772944033 +397154858 -0.110546171 -0.774275302 +397462092 -0.113446198 -0.774199604 +397769326 -0.118965908 -0.773648738 +398076560 -0.122681066 -0.774070739 +398383794 -0.128203526 -0.774569809 +398691028 -0.130147576 -0.773739099 +398998262 -0.133563533 -0.774076879 +399305496 -0.139475196 -0.773865103 +399612730 -0.141814216 -0.773211896 +399919964 -0.145304799 -0.772312283 +400227198 -0.148102626 -0.773067235 +400534431 -0.151750475 -0.771953105 +400841666 -0.153774142 -0.774389445 +401148900 -0.157810762 -0.771232247 +401456134 -0.162279173 -0.769112884 +401763368 -0.165693074 -0.770281553 +402070602 -0.169954836 -0.768876671 +402377836 -0.172837406 -0.769992828 +402685069 -0.176598966 -0.769161999 +402992304 -0.179387941 -0.76799041 +403299539 -0.182694375 -0.767374873 +403606772 -0.186360791 -0.767240226 +403914006 -0.191055774 -0.766897857 +404221240 -0.193989574 -0.765707671 +404528474 -0.19541271 -0.76404345 +404835708 -0.198661014 -0.763749659 +405142942 -0.202849 -0.762110888 +405450176 -0.204561978 -0.760829508 +405757410 -0.209001004 -0.759263157 +406064644 -0.212877765 -0.76067996 +406371878 -0.216107726 -0.758112728 +406679112 -0.218599647 -0.755817711 +406986346 -0.220620527 -0.755208849 +407293580 -0.223073542 -0.755868494 +407600814 -0.226027101 -0.753215909 +407908048 -0.230262279 -0.752550661 +408215282 -0.23316431 -0.751078248 +408522516 -0.233712568 -0.750434815 +408829750 -0.237997531 -0.748931288 +409136984 -0.240357965 -0.747270226 +409444218 -0.243132263 -0.747188925 +409751452 -0.247455179 -0.746074378 +410058686 -0.248996704 -0.743056654 +410365920 -0.252057462 -0.744179427 +410673156 -0.256261259 -0.742160081 +410980388 -0.256404489 -0.742574453 +411287622 -0.259868621 -0.739685356 +411594856 -0.261707216 -0.73747909 +411902090 -0.266455382 -0.734907686 +412209324 -0.266835063 -0.735707879 +412516558 -0.269398421 -0.732883453 +412823790 -0.273100495 -0.731520056 +413131026 -0.275319933 -0.727839291 +413438260 -0.276976108 -0.727982699 +413745494 -0.279807597 -0.725895166 +414052728 -0.280132949 -0.723994672 +414359962 -0.283827751 -0.723007559 +414667196 -0.287393242 -0.722765564 +414974428 -0.286775112 -0.721313059 +415281664 -0.289876192 -0.718024253 +415588898 -0.291711896 -0.717150926 +415896132 -0.294108361 -0.715150535 +416203368 -0.295413732 -0.714119911 +416510600 -0.297555029 -0.712511181 +416817834 -0.299962162 -0.710382997 +417125066 -0.302520781 -0.709792912 +417432302 -0.302201479 -0.707982122 +417739536 -0.303146213 -0.706292808 +418046770 -0.306337773 -0.704720675 +418354006 -0.307306438 -0.702963888 +418661238 -0.308869361 -0.701071441 +418968472 -0.309976816 -0.698351919 +419275706 -0.310642004 -0.698788046 +419582940 -0.309042215 -0.69875586 +419890174 -0.311722815 -0.696813821 +420197408 -0.313677579 -0.695578813 +420504642 -0.314564466 -0.693006336 +420811875 -0.316188216 -0.69223237 +421119110 -0.31611973 -0.690224587 +421426344 -0.319250494 -0.689989864 +421733578 -0.318387478 -0.688366591 +422040812 -0.320141851 -0.687820076 +422348045 -0.32169184 -0.686911106 +422655280 -0.32289347 -0.68453139 +422962514 -0.324511647 -0.686374187 +423269748 -0.327084034 -0.684318721 +423576982 -0.326471596 -0.68208599 +423884216 -0.326033592 -0.680406868 +424191450 -0.328718096 -0.681522965 +424498684 -0.329107463 -0.679668068 +424805918 -0.33096832 -0.679438114 +425113152 -0.331581085 -0.676838159 +425420385 -0.332053571 -0.676884949 +425727620 -0.334302783 -0.676936984 +426034854 -0.336342185 -0.677164852 +426342088 -0.33481118 -0.67560935 +426649322 -0.338076025 -0.6739766 +426956556 -0.338350236 -0.672793209 +427263790 -0.338849216 -0.674399554 +427571024 -0.339146226 -0.671031415 +427878258 -0.341577291 -0.672433793 +428185492 -0.342460632 -0.671639442 +428492726 -0.343794882 -0.672457873 +428799960 -0.34413594 -0.671575725 +429107194 -0.344038277 -0.670971035 +429414428 -0.345631033 -0.669162988 +429721662 -0.347412198 -0.669879913 +430028896 -0.348209589 -0.669573366 +430336130 -0.350360214 -0.668384969 +430643364 -0.351566165 -0.667757809 +430950598 -0.353713065 -0.6667912 +431257832 -0.354817688 -0.666836202 +431565065 -0.355824649 -0.666702687 +431872300 -0.357657253 -0.667171537 +432179534 -0.358693122 -0.666706919 +432486768 -0.359230637 -0.666518986 +432794002 -0.361847221 -0.665744185 +433101236 -0.363278508 -0.667832493 +433408470 -0.365018486 -0.666730105 +433715703 -0.367032557 -0.664848685 +434022938 -0.368502289 -0.666004657 +434330173 -0.370283782 -0.664714276 +434637406 -0.371249228 -0.665546298 +434944640 -0.37400344 -0.665174365 +435251874 -0.373918294 -0.664570271 +435559108 -0.378761947 -0.667065501 +435866342 -0.378454267 -0.665014564 +436173576 -0.380719423 -0.665884077 +436480810 -0.382672876 -0.665351569 +436788044 -0.384630262 -0.664011001 +437095278 -0.387212812 -0.663984 +437402512 -0.390628635 -0.663298487 +437709746 -0.392444968 -0.66303122 +438016980 -0.393969923 -0.662717044 +438324214 -0.396524369 -0.662502288 +438631448 -0.399471104 -0.661321222 +438938682 -0.401402413 -0.660097718 +439245916 -0.404294103 -0.660113215 +439553150 -0.406277 -0.659130632 +439860384 -0.408966213 -0.659817576 +440167618 -0.410465687 -0.656456112 +440474852 -0.414502263 -0.655543267 +440782086 -0.415819942 -0.655420899 +441089320 -0.41768828 -0.654993593 +441396554 -0.42085281 -0.652031481 +441703790 -0.423301815 -0.650813817 +442011022 -0.425217211 -0.649611949 +442318256 -0.427978247 -0.648141682 +442625490 -0.430552929 -0.646475255 +442932724 -0.433005988 -0.64532274 +443239958 -0.43517667 -0.64460498 +443547192 -0.438605904 -0.641879618 +443854424 -0.440228134 -0.6405797 +444161660 -0.44286412 -0.639288604 +444468894 -0.446289539 -0.636299192 +444776128 -0.447310626 -0.63502717 +445083362 -0.449347883 -0.633425414 +445390596 -0.45286563 -0.631045877 +445697830 -0.45420441 -0.627388894 +446005062 -0.455636858 -0.626957595 +446312298 -0.457158565 -0.625470399 +446619532 -0.459517508 -0.620980381 +446926766 -0.460888892 -0.620492339 +447234002 -0.463483631 -0.618099629 +447541234 -0.463794738 -0.614698708 +447848468 -0.46643114 -0.613695502 +448155700 -0.467991679 -0.611250519 +448462936 -0.470118999 -0.608109533 +448770170 -0.470200747 -0.606378912 +449077404 -0.472817838 -0.603383421 +449384640 -0.47454372 -0.600811004 +449691872 -0.475626528 -0.599619805 +449999106 -0.477336168 -0.597438812 diff --git a/test/data/valid_common.s2p b/test/data/valid_common.s2p new file mode 100644 index 00000000..5936112c --- /dev/null +++ b/test/data/valid_common.s2p @@ -0,0 +1,1027 @@ +! Vector Network Analyzer VNA R2 +! Tucson Amateur Packet Radio +! Saturday, 9 November, 2019 17:48:47 +! Frequency S11 S21 S12 S22 +! ListType=Lin +# HZ S RI R 50 +000500000 -3.33238E-001 1.80018E-004 6.74780E-001 -8.19510E-007 6.75290E-001 -8.20129E-007 -3.33238E-001 3.08078E-004 +001382728 -3.33017E-001 6.89580E-004 6.74251E-001 -3.70855E-004 6.74761E-001 -5.04361E-004 -3.33016E-001 9.45694E-004 +002265456 -3.33136E-001 1.06095E-003 6.74766E-001 -1.00228E-003 6.75276E-001 -1.00304E-003 -3.33136E-001 1.06095E-003 +003148184 -3.33120E-001 1.97467E-003 6.74773E-001 -1.65230E-003 6.74773E-001 -1.65230E-003 -3.33121E-001 1.91064E-003 +004030912 -3.32847E-001 2.45743E-003 6.74777E-001 -2.28839E-003 6.75288E-001 -2.15679E-003 -3.33092E-001 2.45894E-003 +004913640 -3.32746E-001 2.93382E-003 6.75260E-001 -2.94645E-003 6.75261E-001 -2.81312E-003 -3.32990E-001 3.06364E-003 +005796368 -3.33479E-001 3.06528E-003 6.75798E-001 -2.32365E-003 6.76309E-001 -2.32540E-003 -3.33479E-001 3.06528E-003 +006679097 -3.32609E-001 3.80377E-003 6.74764E-001 -4.08250E-003 6.74764E-001 -4.08250E-003 -3.32854E-001 3.80608E-003 +007561825 -3.32448E-001 4.35906E-003 6.75247E-001 -4.96650E-003 6.75249E-001 -4.69986E-003 -3.32692E-001 4.36169E-003 +008444553 -3.32510E-001 4.94361E-003 6.74737E-001 -5.33508E-003 6.75248E-001 -5.20579E-003 -3.32508E-001 5.13540E-003 +009327281 -3.32540E-001 5.23454E-003 6.74245E-001 -5.84230E-003 6.74756E-001 -5.71349E-003 -3.32538E-001 5.36240E-003 +010210009 -3.32439E-001 5.63223E-003 6.74238E-001 -6.34444E-003 6.74748E-001 -6.34923E-003 -3.32682E-001 5.82741E-003 +011092737 -3.32540E-001 6.33491E-003 6.74725E-001 -6.98455E-003 6.75233E-001 -7.12315E-003 -3.32782E-001 6.46647E-003 +011975466 -3.32520E-001 6.78266E-003 6.74742E-001 -7.74980E-003 6.74742E-001 -7.74980E-003 -3.32517E-001 6.97444E-003 +012858194 -3.32251E-001 7.21687E-003 6.74709E-001 -8.25742E-003 6.75221E-001 -8.13034E-003 -3.32495E-001 7.28494E-003 +013740922 -3.32283E-001 7.65529E-003 6.74224E-001 -8.75331E-003 6.74732E-001 -8.89315E-003 -3.32280E-001 7.78304E-003 +014623650 -3.32136E-001 8.17053E-003 6.74712E-001 -9.26713E-003 6.75222E-001 -9.27414E-003 -3.32383E-001 8.04729E-003 +015506378 -3.32205E-001 8.76676E-003 6.74685E-001 -9.90079E-003 6.75197E-001 -9.77496E-003 -3.32447E-001 8.89952E-003 +016389106 -3.32080E-001 9.21012E-003 6.74662E-001 -1.04145E-002 6.75170E-001 -1.05557E-002 -3.32079E-001 9.27399E-003 +017271835 -3.32154E-001 9.49379E-003 6.74193E-001 -1.11608E-002 6.74700E-001 -1.13025E-002 -3.32152E-001 9.62154E-003 +018154563 -3.31998E-001 1.00992E-002 6.74662E-001 -1.15629E-002 6.75170E-001 -1.17050E-002 -3.31995E-001 1.02268E-002 +019037291 -3.31940E-001 1.03930E-002 6.74689E-001 -1.23231E-002 6.74689E-001 -1.23231E-002 -3.31937E-001 1.05206E-002 +019920019 -3.31798E-001 1.10248E-002 6.74629E-001 -1.29565E-002 6.75141E-001 -1.28330E-002 -3.31796E-001 1.10886E-002 +020802747 -3.31929E-001 1.12927E-002 6.74623E-001 -1.34714E-002 6.75135E-001 -1.33483E-002 -3.31681E-001 1.14141E-002 +021685475 -3.31956E-001 1.19022E-002 6.74129E-001 -1.40862E-002 6.74639E-001 -1.40969E-002 -3.31708E-001 1.20233E-002 +022568204 -3.31942E-001 1.21935E-002 6.74592E-001 -1.46133E-002 6.75102E-001 -1.46243E-002 -3.31936E-001 1.23849E-002 +023450932 -3.31821E-001 1.26789E-002 6.74632E-001 -1.53881E-002 6.74635E-001 -1.52549E-002 -3.31571E-001 1.28632E-002 +024333660 -3.31622E-001 1.32997E-002 6.74572E-001 -1.58781E-002 6.75082E-001 -1.58901E-002 -3.31868E-001 1.32432E-002 +025216388 -3.31641E-001 1.36906E-002 6.74565E-001 -1.62470E-002 6.75071E-001 -1.63926E-002 -3.31395E-001 1.37468E-002 +026099116 -3.31683E-001 1.42242E-002 6.74097E-001 -1.68722E-002 6.74604E-001 -1.70181E-002 -3.31679E-001 1.43518E-002 +026981844 -3.31563E-001 1.44243E-002 6.74518E-001 -1.75147E-002 6.75028E-001 -1.75279E-002 -3.31305E-001 1.48623E-002 +027864573 -3.31389E-001 1.49567E-002 6.73999E-001 -1.80216E-002 6.74505E-001 -1.81684E-002 -3.31380E-001 1.52115E-002 +028747301 -3.31391E-001 1.55249E-002 6.74047E-001 -1.86480E-002 6.74552E-001 -1.87952E-002 -3.31384E-001 1.57160E-002 +029630029 -3.31274E-001 1.57520E-002 6.74477E-001 -1.90232E-002 6.74473E-001 -1.91564E-002 -3.31262E-001 1.60705E-002 +030512757 -3.31105E-001 1.63176E-002 6.73953E-001 -1.96645E-002 6.74972E-001 -1.96943E-002 -3.31342E-001 1.65176E-002 +031395485 -3.31155E-001 1.67731E-002 6.74001E-001 -2.04559E-002 6.74510E-001 -2.04714E-002 -3.31394E-001 1.69097E-002 +032278213 -3.31325E-001 1.73646E-002 6.74426E-001 -2.09647E-002 6.74932E-001 -2.11138E-002 -3.31322E-001 1.74283E-002 +033160942 -3.30956E-001 1.77978E-002 6.73911E-001 -2.15534E-002 6.74420E-001 -2.15696E-002 -3.31195E-001 1.79348E-002 +034043670 -3.31049E-001 1.80684E-002 6.73961E-001 -2.18557E-002 6.74461E-001 -2.21386E-002 -3.31031E-001 1.85139E-002 +034926398 -3.31017E-001 1.84219E-002 6.74366E-001 -2.27068E-002 6.74872E-001 -2.28572E-002 -3.31009E-001 1.86128E-002 +035809126 -3.30806E-001 1.91017E-002 6.74350E-001 -2.32203E-002 6.74350E-001 -2.32203E-002 -3.30795E-001 1.93560E-002 +036691854 -3.30747E-001 1.94894E-002 6.73901E-001 -2.37532E-002 6.74415E-001 -2.36380E-002 -3.30979E-001 1.97544E-002 +037574582 -3.30842E-001 1.98303E-002 6.74319E-001 -2.42221E-002 6.74824E-001 -2.43736E-002 -3.30831E-001 2.00846E-002 +038457311 -3.30823E-001 2.03842E-002 6.73787E-001 -2.48629E-002 6.74296E-001 -2.48817E-002 -3.30563E-001 2.07543E-002 +039340039 -3.30693E-001 2.08593E-002 6.73870E-001 -2.53306E-002 6.74374E-001 -2.54829E-002 -3.30679E-001 2.11770E-002 +040222767 -3.30797E-001 2.11834E-002 6.74253E-001 -2.59793E-002 6.74768E-001 -2.58657E-002 -3.30788E-001 2.13741E-002 +041105495 -3.30415E-001 2.17180E-002 6.74224E-001 -2.65341E-002 6.74734E-001 -2.65541E-002 -3.30402E-001 2.19722E-002 +041988223 -3.30429E-001 2.21901E-002 6.73797E-001 -2.68669E-002 6.74295E-001 -2.71535E-002 -3.30407E-001 2.26347E-002 +042870951 -3.30209E-001 2.24610E-002 6.74182E-001 -2.76700E-002 6.74692E-001 -2.76909E-002 -3.30439E-001 2.27271E-002 +043753680 -3.30461E-001 2.29905E-002 6.74164E-001 -2.81678E-002 6.74673E-001 -2.81891E-002 -3.30457E-001 2.30540E-002 +044636408 -3.30348E-001 2.32602E-002 6.74149E-001 -2.85650E-002 6.74647E-001 -2.88530E-002 -3.30338E-001 2.34506E-002 +045519136 -3.30232E-001 2.37382E-002 6.74110E-001 -2.91788E-002 6.74099E-001 -2.94450E-002 -3.30451E-001 2.41954E-002 +046401864 -3.30193E-001 2.42220E-002 6.73671E-001 -2.99264E-002 6.74180E-001 -2.99490E-002 -3.30176E-001 2.45392E-002 +047284592 -3.29919E-001 2.46057E-002 6.74047E-001 -3.06120E-002 6.74550E-001 -3.07683E-002 -3.30374E-001 2.52035E-002 +048167320 -3.30232E-001 2.49503E-002 6.74033E-001 -3.12221E-002 6.74542E-001 -3.12457E-002 -3.30214E-001 2.52676E-002 +049050049 -3.29961E-001 2.53895E-002 6.74009E-001 -3.17233E-002 6.73996E-001 -3.19895E-002 -3.30175E-001 2.59107E-002 +049932777 -3.29937E-001 2.56649E-002 6.73586E-001 -3.22137E-002 6.74089E-001 -3.23711E-002 -3.29904E-001 2.62355E-002 +050815505 -3.29904E-001 2.63352E-002 6.73959E-001 -3.27384E-002 6.74461E-001 -3.28963E-002 -3.29632E-001 2.68279E-002 +051698233 -3.29902E-001 2.65745E-002 6.73927E-001 -3.33988E-002 6.74423E-001 -3.36904E-002 -3.29634E-001 2.70037E-002 +052580961 -3.29685E-001 2.72513E-002 6.73900E-001 -3.38732E-002 6.74383E-001 -3.44314E-002 -3.29677E-001 2.73780E-002 +053463689 -3.29587E-001 2.76318E-002 6.73974E-001 -3.46484E-002 6.74476E-001 -3.48078E-002 -3.29810E-001 2.79634E-002 +054346418 -3.29385E-001 2.80148E-002 6.73835E-001 -3.51768E-002 6.74337E-001 -3.53366E-002 -3.29846E-001 2.84249E-002 +055229146 -3.29826E-001 2.84231E-002 6.73804E-001 -3.56165E-002 6.74802E-001 -3.60701E-002 -3.29551E-001 2.89145E-002 +056111874 -3.29374E-001 2.88509E-002 6.73806E-001 -3.57883E-002 6.74287E-001 -3.63478E-002 -3.29346E-001 2.92938E-002 +056994602 -3.29340E-001 2.92064E-002 6.73754E-001 -3.67240E-002 6.74256E-001 -3.68848E-002 -3.29315E-001 2.95861E-002 +057877330 -3.29083E-001 2.97708E-002 6.73852E-001 -3.73385E-002 6.74346E-001 -3.76330E-002 -3.29296E-001 3.02295E-002 +058760058 -3.29126E-001 3.00328E-002 6.73686E-001 -3.79264E-002 6.74690E-001 -3.82501E-002 -3.29339E-001 3.04917E-002 +059642787 -3.29470E-001 3.05761E-002 6.73647E-001 -3.83997E-002 6.74133E-001 -3.88281E-002 -3.29206E-001 3.08761E-002 +060525515 -3.29230E-001 3.10854E-002 6.73602E-001 -3.92040E-002 6.74095E-001 -3.94998E-002 -3.29209E-001 3.14017E-002 +061408243 -3.28934E-001 3.13888E-002 6.73703E-001 -3.98118E-002 6.74196E-001 -4.01081E-002 -3.29145E-001 3.18482E-002 +062290971 -3.29007E-001 3.18608E-002 6.73552E-001 -4.00784E-002 6.74038E-001 -4.05079E-002 -3.28971E-001 3.23664E-002 +063173699 -3.28928E-001 3.23916E-002 6.73510E-001 -4.07465E-002 6.74003E-001 -4.10435E-002 -3.28896E-001 3.28339E-002 +064056427 -3.28657E-001 3.25978E-002 6.73470E-001 -4.13831E-002 6.73963E-001 -4.16806E-002 -3.28858E-001 3.31838E-002 +064939156 -3.28741E-001 3.30104E-002 6.73452E-001 -4.16954E-002 6.73436E-001 -4.19613E-002 -3.28709E-001 3.34524E-002 +065821884 -3.28672E-001 3.35412E-002 6.73558E-001 -4.23998E-002 6.74034E-001 -4.29642E-002 -3.28881E-001 3.40012E-002 +066704612 -3.28685E-001 3.39145E-002 6.73362E-001 -4.31657E-002 6.74380E-001 -4.32310E-002 -3.28652E-001 3.43564E-002 +067587340 -3.28727E-001 3.44237E-002 6.73319E-001 -4.38757E-002 6.73811E-001 -4.41749E-002 -3.28703E-001 3.47393E-002 +068470068 -3.28457E-001 3.48298E-002 6.73442E-001 -4.43672E-002 6.73933E-001 -4.46669E-002 -3.28418E-001 3.53344E-002 +069352796 -3.28463E-001 3.50287E-002 6.72782E-001 -4.42837E-002 6.73791E-001 -4.44837E-002 -3.28419E-001 3.55964E-002 +070235525 -3.28066E-001 3.56237E-002 6.73380E-001 -4.52778E-002 6.73853E-001 -4.58443E-002 -3.28046E-001 3.58758E-002 +071118253 -3.28182E-001 3.59179E-002 6.73546E-001 -4.56474E-002 6.74028E-001 -4.60812E-002 -3.28383E-001 3.64415E-002 +072000981 -3.28320E-001 3.61305E-002 6.73095E-001 -4.70120E-002 6.73632E-001 -4.66485E-002 -3.28270E-001 3.67610E-002 +072883709 -3.28074E-001 3.68275E-002 6.72880E-001 -4.76601E-002 6.73388E-001 -4.76961E-002 -3.28274E-001 3.73514E-002 +073766437 -3.28124E-001 3.72758E-002 6.73721E-001 -4.79507E-002 6.73731E-001 -4.78177E-002 -3.28103E-001 3.75278E-002 +074649165 -3.27911E-001 3.75450E-002 6.73563E-001 -4.78605E-002 6.74553E-001 -4.83325E-002 -3.28110E-001 3.80687E-002 +075531894 -3.27895E-001 3.81299E-002 6.73159E-001 -4.86500E-002 6.73648E-001 -4.89527E-002 -3.27863E-001 3.85076E-002 +076414622 -3.27763E-001 3.85933E-002 6.72383E-001 -4.98106E-002 6.72911E-001 -4.95825E-002 -3.27758E-001 3.86562E-002 +077297350 -3.27786E-001 3.86343E-002 6.72953E-001 -4.91056E-002 6.73423E-001 -4.96745E-002 -3.27727E-001 3.93265E-002 +078180078 -3.27778E-001 3.92203E-002 6.72915E-001 -4.97056E-002 6.72855E-001 -5.05028E-002 -3.27740E-001 3.96608E-002 +079062806 -3.27551E-001 3.98247E-002 6.72477E-001 -5.10612E-002 6.73453E-001 -5.16703E-002 -3.27271E-001 4.02437E-002 +079945534 -3.27363E-001 4.02130E-002 6.72920E-001 -5.18203E-002 6.73418E-001 -5.19924E-002 -3.27318E-001 4.07159E-002 +080828263 -3.27157E-001 4.05666E-002 6.72996E-001 -5.27771E-002 6.73556E-001 -5.21521E-002 -3.27347E-001 4.11537E-002 +081710991 -3.27255E-001 4.07847E-002 6.72572E-001 -5.22782E-002 6.73558E-001 -5.27562E-002 -3.27187E-001 4.15384E-002 +082593719 -3.27269E-001 4.14646E-002 6.73478E-001 -5.32207E-002 6.74443E-001 -5.39670E-002 -3.27211E-001 4.20928E-002 +083476447 -3.27078E-001 4.17507E-002 6.72171E-001 -5.42646E-002 6.72658E-001 -5.45712E-002 -3.26792E-001 4.22307E-002 +084359175 -3.27133E-001 4.22195E-002 6.72378E-001 -5.46778E-002 6.72843E-001 -5.52506E-002 -3.26829E-001 4.28877E-002 +085241903 -3.27021E-001 4.27576E-002 6.72232E-001 -5.57017E-002 6.73292E-001 -5.52542E-002 -3.26973E-001 4.32599E-002 +086124631 -3.27061E-001 4.32178E-002 6.72511E-001 -5.47591E-002 6.73462E-001 -5.56398E-002 -3.26773E-001 4.36970E-002 +087007360 -3.26826E-001 4.35176E-002 6.72530E-001 -5.68958E-002 6.73569E-001 -5.67159E-002 -3.26765E-001 4.41449E-002 +087890088 -3.26596E-001 4.38587E-002 6.72830E-001 -5.71160E-002 6.73304E-001 -5.75580E-002 -3.26541E-001 4.44228E-002 +088772816 -3.26526E-001 4.43908E-002 6.72614E-001 -5.72785E-002 6.73100E-001 -5.75876E-002 -3.26465E-001 4.50175E-002 +089655544 -3.26600E-001 4.49511E-002 6.72474E-001 -5.76679E-002 6.72890E-001 -5.87744E-002 -3.26531E-001 4.56404E-002 +090538272 -3.26631E-001 4.54994E-002 6.73503E-001 -5.87715E-002 6.73386E-001 -6.01011E-002 -3.26328E-001 4.61018E-002 +091421000 -3.26536E-001 4.57718E-002 6.72370E-001 -5.88357E-002 6.72843E-001 -5.92787E-002 -3.26492E-001 4.62103E-002 +092303729 -3.26346E-001 4.60928E-002 6.72393E-001 -5.97932E-002 6.72842E-001 -6.05026E-002 -3.26036E-001 4.67567E-002 +093186457 -3.26362E-001 4.68231E-002 6.72047E-001 -6.01921E-002 6.73039E-001 -6.05489E-002 -3.26317E-001 4.72613E-002 +094069185 -3.26275E-001 4.70625E-002 6.72695E-001 -6.09588E-002 6.72683E-001 -6.10917E-002 -3.26449E-001 4.77133E-002 +094951913 -3.26052E-001 4.71870E-002 6.72395E-001 -6.19507E-002 6.73388E-001 -6.23103E-002 -3.26200E-001 4.80876E-002 +095834641 -3.25952E-001 4.77116E-002 6.72465E-001 -6.11352E-002 6.72802E-001 -6.30414E-002 -3.25873E-001 4.84618E-002 +096717369 -3.26032E-001 4.82203E-002 6.72345E-001 -6.25666E-002 6.72828E-001 -6.28796E-002 -3.25726E-001 4.88198E-002 +097600098 -3.25840E-001 4.85197E-002 6.72319E-001 -6.29417E-002 6.72752E-001 -6.37863E-002 -3.25774E-001 4.91444E-002 +098482826 -3.25880E-001 4.90403E-002 6.72257E-001 -6.35235E-002 6.72702E-001 -6.42356E-002 -3.25573E-001 4.96391E-002 +099365554 -3.25964E-001 4.95814E-002 6.72179E-001 -6.42571E-002 6.72623E-001 -6.49697E-002 -3.25650E-001 5.02424E-002 +100248282 -3.25713E-001 4.96256E-002 6.71603E-001 -6.49295E-002 6.72097E-001 -6.51113E-002 -3.25638E-001 5.03125E-002 +101131010 -3.25686E-001 5.00183E-002 6.71793E-001 -6.53358E-002 6.72275E-001 -6.56506E-002 -3.25351E-001 5.08658E-002 +102013738 -3.25246E-001 5.06076E-002 6.71820E-001 -6.56199E-002 6.72237E-001 -6.65987E-002 -3.25176E-001 5.12310E-002 +102896467 -3.25392E-001 5.09116E-002 6.71481E-001 -6.62420E-002 6.71910E-001 -6.70881E-002 -3.25294E-001 5.17845E-002 +103779195 -3.25145E-001 5.12740E-002 6.71112E-001 -6.71043E-002 6.72074E-001 -6.77366E-002 -3.25292E-001 5.21116E-002 +104661923 -3.25161E-001 5.17189E-002 6.71858E-001 -6.75653E-002 6.72312E-001 -6.81474E-002 -3.25097E-001 5.22798E-002 +105544651 -3.24920E-001 5.21407E-002 6.71795E-001 -6.81922E-002 6.72276E-001 -6.85092E-002 -3.24855E-001 5.27011E-002 +106427379 -3.24707E-001 5.26223E-002 6.71743E-001 -6.86456E-002 6.72182E-001 -6.93611E-002 -3.24866E-001 5.33347E-002 +107310107 -3.25029E-001 5.29706E-002 6.71204E-001 -6.88049E-002 6.71629E-001 -6.96525E-002 -3.24697E-001 5.37523E-002 +108192836 -3.24783E-001 5.32621E-002 6.71117E-001 -6.97070E-002 6.71555E-001 -7.04227E-002 -3.24688E-001 5.40713E-002 +109075564 -3.24723E-001 5.36368E-002 6.71266E-001 -7.07086E-002 6.71224E-001 -7.11062E-002 -3.24613E-001 5.45700E-002 +109958292 -3.24550E-001 5.39678E-002 6.71511E-001 -7.08322E-002 6.72442E-001 -7.17360E-002 -3.24461E-001 5.47140E-002 +110841020 -3.24461E-001 5.44128E-002 6.70713E-001 -7.11206E-002 6.71642E-001 -7.20239E-002 -3.24349E-001 5.53455E-002 +111723748 -3.24444E-001 5.49481E-002 6.70916E-001 -7.15830E-002 6.71323E-001 -7.25650E-002 -3.24339E-001 5.58184E-002 +112606476 -3.24376E-001 5.53409E-002 6.70836E-001 -7.23522E-002 6.71301E-001 -7.28046E-002 -3.24254E-001 5.63351E-002 +113489205 -3.24254E-001 5.58739E-002 6.71037E-001 -7.28873E-002 6.71458E-001 -7.37379E-002 -3.24171E-001 5.65570E-002 +114371933 -3.24257E-001 5.59954E-002 6.70736E-001 -7.32912E-002 6.71126E-001 -7.44068E-002 -3.24142E-001 5.69269E-002 +115254661 -3.23957E-001 5.63014E-002 6.70651E-001 -7.40532E-002 6.71577E-001 -7.49608E-002 -3.23841E-001 5.72321E-002 +116137389 -3.23953E-001 5.71404E-002 6.70874E-001 -7.44560E-002 6.71784E-001 -7.54971E-002 -3.23622E-001 5.78546E-002 +117020117 -3.23916E-001 5.72465E-002 6.71040E-001 -7.51807E-002 6.71965E-001 -7.60904E-002 -3.23570E-001 5.80842E-002 +117902845 -3.23447E-001 5.75876E-002 6.70942E-001 -7.59514E-002 6.71359E-001 -7.68041E-002 -3.23558E-001 5.86089E-002 +118785574 -3.23367E-001 5.81923E-002 6.70891E-001 -7.64463E-002 6.71322E-001 -7.71668E-002 -3.23263E-001 5.89973E-002 +119668302 -3.23528E-001 5.85217E-002 6.70856E-001 -7.67558E-002 6.70750E-001 -7.76829E-002 -3.23432E-001 5.92651E-002 +120551030 -3.23566E-001 5.88291E-002 6.70784E-001 -7.73256E-002 6.70661E-001 -7.83851E-002 -3.23241E-001 5.94797E-002 +121433758 -3.23290E-001 5.90924E-002 6.70199E-001 -7.79500E-002 6.71135E-001 -7.87305E-002 -3.23169E-001 6.00209E-002 +122316486 -3.22995E-001 5.95820E-002 6.70131E-001 -7.85243E-002 6.70559E-001 -7.92456E-002 -3.22906E-001 6.02623E-002 +123199214 -3.23200E-001 5.98958E-002 6.70044E-001 -7.92925E-002 6.70979E-001 -8.00748E-002 -3.23069E-001 6.08856E-002 +124081943 -3.23128E-001 6.04008E-002 6.70790E-001 -7.97564E-002 6.70711E-001 -8.04186E-002 -3.23021E-001 6.12045E-002 +124964671 -3.23084E-001 6.06619E-002 6.70145E-001 -8.03924E-002 6.70555E-001 -8.12475E-002 -3.22723E-001 6.15573E-002 +125847399 -3.23122E-001 6.11601E-002 6.69845E-001 -8.09106E-002 6.70271E-001 -8.16335E-002 -3.22769E-001 6.19936E-002 +126730127 -3.22687E-001 6.16202E-002 6.70099E-001 -8.11359E-002 6.71016E-001 -8.20536E-002 -3.22578E-001 6.24229E-002 +127612855 -3.22657E-001 6.21479E-002 6.69747E-001 -8.17210E-002 6.70630E-001 -8.29039E-002 -3.22293E-001 6.30414E-002 +128495583 -3.22578E-001 6.22279E-002 6.69683E-001 -8.22250E-002 6.70614E-001 -8.30114E-002 -3.22662E-001 6.33718E-002 +129378312 -3.22668E-001 6.26486E-002 6.69905E-001 -8.27803E-002 6.70803E-001 -8.38326E-002 -3.22278E-001 6.37265E-002 +130261040 -3.22538E-001 6.29975E-002 6.69774E-001 -8.33282E-002 6.70181E-001 -8.41851E-002 -3.22366E-001 6.42311E-002 +131143768 -3.22287E-001 6.36044E-002 6.69491E-001 -8.39138E-002 6.70404E-001 -8.48349E-002 -3.22166E-001 6.44672E-002 +132026496 -3.22138E-001 6.39125E-002 6.69725E-001 -8.43651E-002 6.70164E-001 -8.49582E-002 -3.22235E-001 6.49318E-002 +132909224 -3.22405E-001 6.43886E-002 6.69559E-001 -8.49339E-002 6.69947E-001 -8.59241E-002 -3.22046E-001 6.52179E-002 +133791952 -3.21950E-001 6.47191E-002 6.69293E-001 -8.54328E-002 6.69663E-001 -8.65553E-002 -3.21809E-001 6.57040E-002 +134674681 -3.22025E-001 6.50451E-002 6.69271E-001 -8.56743E-002 6.69607E-001 -8.70613E-002 -3.21648E-001 6.59958E-002 +135557409 -3.21694E-001 6.53917E-002 6.69469E-001 -8.64814E-002 6.69872E-001 -8.73404E-002 -3.21761E-001 6.65942E-002 +136440137 -3.21647E-001 6.59054E-002 6.69063E-001 -8.71534E-002 6.69447E-001 -8.81446E-002 -3.21521E-001 6.67662E-002 +137322865 -3.21729E-001 6.63640E-002 6.69023E-001 -8.74639E-002 6.69407E-001 -8.84553E-002 -3.21377E-001 6.71290E-002 +138205593 -3.21592E-001 6.66928E-002 6.68951E-001 -8.80189E-002 6.69334E-001 -8.90106E-002 -3.21221E-001 6.75799E-002 +139088321 -3.21365E-001 6.69449E-002 6.68873E-001 -8.86226E-002 6.69273E-001 -8.94825E-002 -3.21220E-001 6.79276E-002 +139971050 -3.21199E-001 6.72945E-002 6.68757E-001 -8.94135E-002 6.69662E-001 -9.03421E-002 -3.21044E-001 6.83381E-002 +140853778 -3.21144E-001 6.75727E-002 6.68669E-001 -9.01435E-002 6.69609E-001 -9.08087E-002 -3.21186E-001 6.88966E-002 +141736506 -3.21307E-001 6.81744E-002 6.68466E-001 -9.02556E-002 6.69334E-001 -9.14494E-002 -3.20906E-001 6.92435E-002 +142619234 -3.21127E-001 6.83983E-002 6.68257E-001 -9.07439E-002 6.69159E-001 -9.16739E-002 -3.20716E-001 6.95281E-002 +143501962 -3.21015E-001 6.89820E-002 6.68347E-001 -9.10859E-002 6.69213E-001 -9.22808E-002 -3.20846E-001 7.00856E-002 +144384690 -3.21058E-001 6.93996E-002 6.68954E-001 -9.18542E-002 6.69277E-001 -9.32452E-002 -3.20702E-001 7.01605E-002 +145267419 -3.20901E-001 6.95503E-002 6.68846E-001 -9.25837E-002 6.68754E-001 -9.32440E-002 -3.20713E-001 7.07761E-002 +146150147 -3.20659E-001 6.99236E-002 6.68255E-001 -9.30910E-002 6.68649E-001 -9.39535E-002 -3.20254E-001 7.09894E-002 +147032875 -3.20579E-001 7.02907E-002 6.68561E-001 -9.34246E-002 6.68880E-001 -9.48160E-002 -3.20417E-001 7.13311E-002 +147915603 -3.20669E-001 7.08004E-002 6.67798E-001 -9.40422E-002 6.68172E-001 -9.50368E-002 -3.20047E-001 7.17057E-002 +148798331 -3.20334E-001 7.12462E-002 6.67547E-001 -9.44350E-002 6.68425E-001 -9.55017E-002 -3.20209E-001 7.20414E-002 +149681059 -3.20364E-001 7.15779E-002 6.68316E-001 -9.50095E-002 6.68632E-001 -9.64017E-002 -3.19975E-001 7.25191E-002 +150563788 -3.20253E-001 7.17102E-002 6.67573E-001 -9.55049E-002 6.67907E-001 -9.67641E-002 -3.20039E-001 7.30547E-002 +151446516 -3.20041E-001 7.21903E-002 6.67112E-001 -9.63035E-002 6.67968E-001 -9.75043E-002 -3.19885E-001 7.31678E-002 +152329244 -3.19850E-001 7.21954E-002 6.67222E-001 -9.66834E-002 6.68116E-001 -9.76211E-002 -3.19645E-001 7.34775E-002 +153211972 -3.19996E-001 7.27016E-002 6.67720E-001 -9.69140E-002 6.68051E-001 -9.81745E-002 -3.19576E-001 7.38234E-002 +154094700 -3.19888E-001 7.32491E-002 6.67583E-001 -9.78133E-002 6.67409E-001 -9.89994E-002 -3.19750E-001 7.41034E-002 +154977428 -3.19608E-001 7.36764E-002 6.67018E-001 -9.80999E-002 6.67851E-001 -9.94352E-002 -3.19206E-001 7.46748E-002 +155860157 -3.19525E-001 7.37816E-002 6.66896E-001 -9.88640E-002 6.67263E-001 -9.98611E-002 -3.19103E-001 7.49014E-002 +156742885 -3.19782E-001 7.44513E-002 6.66835E-001 -9.93151E-002 6.67181E-001 -1.00444E-001 -3.19124E-001 7.55327E-002 +157625613 -3.19444E-001 7.46980E-002 6.67061E-001 -1.00178E-001 6.67446E-001 -1.01045E-001 -3.19039E-001 7.56949E-002 +158508341 -3.19312E-001 7.50713E-002 6.67192E-001 -1.00335E-001 6.67537E-001 -1.01465E-001 -3.19140E-001 7.61065E-002 +159391069 -3.19187E-001 7.54600E-002 6.66560E-001 -1.01089E-001 6.66963E-001 -1.01824E-001 -3.19014E-001 7.64948E-002 +160273797 -3.19132E-001 7.59885E-002 6.66533E-001 -1.01253E-001 6.67339E-001 -1.02723E-001 -3.18725E-001 7.69839E-002 +161156526 -3.19022E-001 7.60595E-002 6.66463E-001 -1.01733E-001 6.67309E-001 -1.02941E-001 -3.18564E-001 7.73580E-002 +162039254 -3.19179E-001 7.67673E-002 6.66827E-001 -1.02743E-001 6.66685E-001 -1.03664E-001 -3.18570E-001 7.75397E-002 +162921982 -3.18663E-001 7.69992E-002 6.66772E-001 -1.03191E-001 6.66649E-001 -1.03981E-001 -3.18679E-001 7.83144E-002 +163804710 -3.18592E-001 7.72549E-002 6.66252E-001 -1.03259E-001 6.66571E-001 -1.04522E-001 -3.18426E-001 7.82263E-002 +164687438 -3.18575E-001 7.76077E-002 6.66172E-001 -1.03618E-001 6.65924E-001 -1.05196E-001 -3.17922E-001 7.86200E-002 +165570166 -3.18126E-001 7.81529E-002 6.66385E-001 -1.04674E-001 6.66744E-001 -1.05675E-001 -3.17937E-001 7.92445E-002 +166452894 -3.18304E-001 7.84908E-002 6.66136E-001 -1.04677E-001 6.66956E-001 -1.06021E-001 -3.18379E-001 7.94414E-002 +167335623 -3.18423E-001 7.88184E-002 6.65880E-001 -1.05514E-001 6.66216E-001 -1.06647E-001 -3.18000E-001 7.98695E-002 +168218351 -3.18205E-001 7.91115E-002 6.66378E-001 -1.05664E-001 6.66650E-001 -1.07192E-001 -3.17793E-001 8.01010E-002 +169101079 -3.18078E-001 7.96544E-002 6.65753E-001 -1.06279E-001 6.65564E-001 -1.07462E-001 -3.17675E-001 8.05827E-002 +169983807 -3.18021E-001 7.96987E-002 6.65512E-001 -1.07060E-001 6.66852E-001 -1.08356E-001 -3.17597E-001 8.07474E-002 +170866535 -3.17862E-001 8.01255E-002 6.65711E-001 -1.07331E-001 6.66504E-001 -1.08809E-001 -3.17416E-001 8.12946E-002 +171749263 -3.17468E-001 8.07066E-002 6.65970E-001 -1.08085E-001 6.65798E-001 -1.09136E-001 -3.17251E-001 8.19163E-002 +172631992 -3.17724E-001 8.11091E-002 6.65372E-001 -1.08607E-001 6.65702E-001 -1.09741E-001 -3.17286E-001 8.22168E-002 +173514720 -3.17608E-001 8.13962E-002 6.65699E-001 -1.09091E-001 6.66051E-001 -1.10094E-001 -3.17191E-001 8.23818E-002 +174397448 -3.17440E-001 8.13841E-002 6.65748E-001 -1.09517E-001 6.66056E-001 -1.10784E-001 -3.17189E-001 8.27736E-002 +175280176 -3.17342E-001 8.20802E-002 6.65356E-001 -1.09466E-001 6.65117E-001 -1.10911E-001 -3.16661E-001 8.32032E-002 +176162904 -3.17436E-001 8.24405E-002 6.65523E-001 -1.10108E-001 6.65304E-001 -1.11422E-001 -3.16985E-001 8.36063E-002 +177045632 -3.17281E-001 8.26762E-002 6.64650E-001 -1.10555E-001 6.64977E-001 -1.11689E-001 -3.16841E-001 8.37809E-002 +177928361 -3.17269E-001 8.31432E-002 6.65351E-001 -1.11259E-001 6.65174E-001 -1.12310E-001 -3.16816E-001 8.43077E-002 +178811089 -3.17053E-001 8.34851E-002 6.64821E-001 -1.11987E-001 6.65190E-001 -1.12860E-001 -3.16577E-001 8.47690E-002 +179693817 -3.16936E-001 8.38469E-002 6.64769E-001 -1.12240E-001 6.65618E-001 -1.13330E-001 -3.16527E-001 8.47685E-002 +180576545 -3.17247E-001 8.42806E-002 6.64683E-001 -1.12799E-001 6.64938E-001 -1.14328E-001 -3.16595E-001 8.52198E-002 +181459273 -3.16637E-001 8.46954E-002 6.64600E-001 -1.13236E-001 6.65425E-001 -1.14459E-001 -3.16180E-001 8.58564E-002 +182342001 -3.16472E-001 8.49288E-002 6.64573E-001 -1.13388E-001 6.65420E-001 -1.14479E-001 -3.16211E-001 8.63131E-002 +183224730 -3.16567E-001 8.55835E-002 6.64486E-001 -1.13891E-001 6.64807E-001 -1.15027E-001 -3.16143E-001 8.65630E-002 +184107458 -3.16576E-001 8.56323E-002 6.64425E-001 -1.14305E-001 6.65157E-001 -1.16054E-001 -3.16383E-001 8.66551E-002 +184990186 -3.16461E-001 8.58382E-002 6.64309E-001 -1.14941E-001 6.64583E-001 -1.16340E-001 -3.15980E-001 8.71173E-002 +185872914 -3.16382E-001 8.63287E-002 6.65179E-001 -1.15355E-001 6.64904E-001 -1.16930E-001 -3.15899E-001 8.76075E-002 +186755642 -3.16054E-001 8.68786E-002 6.64769E-001 -1.15767E-001 6.65041E-001 -1.17168E-001 -3.15374E-001 8.79311E-002 +187638370 -3.16111E-001 8.72500E-002 6.65041E-001 -1.16197E-001 6.65289E-001 -1.17730E-001 -3.15638E-001 8.84667E-002 +188521099 -3.15860E-001 8.73536E-002 6.64468E-001 -1.16990E-001 6.64738E-001 -1.18391E-001 -3.15339E-001 8.88095E-002 +189403827 -3.16115E-001 8.74892E-002 6.64915E-001 -1.17369E-001 6.64658E-001 -1.18813E-001 -3.15605E-001 8.88853E-002 +190286555 -3.16128E-001 8.80718E-002 6.63861E-001 -1.17490E-001 6.65086E-001 -1.19332E-001 -3.15411E-001 8.93018E-002 +191169283 -3.16099E-001 8.86853E-002 6.64242E-001 -1.18236E-001 6.64509E-001 -1.19638E-001 -3.15428E-001 8.96751E-002 +192052011 -3.16031E-001 8.87467E-002 6.64561E-001 -1.18918E-001 6.65354E-001 -1.20280E-001 -3.15289E-001 9.00958E-002 +192934739 -3.15735E-001 8.91912E-002 6.64133E-001 -1.19241E-001 6.64399E-001 -1.20643E-001 -3.14979E-001 9.05986E-002 +193817468 -3.15476E-001 8.94796E-002 6.63940E-001 -1.19837E-001 6.64706E-001 -1.21331E-001 -3.14926E-001 9.10510E-002 +194700196 -3.15514E-001 8.98822E-002 6.63844E-001 -1.20386E-001 6.64131E-001 -1.21657E-001 -3.15058E-001 9.09743E-002 +195582924 -3.15169E-001 9.02526E-002 6.64268E-001 -1.20557E-001 6.64506E-001 -1.22091E-001 -3.14628E-001 9.17624E-002 +196465652 -3.15080E-001 9.05076E-002 6.64245E-001 -1.21048E-001 6.65009E-001 -1.22545E-001 -3.14755E-001 9.21237E-002 +197348380 -3.15367E-001 9.09914E-002 6.64680E-001 -1.21497E-001 6.64439E-001 -1.22809E-001 -3.14594E-001 9.24540E-002 +198231108 -3.15142E-001 9.12419E-002 6.64037E-001 -1.22152E-001 6.64823E-001 -1.23518E-001 -3.14622E-001 9.26309E-002 +199113837 -3.14945E-001 9.16441E-002 6.63993E-001 -1.22428E-001 6.63702E-001 -1.24001E-001 -3.14473E-001 9.27924E-002 +199996565 -3.14710E-001 9.20192E-002 6.63886E-001 -1.23002E-001 6.64094E-001 -1.24669E-001 -3.14405E-001 9.35119E-002 +200879293 -3.14856E-001 9.24734E-002 6.63809E-001 -1.23411E-001 6.63967E-001 -1.25340E-001 -3.14143E-001 9.36334E-002 +201762021 -3.14491E-001 9.27296E-002 6.63692E-001 -1.24018E-001 6.63923E-001 -1.25553E-001 -3.13992E-001 9.39952E-002 +202644749 -3.14747E-001 9.32341E-002 6.64113E-001 -1.24493E-001 6.63841E-001 -1.25935E-001 -3.14221E-001 9.46194E-002 +203527477 -3.14667E-001 9.37556E-002 6.64025E-001 -1.25099E-001 6.63727E-001 -1.26672E-001 -3.13722E-001 9.48655E-002 +204410206 -3.14201E-001 9.39799E-002 6.63911E-001 -1.25481E-001 6.64164E-001 -1.26888E-001 -3.13650E-001 9.54806E-002 +205292934 -3.14260E-001 9.44235E-002 6.63419E-001 -1.25768E-001 6.63595E-001 -1.27566E-001 -3.13807E-001 9.54481E-002 +206175662 -3.14128E-001 9.46905E-002 6.64221E-001 -1.26435E-001 6.63945E-001 -1.27877E-001 -3.13586E-001 9.61315E-002 +207058390 -3.14071E-001 9.50610E-002 6.63700E-001 -1.26687E-001 6.63899E-001 -1.28356E-001 -3.13578E-001 9.62633E-002 +207941118 -3.14359E-001 9.53936E-002 6.63649E-001 -1.26945E-001 6.63873E-001 -1.28483E-001 -3.13841E-001 9.67160E-002 +208823846 -3.13925E-001 9.55641E-002 6.63536E-001 -1.27559E-001 6.63733E-001 -1.29229E-001 -3.13621E-001 9.69924E-002 +209706575 -3.13555E-001 9.59300E-002 6.63437E-001 -1.28051E-001 6.63658E-001 -1.29590E-001 -3.13237E-001 9.74165E-002 +210589303 -3.13976E-001 9.66119E-002 6.63348E-001 -1.28558E-001 6.63542E-001 -1.30227E-001 -3.13189E-001 9.80596E-002 +211472031 -3.13890E-001 9.71856E-002 6.63295E-001 -1.28791E-001 6.63965E-001 -1.30690E-001 -3.13178E-001 9.82767E-002 +212354759 -3.14020E-001 9.71263E-002 6.63721E-001 -1.29241E-001 6.63888E-001 -1.31043E-001 -3.13004E-001 9.85241E-002 +213237487 -3.13517E-001 9.76342E-002 6.63079E-001 -1.29908E-001 6.63771E-001 -1.31678E-001 -3.13018E-001 9.88322E-002 +214120215 -3.13382E-001 9.78686E-002 6.62942E-001 -1.30400E-001 6.64161E-001 -1.32138E-001 -3.12844E-001 9.92441E-002 +215002944 -3.13385E-001 9.82606E-002 6.63447E-001 -1.30741E-001 6.63662E-001 -1.32282E-001 -3.12580E-001 9.97621E-002 +215885672 -3.13381E-001 9.87569E-002 6.63355E-001 -1.31090E-001 6.63966E-001 -1.33255E-001 -3.13094E-001 1.00063E-001 +216768400 -3.13434E-001 9.92999E-002 6.63258E-001 -1.31609E-001 6.63920E-001 -1.33513E-001 -3.12652E-001 1.00682E-001 +217651128 -3.13014E-001 9.92056E-002 6.63137E-001 -1.32216E-001 6.63270E-001 -1.34150E-001 -3.12447E-001 1.00696E-001 +218533856 -3.12937E-001 9.97707E-002 6.63079E-001 -1.32592E-001 6.63739E-001 -1.34497E-001 -3.12699E-001 1.00837E-001 +219416584 -3.12799E-001 1.00150E-001 6.62918E-001 -1.33215E-001 6.63602E-001 -1.34989E-001 -3.12388E-001 1.01987E-001 +220299313 -3.12905E-001 1.00498E-001 6.63389E-001 -1.33564E-001 6.63571E-001 -1.35237E-001 -3.11894E-001 1.01825E-001 +221182041 -3.12710E-001 1.00636E-001 6.63251E-001 -1.34165E-001 6.63405E-001 -1.35969E-001 -3.11938E-001 1.01955E-001 +222064769 -3.12692E-001 1.00980E-001 6.63122E-001 -1.34797E-001 6.63301E-001 -1.36471E-001 -3.11893E-001 1.02416E-001 +222947497 -3.12514E-001 1.01618E-001 6.63121E-001 -1.34904E-001 6.63274E-001 -1.36709E-001 -3.11753E-001 1.02875E-001 +223830225 -3.12621E-001 1.02009E-001 6.63038E-001 -1.35272E-001 6.63691E-001 -1.37180E-001 -3.11818E-001 1.03442E-001 +224712953 -3.12322E-001 1.01918E-001 6.62960E-001 -1.35614E-001 6.63611E-001 -1.37523E-001 -3.11068E-001 1.03247E-001 +225595682 -3.12305E-001 1.02547E-001 6.62830E-001 -1.36268E-001 6.62952E-001 -1.38204E-001 -3.11713E-001 1.04089E-001 +226478410 -3.12309E-001 1.02965E-001 6.63238E-001 -1.36856E-001 6.63359E-001 -1.38793E-001 -3.11744E-001 1.04389E-001 +227361138 -3.12542E-001 1.03430E-001 6.62630E-001 -1.37242E-001 6.63304E-001 -1.39022E-001 -3.11270E-001 1.04815E-001 +228243866 -3.12615E-001 1.03834E-001 6.62599E-001 -1.37491E-001 6.63273E-001 -1.39271E-001 -3.11568E-001 1.05269E-001 +229126594 -3.12046E-001 1.03837E-001 6.62955E-001 -1.38209E-001 6.63600E-001 -1.40122E-001 -3.10999E-001 1.05270E-001 +230009322 -3.11687E-001 1.04549E-001 6.62877E-001 -1.38567E-001 6.63020E-001 -1.40373E-001 -3.10680E-001 1.05803E-001 +230892051 -3.11953E-001 1.04941E-001 6.63299E-001 -1.39029E-001 6.63386E-001 -1.41100E-001 -3.11113E-001 1.06485E-001 +231774779 -3.11412E-001 1.04956E-001 6.62693E-001 -1.39434E-001 6.63306E-001 -1.41479E-001 -3.10799E-001 1.06550E-001 +232657507 -3.11621E-001 1.05502E-001 6.63094E-001 -1.40035E-001 6.63233E-001 -1.41844E-001 -3.10837E-001 1.06808E-001 +233540235 -3.11388E-001 1.06135E-001 6.62454E-001 -1.40548E-001 6.63092E-001 -1.42463E-001 -3.10391E-001 1.07326E-001 +234422963 -3.11056E-001 1.06521E-001 6.62404E-001 -1.40780E-001 6.62513E-001 -1.42719E-001 -3.10269E-001 1.07823E-001 +235305691 -3.11203E-001 1.06617E-001 6.62286E-001 -1.41265E-001 6.62894E-001 -1.43312E-001 -3.10372E-001 1.08096E-001 +236188420 -3.11071E-001 1.07144E-001 6.62213E-001 -1.41644E-001 6.62319E-001 -1.43582E-001 -3.10493E-001 1.08558E-001 +237071148 -3.11192E-001 1.07577E-001 6.62653E-001 -1.42121E-001 6.62730E-001 -1.44192E-001 -3.09940E-001 1.08827E-001 +237953876 -3.11018E-001 1.07860E-001 6.62012E-001 -1.42636E-001 6.63145E-001 -1.44663E-001 -3.10098E-001 1.09688E-001 +238836604 -3.10806E-001 1.08393E-001 6.62427E-001 -1.43101E-001 6.62500E-001 -1.45172E-001 -3.10000E-001 1.09750E-001 +239719332 -3.10540E-001 1.08515E-001 6.62375E-001 -1.43489E-001 6.62419E-001 -1.45691E-001 -3.09887E-001 1.10219E-001 +240602060 -3.10361E-001 1.09044E-001 6.62229E-001 -1.44107E-001 6.62829E-001 -1.46157E-001 -3.09763E-001 1.10511E-001 +241484789 -3.10330E-001 1.09205E-001 6.62551E-001 -1.44966E-001 6.62118E-001 -1.46927E-001 -3.09731E-001 1.10672E-001 +242367517 -3.10700E-001 1.09489E-001 6.63050E-001 -1.45028E-001 6.62560E-001 -1.47252E-001 -3.09817E-001 1.11136E-001 +243250245 -3.10363E-001 1.10040E-001 6.62956E-001 -1.45547E-001 6.62493E-001 -1.47640E-001 -3.09315E-001 1.11393E-001 +244132973 -3.10215E-001 1.10320E-001 6.62875E-001 -1.45898E-001 6.62382E-001 -1.48122E-001 -3.09389E-001 1.11729E-001 +245015701 -3.10387E-001 1.10880E-001 6.62177E-001 -1.46689E-001 6.62269E-001 -1.48631E-001 -3.09574E-001 1.12229E-001 +245898429 -3.10309E-001 1.11551E-001 6.62134E-001 -1.47043E-001 6.62225E-001 -1.48985E-001 -3.09256E-001 1.12902E-001 +246781157 -3.09654E-001 1.11533E-001 6.62534E-001 -1.47381E-001 6.63095E-001 -1.49568E-001 -3.09272E-001 1.13051E-001 +247663886 -3.09758E-001 1.11987E-001 6.62439E-001 -1.47759E-001 6.62469E-001 -1.49965E-001 -3.08912E-001 1.13449E-001 +248546614 -3.09715E-001 1.12321E-001 6.62338E-001 -1.48226E-001 6.62837E-001 -1.50675E-001 -3.09047E-001 1.14016E-001 +249429342 -3.09685E-001 1.12553E-001 6.62267E-001 -1.48633E-001 6.62795E-001 -1.50952E-001 -3.08779E-001 1.14247E-001 +250312070 -3.09511E-001 1.12596E-001 6.62577E-001 -1.49624E-001 6.62101E-001 -1.51717E-001 -3.08590E-001 1.14347E-001 +251194798 -3.09459E-001 1.13435E-001 6.62011E-001 -1.49715E-001 6.62034E-001 -1.51920E-001 -3.08639E-001 1.14777E-001 +252077526 -3.09344E-001 1.13606E-001 6.61900E-001 -1.50252E-001 6.62452E-001 -1.52441E-001 -3.08212E-001 1.15238E-001 +252960255 -3.09359E-001 1.14621E-001 6.61842E-001 -1.50447E-001 6.61834E-001 -1.52783E-001 -3.08567E-001 1.15845E-001 +253842983 -3.08974E-001 1.14556E-001 6.62260E-001 -1.51236E-001 6.61718E-001 -1.53589E-001 -3.08329E-001 1.16126E-001 +254725711 -3.08886E-001 1.14897E-001 6.62053E-001 -1.51816E-001 6.62570E-001 -1.54140E-001 -3.07956E-001 1.16641E-001 +255608439 -3.09197E-001 1.15334E-001 6.62494E-001 -1.52191E-001 6.62511E-001 -1.54400E-001 -3.08089E-001 1.16844E-001 +256491167 -3.08683E-001 1.15815E-001 6.62295E-001 -1.53049E-001 6.61839E-001 -1.55010E-001 -3.08079E-001 1.17208E-001 +257373895 -3.08805E-001 1.16205E-001 6.61761E-001 -1.53075E-001 6.62182E-001 -1.55792E-001 -3.07916E-001 1.17772E-001 +258256624 -3.08724E-001 1.16396E-001 6.61548E-001 -1.54080E-001 6.62181E-001 -1.55882E-001 -3.07361E-001 1.17958E-001 +259139352 -3.08240E-001 1.16711E-001 6.61458E-001 -1.54418E-001 6.61497E-001 -1.56495E-001 -3.07319E-001 1.18389E-001 +260022080 -3.08132E-001 1.17125E-001 6.61454E-001 -1.54413E-001 6.61869E-001 -1.57131E-001 -3.07431E-001 1.18862E-001 +260904808 -3.07918E-001 1.17525E-001 6.61407E-001 -1.54939E-001 6.61445E-001 -1.57016E-001 -3.07278E-001 1.19029E-001 +261787536 -3.08153E-001 1.17923E-001 6.62189E-001 -1.55759E-001 6.62663E-001 -1.58219E-001 -3.06977E-001 1.19653E-001 +262670264 -3.07974E-001 1.18622E-001 6.61520E-001 -1.55968E-001 6.61961E-001 -1.58558E-001 -3.07046E-001 1.20295E-001 +263552993 -3.08025E-001 1.18930E-001 6.61024E-001 -1.56586E-001 6.61026E-001 -1.58793E-001 -3.07128E-001 1.20487E-001 +264435721 -3.07946E-001 1.19178E-001 6.61839E-001 -1.57202E-001 6.61775E-001 -1.59673E-001 -3.07016E-001 1.20849E-001 +265318449 -3.07854E-001 1.19632E-001 6.61807E-001 -1.57374E-001 6.61244E-001 -1.59725E-001 -3.06765E-001 1.21011E-001 +266201177 -3.07292E-001 1.20198E-001 6.61141E-001 -1.57958E-001 6.61574E-001 -1.60549E-001 -3.06359E-001 1.21865E-001 +267083905 -3.06964E-001 1.20397E-001 6.61737E-001 -1.58106E-001 6.61139E-001 -1.60588E-001 -3.06600E-001 1.21722E-001 +267966633 -3.07002E-001 1.20664E-001 6.61964E-001 -1.58958E-001 6.61490E-001 -1.60918E-001 -3.06304E-001 1.22331E-001 +268849362 -3.06925E-001 1.21076E-001 6.61340E-001 -1.59328E-001 6.61897E-001 -1.61399E-001 -3.06211E-001 1.22799E-001 +269732090 -3.06257E-001 1.21361E-001 6.60751E-001 -1.59604E-001 6.61178E-001 -1.62197E-001 -3.05762E-001 1.23142E-001 +270614818 -3.06857E-001 1.21922E-001 6.61112E-001 -1.60267E-001 6.61440E-001 -1.63253E-001 -3.05919E-001 1.23582E-001 +271497546 -3.06329E-001 1.22114E-001 6.61153E-001 -1.60080E-001 6.61483E-001 -1.63066E-001 -3.05423E-001 1.23656E-001 +272380274 -3.06404E-001 1.22617E-001 6.60764E-001 -1.61639E-001 6.61281E-001 -1.63843E-001 -3.05196E-001 1.24383E-001 +273263002 -3.06282E-001 1.23149E-001 6.60929E-001 -1.61471E-001 6.61382E-001 -1.63936E-001 -3.04968E-001 1.24450E-001 +274145731 -3.05800E-001 1.23443E-001 6.61173E-001 -1.62155E-001 6.61125E-001 -1.64498E-001 -3.05142E-001 1.24927E-001 +275028459 -3.05675E-001 1.24065E-001 6.61065E-001 -1.62612E-001 6.61449E-001 -1.65341E-001 -3.05250E-001 1.25555E-001 +275911187 -3.05759E-001 1.24050E-001 6.60985E-001 -1.62915E-001 6.60901E-001 -1.65388E-001 -3.05050E-001 1.25706E-001 +276793915 -3.05658E-001 1.24379E-001 6.61288E-001 -1.63829E-001 6.61234E-001 -1.66173E-001 -3.05150E-001 1.26154E-001 +277676643 -3.05742E-001 1.25057E-001 6.60890E-001 -1.63786E-001 6.61335E-001 -1.66254E-001 -3.04473E-001 1.26178E-001 +278559371 -3.05353E-001 1.25430E-001 6.60484E-001 -1.64461E-001 6.60959E-001 -1.66798E-001 -3.04220E-001 1.26898E-001 +279442100 -3.05194E-001 1.25551E-001 6.60684E-001 -1.64685E-001 6.60527E-001 -1.67419E-001 -3.04027E-001 1.27131E-001 +280324828 -3.05027E-001 1.25974E-001 6.61293E-001 -1.65922E-001 6.61232E-001 -1.68268E-001 -3.03858E-001 1.27552E-001 +281207556 -3.05121E-001 1.26374E-001 6.60711E-001 -1.66130E-001 6.60682E-001 -1.68344E-001 -3.04086E-001 1.28300E-001 +282090284 -3.05073E-001 1.27059E-001 6.60628E-001 -1.66388E-001 6.60498E-001 -1.68993E-001 -3.03632E-001 1.28742E-001 +282973012 -3.04603E-001 1.27569E-001 6.60706E-001 -1.66657E-001 6.60541E-001 -1.69392E-001 -3.03698E-001 1.29035E-001 +283855740 -3.04659E-001 1.27773E-001 6.60809E-001 -1.67835E-001 6.60774E-001 -1.70050E-001 -3.03485E-001 1.29344E-001 +284738469 -3.04079E-001 1.27944E-001 6.60772E-001 -1.68042E-001 6.60771E-001 -1.70127E-001 -3.03457E-001 1.30046E-001 +285621197 -3.04042E-001 1.28483E-001 6.60651E-001 -1.68437E-001 6.61012E-001 -1.71172E-001 -3.02867E-001 1.30050E-001 +286503925 -3.03974E-001 1.28755E-001 6.60675E-001 -1.68403E-001 6.61002E-001 -1.71269E-001 -3.02730E-001 1.30547E-001 +287386653 -3.03967E-001 1.29466E-001 6.60613E-001 -1.69190E-001 6.60608E-001 -1.71275E-001 -3.02286E-001 1.31125E-001 +288269381 -3.03577E-001 1.29682E-001 6.60339E-001 -1.69642E-001 6.61229E-001 -1.72378E-001 -3.02096E-001 1.31462E-001 +289152109 -3.03345E-001 1.30453E-001 6.61093E-001 -1.70236E-001 6.60518E-001 -1.72454E-001 -3.02017E-001 1.31721E-001 +290034838 -3.03222E-001 1.30500E-001 6.60141E-001 -1.70368E-001 6.60098E-001 -1.72583E-001 -3.02171E-001 1.32406E-001 +290917566 -3.03205E-001 1.31082E-001 6.59707E-001 -1.70645E-001 6.60627E-001 -1.73251E-001 -3.02005E-001 1.32694E-001 +291800294 -3.02453E-001 1.31339E-001 6.60378E-001 -1.71524E-001 6.60332E-001 -1.73740E-001 -3.01271E-001 1.32891E-001 +292683022 -3.02726E-001 1.31809E-001 6.60242E-001 -1.72070E-001 6.60159E-001 -1.74416E-001 -3.01040E-001 1.33453E-001 +293565750 -3.01790E-001 1.31970E-001 6.59644E-001 -1.72335E-001 6.60557E-001 -1.74944E-001 -3.00502E-001 1.33854E-001 +294448478 -3.02422E-001 1.32366E-001 6.60697E-001 -1.73049E-001 6.59510E-001 -1.75524E-001 -3.01148E-001 1.34195E-001 +295331207 -3.02085E-001 1.32866E-001 6.59995E-001 -1.73035E-001 6.60130E-001 -1.76557E-001 -3.00380E-001 1.34557E-001 +296213935 -3.01778E-001 1.33223E-001 6.60254E-001 -1.73428E-001 6.59564E-001 -1.76034E-001 -3.00074E-001 1.34910E-001 +297096663 -3.01564E-001 1.33551E-001 6.59792E-001 -1.74434E-001 6.59133E-001 -1.76908E-001 -3.00393E-001 1.35035E-001 +297979391 -3.01602E-001 1.34191E-001 6.59478E-001 -1.74931E-001 6.59848E-001 -1.77541E-001 -3.00302E-001 1.35327E-001 +298862119 -3.01624E-001 1.34185E-001 6.59803E-001 -1.75713E-001 6.59104E-001 -1.78317E-001 -3.00329E-001 1.36062E-001 +299744847 -3.00602E-001 1.36691E-001 6.64455E-001 -1.69211E-001 6.63612E-001 -1.72489E-001 -2.99938E-001 1.38023E-001 +300627576 -2.99082E-001 1.36087E-001 6.49383E-001 -1.85702E-001 6.47130E-001 -1.86164E-001 -2.97869E-001 1.38398E-001 +301510304 -2.99398E-001 1.36794E-001 6.46022E-001 -1.84446E-001 6.47611E-001 -1.86283E-001 -2.98030E-001 1.37399E-001 +302393032 -2.97402E-001 1.36272E-001 6.47734E-001 -1.85668E-001 6.47255E-001 -1.87330E-001 -2.96444E-001 1.37065E-001 +303275760 -2.98400E-001 1.36084E-001 6.47691E-001 -1.84118E-001 6.48195E-001 -1.86061E-001 -2.97261E-001 1.37433E-001 +304158488 -2.97371E-001 1.36831E-001 6.45066E-001 -1.87175E-001 6.46270E-001 -1.88494E-001 -2.95600E-001 1.37968E-001 +305041216 -2.97630E-001 1.38654E-001 6.47199E-001 -1.85859E-001 6.46912E-001 -1.88684E-001 -2.96428E-001 1.40162E-001 +305923945 -2.97977E-001 1.38078E-001 6.46760E-001 -1.87231E-001 6.47557E-001 -1.89958E-001 -2.97664E-001 1.39757E-001 +306806673 -2.98330E-001 1.38567E-001 6.44706E-001 -1.88331E-001 6.44255E-001 -1.91656E-001 -2.96607E-001 1.40949E-001 +307689401 -2.97747E-001 1.37316E-001 6.45490E-001 -1.88055E-001 6.47032E-001 -1.91835E-001 -2.95473E-001 1.40665E-001 +308572129 -2.96854E-001 1.38626E-001 6.45982E-001 -1.88900E-001 6.46358E-001 -1.91228E-001 -2.96672E-001 1.40589E-001 +309454857 -2.97131E-001 1.39807E-001 6.46330E-001 -1.89235E-001 6.46255E-001 -1.91293E-001 -2.95846E-001 1.40853E-001 +310337585 -2.95688E-001 1.40836E-001 6.46069E-001 -1.89286E-001 6.48884E-001 -1.94152E-001 -2.94948E-001 1.43036E-001 +311220314 -2.96340E-001 1.39745E-001 6.47919E-001 -1.89357E-001 6.46038E-001 -1.92135E-001 -2.95670E-001 1.41051E-001 +312103042 -2.97043E-001 1.39530E-001 6.44436E-001 -1.92026E-001 6.45056E-001 -1.95266E-001 -2.94166E-001 1.41863E-001 +312985770 -2.96230E-001 1.40313E-001 6.46679E-001 -1.91215E-001 6.45497E-001 -1.95169E-001 -2.94374E-001 1.43051E-001 +313868498 -2.95192E-001 1.41303E-001 6.43920E-001 -1.92264E-001 6.44731E-001 -1.94866E-001 -2.94458E-001 1.42093E-001 +314751226 -2.95003E-001 1.42031E-001 6.44613E-001 -1.89907E-001 6.44513E-001 -1.95561E-001 -2.93081E-001 1.43576E-001 +315633954 -2.96242E-001 1.43837E-001 6.43375E-001 -1.92949E-001 6.42507E-001 -1.94072E-001 -2.94758E-001 1.44743E-001 +316516683 -2.96356E-001 1.42505E-001 6.40660E-001 -1.91198E-001 6.41375E-001 -1.95833E-001 -2.95226E-001 1.44451E-001 +317399411 -2.94748E-001 1.40709E-001 6.44242E-001 -1.93454E-001 6.45265E-001 -1.97099E-001 -2.93823E-001 1.44784E-001 +318282139 -2.95035E-001 1.42565E-001 6.45840E-001 -1.96355E-001 6.44710E-001 -1.96567E-001 -2.93262E-001 1.43666E-001 +319164867 -2.95672E-001 1.43074E-001 6.43807E-001 -1.93390E-001 6.43424E-001 -1.94661E-001 -2.93555E-001 1.45168E-001 +320047595 -2.94594E-001 1.44189E-001 6.46894E-001 -1.94018E-001 6.45518E-001 -1.96804E-001 -2.90773E-001 1.45794E-001 +320930323 -2.94985E-001 1.44781E-001 6.43354E-001 -1.96775E-001 6.46843E-001 -1.97563E-001 -2.93373E-001 1.44713E-001 +321813052 -2.94940E-001 1.43766E-001 6.42698E-001 -1.95512E-001 6.45190E-001 -1.97802E-001 -2.92238E-001 1.44843E-001 +322695780 -2.95250E-001 1.45215E-001 6.44538E-001 -1.96315E-001 6.43166E-001 -2.00765E-001 -2.91835E-001 1.47630E-001 +323578508 -2.93260E-001 1.45114E-001 6.41793E-001 -1.98085E-001 6.43697E-001 -1.98812E-001 -2.91523E-001 1.46765E-001 +324461236 -2.92673E-001 1.45997E-001 6.43668E-001 -1.97046E-001 6.44870E-001 -2.00063E-001 -2.90706E-001 1.46953E-001 +325343964 -2.92279E-001 1.44815E-001 6.43063E-001 -1.96899E-001 6.45289E-001 -2.01769E-001 -2.90133E-001 1.46317E-001 +326226692 -2.92512E-001 1.46392E-001 6.43077E-001 -1.98426E-001 6.42972E-001 -2.00481E-001 -2.91499E-001 1.49265E-001 +327109421 -2.93623E-001 1.45966E-001 6.42968E-001 -1.97896E-001 6.43232E-001 -2.00483E-001 -2.91903E-001 1.47557E-001 +327992149 -2.92654E-001 1.47444E-001 6.41760E-001 -1.98504E-001 6.43755E-001 -2.00654E-001 -2.91214E-001 1.48211E-001 +328874877 -2.90866E-001 1.46411E-001 6.43401E-001 -1.99259E-001 6.42975E-001 -2.02331E-001 -2.90155E-001 1.48402E-001 +329757605 -2.92080E-001 1.46577E-001 6.43360E-001 -1.99398E-001 6.40224E-001 -2.00923E-001 -2.90525E-001 1.48995E-001 +330640333 -2.92908E-001 1.48095E-001 6.40965E-001 -2.01740E-001 6.39397E-001 -2.04999E-001 -2.92300E-001 1.49156E-001 +331523061 -2.92524E-001 1.48017E-001 6.42136E-001 -2.01432E-001 6.42063E-001 -2.05035E-001 -2.91536E-001 1.49491E-001 +332405789 -2.92073E-001 1.47220E-001 6.42071E-001 -2.01725E-001 6.44586E-001 -2.03915E-001 -2.89535E-001 1.48500E-001 +333288518 -2.91120E-001 1.48435E-001 6.42393E-001 -1.99806E-001 6.42449E-001 -2.03028E-001 -2.90466E-001 1.50263E-001 +334171246 -2.90465E-001 1.48521E-001 6.39312E-001 -2.03500E-001 6.40521E-001 -2.04721E-001 -2.88815E-001 1.50533E-001 +335053974 -2.91742E-001 1.50115E-001 6.42636E-001 -2.03959E-001 6.41907E-001 -2.06241E-001 -2.89824E-001 1.51550E-001 +335936702 -2.91041E-001 1.49859E-001 6.43593E-001 -2.02810E-001 6.43070E-001 -2.04461E-001 -2.89393E-001 1.52479E-001 +336819430 -2.89979E-001 1.50471E-001 6.41745E-001 -2.01778E-001 6.40741E-001 -2.04944E-001 -2.88358E-001 1.52367E-001 +337702158 -2.90283E-001 1.51884E-001 6.41435E-001 -2.02711E-001 6.41307E-001 -2.08116E-001 -2.88434E-001 1.52481E-001 +338584887 -2.90432E-001 1.50752E-001 6.41445E-001 -2.06022E-001 6.36929E-001 -2.06793E-001 -2.87663E-001 1.51981E-001 +339467615 -2.89671E-001 1.50075E-001 6.39671E-001 -2.00931E-001 6.40483E-001 -2.05083E-001 -2.87804E-001 1.51384E-001 +340350343 -2.90529E-001 1.52051E-001 6.39115E-001 -2.05225E-001 6.38700E-001 -2.08156E-001 -2.87699E-001 1.51550E-001 +341233071 -3.06349E-001 1.44393E-001 6.57640E-001 -2.47410E-001 5.98146E-001 -1.87623E-001 -2.78199E-001 1.67749E-001 +342115799 -2.88875E-001 1.50850E-001 6.40651E-001 -2.06075E-001 6.39460E-001 -2.09740E-001 -2.87289E-001 1.52017E-001 +342998527 -2.89044E-001 1.52054E-001 6.40856E-001 -2.05460E-001 6.38713E-001 -2.07139E-001 -2.87260E-001 1.53749E-001 +343881256 -2.88144E-001 1.51904E-001 6.38532E-001 -2.05198E-001 6.40449E-001 -2.09167E-001 -2.87543E-001 1.54160E-001 +344763984 -2.88283E-001 1.51658E-001 6.39024E-001 -2.06118E-001 6.38040E-001 -2.09144E-001 -2.87412E-001 1.53390E-001 +345646712 -2.87514E-001 1.51414E-001 6.37952E-001 -2.08082E-001 6.37189E-001 -2.12017E-001 -2.86335E-001 1.52118E-001 +346529440 -2.88793E-001 1.52140E-001 6.38064E-001 -2.08991E-001 6.38030E-001 -2.12333E-001 -2.86814E-001 1.54963E-001 +347412168 -2.88376E-001 1.52400E-001 6.39115E-001 -2.08374E-001 6.36883E-001 -2.13505E-001 -2.86119E-001 1.55963E-001 +348294896 -2.86738E-001 1.55178E-001 6.36532E-001 -2.10551E-001 6.38292E-001 -2.11692E-001 -2.87165E-001 1.55883E-001 +349177625 -2.87435E-001 1.53093E-001 6.40139E-001 -2.09547E-001 6.37331E-001 -2.14775E-001 -2.86101E-001 1.57237E-001 +350060353 -2.88207E-001 1.55477E-001 6.39518E-001 -2.10379E-001 6.38386E-001 -2.10566E-001 -2.86121E-001 1.54270E-001 +350943081 -2.86859E-001 1.54017E-001 6.36001E-001 -2.11599E-001 6.37066E-001 -2.13212E-001 -2.85319E-001 1.57470E-001 +351825809 -2.88762E-001 1.54999E-001 6.38004E-001 -2.11897E-001 6.38140E-001 -2.16288E-001 -2.85923E-001 1.57610E-001 +352708537 -2.86451E-001 1.54914E-001 6.38263E-001 -2.11549E-001 6.37777E-001 -2.14608E-001 -2.84522E-001 1.58756E-001 +353591265 -2.86656E-001 1.56759E-001 6.40086E-001 -2.11499E-001 6.38552E-001 -2.14494E-001 -2.84562E-001 1.56803E-001 +354473994 -2.85915E-001 1.54263E-001 6.35697E-001 -2.11411E-001 6.33528E-001 -2.16262E-001 -2.82990E-001 1.58307E-001 +355356722 -2.84774E-001 1.55557E-001 6.36309E-001 -2.10755E-001 6.35134E-001 -2.14269E-001 -2.83548E-001 1.57546E-001 +356239450 -2.84901E-001 1.56612E-001 6.36962E-001 -2.13874E-001 6.33659E-001 -2.14158E-001 -2.82433E-001 1.57642E-001 +357122178 -2.82484E-001 1.55674E-001 6.39257E-001 -2.09922E-001 6.39535E-001 -2.13938E-001 -2.81771E-001 1.58689E-001 +358004906 -2.83989E-001 1.58857E-001 6.35115E-001 -2.15505E-001 6.38010E-001 -2.10045E-001 -2.82921E-001 1.56879E-001 +358887634 -2.85481E-001 1.56607E-001 6.35838E-001 -2.14040E-001 6.37776E-001 -2.16236E-001 -2.83805E-001 1.59150E-001 +359770363 -2.84730E-001 1.56188E-001 6.36690E-001 -2.13263E-001 6.35201E-001 -2.17658E-001 -2.82549E-001 1.58282E-001 +360653091 -2.83710E-001 1.56924E-001 6.35699E-001 -2.12971E-001 6.35557E-001 -2.16559E-001 -2.82630E-001 1.59684E-001 +361535819 -2.84356E-001 1.57031E-001 6.36273E-001 -2.14958E-001 6.35727E-001 -2.18136E-001 -2.82689E-001 1.58963E-001 +362418547 -2.83166E-001 1.57518E-001 6.35418E-001 -2.15065E-001 6.35258E-001 -2.17110E-001 -2.82879E-001 1.60589E-001 +363301275 -2.83049E-001 1.58146E-001 6.36423E-001 -2.16513E-001 6.35433E-001 -2.19401E-001 -2.81868E-001 1.60555E-001 +364184003 -2.85124E-001 1.60207E-001 6.33782E-001 -2.16522E-001 6.33789E-001 -2.18063E-001 -2.81773E-001 1.61103E-001 +365066732 -2.82009E-001 1.58677E-001 6.34628E-001 -2.16207E-001 6.33071E-001 -2.17631E-001 -2.79696E-001 1.59320E-001 +365949460 -2.82360E-001 1.58348E-001 6.35416E-001 -2.16629E-001 6.34906E-001 -2.19678E-001 -2.80390E-001 1.60446E-001 +366832188 -2.82313E-001 1.58632E-001 6.36389E-001 -2.16567E-001 6.35488E-001 -2.20750E-001 -2.80169E-001 1.60595E-001 +367714916 -2.81422E-001 1.59852E-001 6.33804E-001 -2.16464E-001 6.36520E-001 -2.21047E-001 -2.80870E-001 1.59515E-001 +368597644 -2.82123E-001 1.60586E-001 6.36518E-001 -2.18268E-001 6.34734E-001 -2.21867E-001 -2.81169E-001 1.60684E-001 +369480372 -2.81044E-001 1.58075E-001 6.35840E-001 -2.18676E-001 6.36281E-001 -2.22064E-001 -2.80159E-001 1.63748E-001 +370363101 -2.80578E-001 1.60987E-001 6.33811E-001 -2.20055E-001 6.33333E-001 -2.19889E-001 -2.80496E-001 1.61193E-001 +371245829 -2.81194E-001 1.61787E-001 6.34157E-001 -2.19890E-001 6.35068E-001 -2.23441E-001 -2.80613E-001 1.62093E-001 +372128557 -2.83661E-001 1.61980E-001 6.34656E-001 -2.20729E-001 6.34263E-001 -2.21857E-001 -2.79646E-001 1.62883E-001 +373011285 -2.82048E-001 1.63635E-001 6.34983E-001 -2.20257E-001 6.31612E-001 -2.25254E-001 -2.79183E-001 1.63366E-001 +373894013 -2.80912E-001 1.62028E-001 6.32801E-001 -2.19837E-001 6.32443E-001 -2.23919E-001 -2.77296E-001 1.64182E-001 +374776741 -2.81311E-001 1.62188E-001 6.35295E-001 -2.19835E-001 6.35028E-001 -2.23681E-001 -2.78643E-001 1.64268E-001 +375659470 -2.80974E-001 1.62135E-001 6.31972E-001 -2.22116E-001 6.30303E-001 -2.25311E-001 -2.79419E-001 1.63718E-001 +376542198 -2.79518E-001 1.62266E-001 6.33463E-001 -2.22119E-001 6.32589E-001 -2.23075E-001 -2.77674E-001 1.62886E-001 +377424926 -2.80016E-001 1.60757E-001 6.33824E-001 -2.20458E-001 6.33375E-001 -2.24797E-001 -2.77918E-001 1.62585E-001 +378307654 -2.78785E-001 1.62559E-001 6.30362E-001 -2.23239E-001 6.30129E-001 -2.25399E-001 -2.77572E-001 1.65507E-001 +379190382 -2.79836E-001 1.64285E-001 6.31984E-001 -2.23092E-001 6.31886E-001 -2.24883E-001 -2.78505E-001 1.64752E-001 +380073110 -2.78825E-001 1.62481E-001 6.32149E-001 -2.21146E-001 6.33382E-001 -2.26784E-001 -2.77095E-001 1.65040E-001 +380955839 -2.78724E-001 1.62887E-001 6.33132E-001 -2.23848E-001 6.30901E-001 -2.25585E-001 -2.76246E-001 1.63948E-001 +381838567 -2.79261E-001 1.63056E-001 6.30534E-001 -2.23632E-001 6.31686E-001 -2.26431E-001 -2.78355E-001 1.64710E-001 +382721295 -2.80512E-001 1.63973E-001 6.33852E-001 -2.22144E-001 6.31079E-001 -2.26925E-001 -2.78739E-001 1.66047E-001 +383604023 -2.78967E-001 1.64291E-001 6.32224E-001 -2.25567E-001 6.32822E-001 -2.26908E-001 -2.77293E-001 1.67225E-001 +384486751 -2.78251E-001 1.64857E-001 6.31874E-001 -2.24734E-001 6.32279E-001 -2.28118E-001 -2.76526E-001 1.67350E-001 +385369479 -2.77881E-001 1.65268E-001 6.32222E-001 -2.23557E-001 6.31164E-001 -2.29512E-001 -2.76199E-001 1.66586E-001 +386252208 -2.78273E-001 1.65498E-001 6.32682E-001 -2.24676E-001 6.30297E-001 -2.28324E-001 -2.78094E-001 1.67030E-001 +387134936 -2.77754E-001 1.65676E-001 6.31118E-001 -2.24989E-001 6.29253E-001 -2.28675E-001 -2.75237E-001 1.66815E-001 +388017664 -2.77898E-001 1.64654E-001 6.32164E-001 -2.24964E-001 6.32749E-001 -2.27849E-001 -2.75545E-001 1.64857E-001 +388900392 -2.76522E-001 1.64191E-001 6.30474E-001 -2.25742E-001 6.29918E-001 -2.28776E-001 -2.74094E-001 1.64063E-001 +389783120 -2.77264E-001 1.67271E-001 6.31253E-001 -2.27023E-001 6.29532E-001 -2.31753E-001 -2.74354E-001 1.67171E-001 +390665848 -2.76043E-001 1.66427E-001 6.30723E-001 -2.28149E-001 6.29477E-001 -2.30090E-001 -2.74935E-001 1.68504E-001 +391548577 -2.77700E-001 1.65961E-001 6.31119E-001 -2.27884E-001 6.29217E-001 -2.30151E-001 -2.74785E-001 1.69121E-001 +392431305 -2.77318E-001 1.65461E-001 6.30917E-001 -2.27886E-001 6.31259E-001 -2.31397E-001 -2.73919E-001 1.66583E-001 +393314033 -2.76943E-001 1.65311E-001 6.30147E-001 -2.28240E-001 6.28600E-001 -2.32465E-001 -2.74500E-001 1.68427E-001 +394196761 -2.75537E-001 1.66355E-001 6.31199E-001 -2.29943E-001 6.28183E-001 -2.30813E-001 -2.74944E-001 1.67227E-001 +395079489 -2.75649E-001 1.68395E-001 6.30750E-001 -2.27500E-001 6.31571E-001 -2.31184E-001 -2.75009E-001 1.68832E-001 +395962217 -2.76970E-001 1.67928E-001 6.30686E-001 -2.29529E-001 6.30480E-001 -2.31570E-001 -2.73781E-001 1.69055E-001 +396844946 -2.75179E-001 1.66778E-001 6.29824E-001 -2.30078E-001 6.30814E-001 -2.33268E-001 -2.72323E-001 1.68735E-001 +397727674 -2.75667E-001 1.66602E-001 6.27772E-001 -2.30334E-001 6.27911E-001 -2.34332E-001 -2.74905E-001 1.67340E-001 +398610402 -2.76504E-001 1.67531E-001 6.32079E-001 -2.27884E-001 6.32332E-001 -2.31650E-001 -2.72612E-001 1.70292E-001 +399493130 -2.73075E-001 1.66690E-001 6.30348E-001 -2.30205E-001 6.29461E-001 -2.35532E-001 -2.69946E-001 1.69266E-001 +400375858 -2.73510E-001 1.67615E-001 6.30428E-001 -2.30160E-001 6.30157E-001 -2.35292E-001 -2.72207E-001 1.70118E-001 +401258586 -2.74026E-001 1.67728E-001 6.27979E-001 -2.29245E-001 6.29121E-001 -2.33470E-001 -2.74106E-001 1.70179E-001 +402141315 -2.73498E-001 1.69214E-001 6.30083E-001 -2.31416E-001 6.28272E-001 -2.34843E-001 -2.71918E-001 1.68706E-001 +403024043 -2.73845E-001 1.69961E-001 6.29856E-001 -2.32611E-001 6.29315E-001 -2.35523E-001 -2.72054E-001 1.69425E-001 +403906771 -2.74057E-001 1.68656E-001 6.28593E-001 -2.31117E-001 6.30037E-001 -2.33061E-001 -2.71979E-001 1.70345E-001 +404789499 -2.73106E-001 1.67192E-001 6.28850E-001 -2.30015E-001 6.28425E-001 -2.34090E-001 -2.71288E-001 1.69334E-001 +405672227 -2.72165E-001 1.68697E-001 6.27478E-001 -2.31656E-001 6.28744E-001 -2.35514E-001 -2.72253E-001 1.71610E-001 +406554955 -2.72357E-001 1.68888E-001 6.28835E-001 -2.31550E-001 6.28436E-001 -2.34084E-001 -2.71045E-001 1.69319E-001 +407437684 -2.71996E-001 1.68655E-001 6.29020E-001 -2.30295E-001 6.29452E-001 -2.34973E-001 -2.71447E-001 1.68888E-001 +408320412 -2.72178E-001 1.69154E-001 6.29256E-001 -2.32290E-001 6.28716E-001 -2.35199E-001 -2.71290E-001 1.71175E-001 +409203140 -2.70911E-001 1.68652E-001 6.27242E-001 -2.33552E-001 6.27836E-001 -2.37734E-001 -2.70948E-001 1.69603E-001 +410085868 -2.71544E-001 1.71067E-001 6.28766E-001 -2.33332E-001 6.28174E-001 -2.36364E-001 -2.71726E-001 1.71676E-001 +410968596 -2.72456E-001 1.69628E-001 6.27252E-001 -2.33862E-001 6.28406E-001 -2.36556E-001 -2.70494E-001 1.70528E-001 +411851324 -2.71326E-001 1.69875E-001 6.28476E-001 -2.34325E-001 6.29819E-001 -2.36527E-001 -2.69576E-001 1.70800E-001 +412734052 -2.73028E-001 1.70199E-001 6.26681E-001 -2.35450E-001 6.28589E-001 -2.39004E-001 -2.71560E-001 1.72019E-001 +413616781 -2.73717E-001 1.70595E-001 6.29523E-001 -2.35480E-001 6.28070E-001 -2.39329E-001 -2.71010E-001 1.70118E-001 +414499509 -2.71371E-001 1.71033E-001 6.29255E-001 -2.34359E-001 6.28471E-001 -2.37890E-001 -2.70016E-001 1.72078E-001 +415382237 -2.70827E-001 1.70117E-001 6.29189E-001 -2.35007E-001 6.27453E-001 -2.38177E-001 -2.70641E-001 1.72084E-001 +416264965 -2.70855E-001 1.70401E-001 6.27819E-001 -2.34481E-001 6.25144E-001 -2.38700E-001 -2.69785E-001 1.73840E-001 +417147693 -2.68553E-001 1.71253E-001 6.27282E-001 -2.35369E-001 6.27960E-001 -2.39308E-001 -2.68574E-001 1.72721E-001 +418030421 -2.69404E-001 1.72389E-001 6.27697E-001 -2.35489E-001 6.27898E-001 -2.37830E-001 -2.69177E-001 1.74399E-001 +418913150 -2.69321E-001 1.71975E-001 6.28384E-001 -2.36969E-001 6.26686E-001 -2.40010E-001 -2.68729E-001 1.73291E-001 +419795878 -2.69152E-001 1.70831E-001 6.27851E-001 -2.36569E-001 6.27670E-001 -2.39904E-001 -2.66060E-001 1.74240E-001 +420678606 -2.70135E-001 1.71793E-001 6.26322E-001 -2.34966E-001 6.25817E-001 -2.40573E-001 -2.69066E-001 1.73189E-001 +421561334 -2.70649E-001 1.72101E-001 6.28368E-001 -2.37009E-001 6.26669E-001 -2.40050E-001 -2.68436E-001 1.72062E-001 +422444062 -2.70844E-001 1.72532E-001 6.26170E-001 -2.37918E-001 6.26219E-001 -2.40629E-001 -2.68370E-001 1.73070E-001 +423326790 -2.71553E-001 1.74692E-001 6.27660E-001 -2.37431E-001 6.27521E-001 -2.42064E-001 -2.68264E-001 1.74038E-001 +424209519 -2.72394E-001 1.72725E-001 6.26907E-001 -2.38999E-001 6.26857E-001 -2.41962E-001 -2.69123E-001 1.76011E-001 +425092247 -2.69672E-001 1.72645E-001 6.27142E-001 -2.39359E-001 6.26707E-001 -2.43314E-001 -2.69464E-001 1.72610E-001 +425974975 -2.70011E-001 1.73973E-001 6.26063E-001 -2.40892E-001 6.26573E-001 -2.43790E-001 -2.68872E-001 1.76501E-001 +426857703 -2.68607E-001 1.71384E-001 6.28088E-001 -2.38647E-001 6.26657E-001 -2.43784E-001 -2.68020E-001 1.74661E-001 +427740431 -2.69062E-001 1.70565E-001 6.28542E-001 -2.40155E-001 6.27397E-001 -2.43131E-001 -2.67751E-001 1.74974E-001 +428623159 -2.68066E-001 1.73300E-001 6.28320E-001 -2.39666E-001 6.25518E-001 -2.42709E-001 -2.66896E-001 1.75388E-001 +429505888 -2.69196E-001 1.72368E-001 6.25768E-001 -2.40252E-001 6.25895E-001 -2.44137E-001 -2.69233E-001 1.74773E-001 +430388616 -2.66785E-001 1.73707E-001 6.29140E-001 -2.39754E-001 6.27853E-001 -2.43105E-001 -2.65351E-001 1.75382E-001 +431271344 -2.69236E-001 1.74845E-001 6.27631E-001 -2.40888E-001 6.26812E-001 -2.44415E-001 -2.66459E-001 1.75557E-001 +432154072 -2.72771E-001 1.75533E-001 6.27056E-001 -2.41229E-001 6.26611E-001 -2.45186E-001 -2.68587E-001 1.75864E-001 +433036800 -2.68033E-001 1.76513E-001 6.26529E-001 -2.42196E-001 6.25857E-001 -2.43927E-001 -2.66059E-001 1.74978E-001 +433919528 -2.67123E-001 1.73938E-001 6.23630E-001 -2.43099E-001 6.24369E-001 -2.45378E-001 -2.66799E-001 1.76112E-001 +434802257 -2.69037E-001 1.73115E-001 6.26341E-001 -2.41331E-001 6.26549E-001 -2.46397E-001 -2.66641E-001 1.74986E-001 +435684985 -2.68533E-001 1.75961E-001 6.25657E-001 -2.42985E-001 6.25151E-001 -2.47060E-001 -2.65865E-001 1.76441E-001 +436567713 -2.69284E-001 1.77565E-001 6.27432E-001 -2.41526E-001 6.25554E-001 -2.46350E-001 -2.66078E-001 1.78717E-001 +437450441 -2.68273E-001 1.77253E-001 6.26104E-001 -2.42128E-001 6.24839E-001 -2.46759E-001 -2.65949E-001 1.76014E-001 +438333169 -2.67567E-001 1.75670E-001 6.26309E-001 -2.42768E-001 6.26000E-001 -2.46352E-001 -2.65016E-001 1.76369E-001 +439215897 -2.68061E-001 1.76270E-001 6.26273E-001 -2.43195E-001 6.23764E-001 -2.48192E-001 -2.65913E-001 1.78016E-001 +440098626 -2.67562E-001 1.74348E-001 6.26147E-001 -2.43147E-001 6.25967E-001 -2.47783E-001 -2.65305E-001 1.76394E-001 +440981354 -2.68362E-001 1.77272E-001 6.25604E-001 -2.42138E-001 6.24713E-001 -2.47198E-001 -2.65851E-001 1.77913E-001 +441864082 -2.67932E-001 1.75678E-001 6.28910E-001 -2.44522E-001 6.28023E-001 -2.48182E-001 -2.64904E-001 1.76497E-001 +442746810 -2.65425E-001 1.77622E-001 6.28193E-001 -2.43876E-001 6.26966E-001 -2.48398E-001 -2.66984E-001 1.78577E-001 +443629538 -2.67637E-001 1.75784E-001 6.27958E-001 -2.45574E-001 6.25644E-001 -2.48666E-001 -2.65050E-001 1.78074E-001 +444512266 -2.65803E-001 1.75918E-001 6.26704E-001 -2.44794E-001 6.24504E-001 -2.50352E-001 -2.68680E-001 1.79324E-001 +445394995 -2.67064E-001 1.78660E-001 6.24701E-001 -2.46827E-001 6.24566E-001 -2.49916E-001 -2.64006E-001 1.79039E-001 +446277723 -2.65561E-001 1.77290E-001 6.25562E-001 -2.47578E-001 6.24952E-001 -2.50482E-001 -2.63408E-001 1.79543E-001 +447160451 -2.67877E-001 1.76280E-001 6.25483E-001 -2.46458E-001 6.24758E-001 -2.51030E-001 -2.63730E-001 1.81892E-001 +448043179 -2.66493E-001 1.76221E-001 6.27454E-001 -2.46843E-001 6.24441E-001 -2.51657E-001 -2.62851E-001 1.78881E-001 +448925907 -2.64422E-001 1.77790E-001 6.25747E-001 -2.47097E-001 6.27477E-001 -2.49643E-001 -2.65189E-001 1.77570E-001 +449808635 -2.65667E-001 1.77117E-001 6.24635E-001 -2.48374E-001 6.23079E-001 -2.50895E-001 -2.62440E-001 1.80243E-001 +450691364 -2.64765E-001 1.78979E-001 6.26961E-001 -2.50649E-001 6.28086E-001 -2.51962E-001 -2.63691E-001 1.80797E-001 +451574092 -2.64549E-001 1.80559E-001 6.17508E-001 -2.44073E-001 6.17694E-001 -2.47678E-001 -2.60817E-001 1.80445E-001 +452456820 -2.66171E-001 1.76693E-001 6.25153E-001 -2.49983E-001 6.23715E-001 -2.50837E-001 -2.63574E-001 1.80404E-001 +453339548 -2.66303E-001 1.78293E-001 6.23701E-001 -2.49649E-001 6.24024E-001 -2.52928E-001 -2.63459E-001 1.81529E-001 +454222276 -2.64941E-001 1.77420E-001 6.23744E-001 -2.48068E-001 6.23676E-001 -2.52330E-001 -2.63196E-001 1.81579E-001 +455105004 -2.68145E-001 1.79591E-001 6.22566E-001 -2.49616E-001 6.23628E-001 -2.53766E-001 -2.64833E-001 1.81000E-001 +455987733 -2.63523E-001 1.81509E-001 6.21732E-001 -2.50210E-001 6.21948E-001 -2.53728E-001 -2.60558E-001 1.80746E-001 +456870461 -2.67260E-001 1.78788E-001 6.23576E-001 -2.51051E-001 6.23672E-001 -2.53526E-001 -2.61719E-001 1.81166E-001 +457753189 -2.64372E-001 1.77682E-001 6.22348E-001 -2.50163E-001 6.23044E-001 -2.55200E-001 -2.63707E-001 1.78638E-001 +458635917 -2.63201E-001 1.77379E-001 6.23971E-001 -2.50890E-001 6.22104E-001 -2.54144E-001 -2.59541E-001 1.80003E-001 +459518645 -2.66423E-001 1.78429E-001 6.22315E-001 -2.53311E-001 6.21526E-001 -2.56573E-001 -2.61487E-001 1.82303E-001 +460401373 -2.62966E-001 1.78203E-001 6.22191E-001 -2.52319E-001 6.21522E-001 -2.56633E-001 -2.64775E-001 1.81828E-001 +461284102 -2.63802E-001 1.81346E-001 6.25806E-001 -2.52427E-001 6.22567E-001 -2.54988E-001 -2.62813E-001 1.82489E-001 +462166830 -2.63626E-001 1.79304E-001 6.24989E-001 -2.53087E-001 6.22487E-001 -2.56519E-001 -2.61079E-001 1.82348E-001 +463049558 -2.61511E-001 1.79922E-001 6.22481E-001 -2.52458E-001 6.23107E-001 -2.56301E-001 -2.60799E-001 1.79122E-001 +463932286 -2.63763E-001 1.80146E-001 6.21785E-001 -2.53061E-001 6.22710E-001 -2.56165E-001 -2.62159E-001 1.83065E-001 +464815014 -2.62376E-001 1.81238E-001 6.22276E-001 -2.53083E-001 6.22479E-001 -2.56610E-001 -2.60364E-001 1.85818E-001 +465697742 -2.62114E-001 1.82890E-001 6.22954E-001 -2.52658E-001 6.23175E-001 -2.57488E-001 -2.62314E-001 1.83853E-001 +466580471 -2.60922E-001 1.82291E-001 6.23140E-001 -2.53408E-001 6.22517E-001 -2.57606E-001 -2.61132E-001 1.85476E-001 +467463199 -2.62844E-001 1.83325E-001 6.22645E-001 -2.54718E-001 6.22626E-001 -2.57441E-001 -2.61366E-001 1.81801E-001 +468345927 -2.60793E-001 1.79830E-001 6.23610E-001 -2.53974E-001 6.22003E-001 -2.59210E-001 -2.57537E-001 1.84284E-001 +469228655 -2.65093E-001 1.80964E-001 6.22733E-001 -2.55545E-001 6.23385E-001 -2.57973E-001 -2.59522E-001 1.84636E-001 +470111383 -2.61829E-001 1.80428E-001 6.21435E-001 -2.53977E-001 6.22736E-001 -2.58824E-001 -2.59511E-001 1.82468E-001 +470994111 -2.63098E-001 1.81001E-001 6.23686E-001 -2.54787E-001 6.21441E-001 -2.58895E-001 -2.62001E-001 1.81913E-001 +471876840 -2.64387E-001 1.80966E-001 6.23530E-001 -2.56982E-001 6.22193E-001 -2.58877E-001 -2.58472E-001 1.85432E-001 +472759568 -2.60215E-001 1.81506E-001 6.23692E-001 -2.57163E-001 6.23253E-001 -2.60878E-001 -2.61355E-001 1.81888E-001 +473642296 -2.61114E-001 1.83372E-001 6.29433E-001 -2.62238E-001 6.28501E-001 -2.65791E-001 -2.58603E-001 1.82659E-001 +474525024 -2.63460E-001 1.84081E-001 6.22200E-001 -2.56377E-001 6.20927E-001 -2.59445E-001 -2.60086E-001 1.84204E-001 +475407752 -2.61250E-001 1.82492E-001 6.23151E-001 -2.57988E-001 6.22443E-001 -2.61012E-001 -2.59458E-001 1.85231E-001 +476290480 -2.62938E-001 1.83231E-001 6.22501E-001 -2.58913E-001 6.22936E-001 -2.61839E-001 -2.58061E-001 1.85500E-001 +477173209 -2.63661E-001 1.81845E-001 6.21587E-001 -2.57532E-001 6.22443E-001 -2.60772E-001 -2.59865E-001 1.85112E-001 +478055937 -2.59334E-001 1.84303E-001 6.20899E-001 -2.57485E-001 6.21137E-001 -2.63496E-001 -2.58554E-001 1.85414E-001 +478938665 -2.61572E-001 1.83818E-001 6.21371E-001 -2.58024E-001 6.21651E-001 -2.61313E-001 -2.58256E-001 1.81151E-001 +479821393 -2.61943E-001 1.83953E-001 6.22556E-001 -2.57571E-001 6.22432E-001 -2.63151E-001 -2.59909E-001 1.86660E-001 +480704121 -2.62105E-001 1.84331E-001 6.21542E-001 -2.58648E-001 6.21299E-001 -2.61863E-001 -2.59691E-001 1.86071E-001 +481586849 -2.61714E-001 1.82514E-001 6.22684E-001 -2.58967E-001 6.21974E-001 -2.63293E-001 -2.56354E-001 1.83516E-001 +482469578 -2.62990E-001 1.84655E-001 6.22355E-001 -2.58273E-001 6.22223E-001 -2.62549E-001 -2.58913E-001 1.85726E-001 +483352306 -2.61697E-001 1.86778E-001 6.22197E-001 -2.59878E-001 6.21480E-001 -2.62899E-001 -2.61024E-001 1.86789E-001 +484235034 -2.69063E-001 1.85534E-001 6.23729E-001 -2.66527E-001 6.15770E-001 -2.61833E-001 -2.57176E-001 1.92962E-001 +485117762 -2.62938E-001 1.85711E-001 6.21951E-001 -2.63393E-001 6.20696E-001 -2.66337E-001 -2.62414E-001 1.88988E-001 +486000490 -2.66188E-001 1.85628E-001 6.22071E-001 -2.58951E-001 6.21465E-001 -2.63028E-001 -2.63752E-001 1.89171E-001 +486883218 -2.66362E-001 1.87376E-001 6.21190E-001 -2.60855E-001 6.20883E-001 -2.65498E-001 -2.61643E-001 1.85292E-001 +487765947 -2.61537E-001 1.86420E-001 6.20733E-001 -2.60993E-001 6.22041E-001 -2.65745E-001 -2.58923E-001 1.88502E-001 +488648675 -2.65309E-001 1.87909E-001 6.22836E-001 -2.63830E-001 6.21929E-001 -2.69842E-001 -2.61237E-001 1.88514E-001 +489531403 -2.62956E-001 1.87752E-001 6.21341E-001 -2.63036E-001 6.20128E-001 -2.68462E-001 -2.62188E-001 1.87075E-001 +490414131 -2.63425E-001 1.87425E-001 6.21532E-001 -2.64072E-001 6.20637E-001 -2.67461E-001 -2.61066E-001 1.89424E-001 +491296859 -2.60260E-001 1.85993E-001 6.23212E-001 -2.63860E-001 6.20078E-001 -2.66005E-001 -2.60937E-001 1.90743E-001 +492179587 -2.63132E-001 1.88453E-001 6.22204E-001 -2.63368E-001 6.20218E-001 -2.66725E-001 -2.58013E-001 1.91866E-001 +493062315 -2.63598E-001 1.86983E-001 6.20524E-001 -2.63698E-001 6.19525E-001 -2.67327E-001 -2.59959E-001 1.88905E-001 +493945044 -2.63607E-001 1.87178E-001 6.21838E-001 -2.65002E-001 6.19109E-001 -2.68764E-001 -2.60634E-001 1.93416E-001 +494827772 -2.68163E-001 1.87298E-001 6.17284E-001 -2.66524E-001 6.20724E-001 -2.62357E-001 -2.55863E-001 1.92210E-001 +495710500 -2.62830E-001 1.88936E-001 6.21221E-001 -2.67400E-001 6.20682E-001 -2.69933E-001 -2.60035E-001 1.90050E-001 +496593228 -2.63333E-001 1.89275E-001 6.22761E-001 -2.65316E-001 6.21541E-001 -2.69449E-001 -2.60799E-001 1.88604E-001 +497475956 -2.65467E-001 1.87031E-001 6.22483E-001 -2.65507E-001 6.20836E-001 -2.70619E-001 -2.61965E-001 1.89111E-001 +498358684 -2.64724E-001 1.87932E-001 6.24209E-001 -2.66381E-001 6.22727E-001 -2.69828E-001 -2.63290E-001 1.89861E-001 +499241413 -2.63325E-001 1.89824E-001 6.22415E-001 -2.67105E-001 6.22455E-001 -2.69600E-001 -2.62311E-001 1.89643E-001 +500124141 -2.62665E-001 1.90045E-001 6.21881E-001 -2.66609E-001 6.19555E-001 -2.70696E-001 -2.61729E-001 1.90143E-001 +501006869 -2.64138E-001 1.88999E-001 6.23040E-001 -2.67302E-001 6.17887E-001 -2.71471E-001 -2.59872E-001 1.93346E-001 +501889597 -2.62760E-001 1.87698E-001 6.21635E-001 -2.66547E-001 6.20407E-001 -2.70674E-001 -2.63342E-001 1.90023E-001 +502772325 -2.64856E-001 1.88801E-001 6.19586E-001 -2.68665E-001 6.18182E-001 -2.73147E-001 -2.60155E-001 1.91866E-001 +503655053 -2.63362E-001 1.89978E-001 6.20571E-001 -2.68201E-001 6.18756E-001 -2.72361E-001 -2.61623E-001 1.91621E-001 +504537782 -2.64477E-001 1.92052E-001 6.20317E-001 -2.71631E-001 6.19487E-001 -2.70976E-001 -2.61849E-001 1.92378E-001 +505420510 -2.63075E-001 1.90303E-001 6.21837E-001 -2.68729E-001 6.20677E-001 -2.70120E-001 -2.59988E-001 1.93257E-001 +506303238 -2.61887E-001 1.92873E-001 6.21024E-001 -2.67505E-001 6.20995E-001 -2.71426E-001 -2.59228E-001 1.94950E-001 +507185966 -2.62742E-001 1.92179E-001 6.23728E-001 -2.70027E-001 6.22279E-001 -2.73348E-001 -2.62262E-001 1.94785E-001 +508068694 -2.63296E-001 1.90896E-001 6.21189E-001 -2.70159E-001 6.19702E-001 -2.72282E-001 -2.59973E-001 1.91756E-001 +508951422 -2.61539E-001 1.92225E-001 6.21509E-001 -2.71409E-001 6.18520E-001 -2.75647E-001 -2.59788E-001 1.94672E-001 +509834151 -2.65558E-001 1.92381E-001 6.21048E-001 -2.71057E-001 6.17899E-001 -2.75658E-001 -2.59892E-001 1.96290E-001 +510716879 -2.66609E-001 1.90474E-001 6.21035E-001 -2.72959E-001 6.18316E-001 -2.72785E-001 -2.60062E-001 1.94080E-001 +511599607 -2.64286E-001 1.97579E-001 6.22151E-001 -2.74140E-001 6.21970E-001 -2.75821E-001 -2.56462E-001 1.94274E-001 +512482335 -2.62448E-001 1.94360E-001 6.22153E-001 -2.72387E-001 6.19927E-001 -2.77415E-001 -2.58812E-001 1.94099E-001 +513365063 -2.60751E-001 1.93781E-001 6.21296E-001 -2.72653E-001 6.20738E-001 -2.75190E-001 -2.58480E-001 1.95925E-001 +514247791 -2.60246E-001 1.97183E-001 6.22787E-001 -2.70939E-001 6.23250E-001 -2.76278E-001 -2.57960E-001 1.96060E-001 +515130520 -2.61656E-001 1.97921E-001 6.20762E-001 -2.72896E-001 6.18945E-001 -2.78244E-001 -2.57460E-001 1.97848E-001 +516013248 -2.63524E-001 1.97927E-001 6.19762E-001 -2.72217E-001 6.19188E-001 -2.78553E-001 -2.59737E-001 1.98274E-001 +516895976 -2.64141E-001 1.93952E-001 6.20363E-001 -2.72056E-001 6.21528E-001 -2.78293E-001 -2.59472E-001 1.97198E-001 +517778704 -2.62346E-001 1.95610E-001 6.20657E-001 -2.72546E-001 6.20108E-001 -2.77579E-001 -2.59797E-001 1.96201E-001 +518661432 -2.59331E-001 1.94762E-001 6.20493E-001 -2.73121E-001 6.21981E-001 -2.78627E-001 -2.58946E-001 1.96288E-001 +519544160 -2.62950E-001 1.94465E-001 6.18988E-001 -2.75401E-001 6.19655E-001 -2.78929E-001 -2.57780E-001 1.97012E-001 +520426889 -2.64072E-001 1.97820E-001 6.19048E-001 -2.75779E-001 6.17760E-001 -2.79896E-001 -2.60298E-001 2.00983E-001 +521309617 -2.62896E-001 1.95805E-001 6.19889E-001 -2.75541E-001 6.17558E-001 -2.79484E-001 -2.59012E-001 1.99275E-001 +522192345 -2.62550E-001 2.01411E-001 6.19698E-001 -2.78826E-001 6.17548E-001 -2.81090E-001 -2.61391E-001 2.03894E-001 +523075073 -2.64437E-001 1.99587E-001 6.20036E-001 -2.78473E-001 6.18843E-001 -2.82354E-001 -2.57127E-001 2.01477E-001 +523957801 -2.64923E-001 1.99212E-001 6.19505E-001 -2.76245E-001 6.17882E-001 -2.81098E-001 -2.58285E-001 1.99461E-001 +524840529 -2.62259E-001 1.98115E-001 6.19957E-001 -2.79807E-001 6.18635E-001 -2.80240E-001 -2.59761E-001 2.01829E-001 +525723258 -2.59333E-001 2.01868E-001 6.17990E-001 -2.79096E-001 6.17370E-001 -2.82935E-001 -2.58596E-001 2.01982E-001 +526605986 -2.60124E-001 2.01717E-001 6.18639E-001 -2.78662E-001 6.18378E-001 -2.82962E-001 -2.58317E-001 2.02514E-001 +527488714 -2.60247E-001 1.97545E-001 6.20145E-001 -2.80683E-001 6.18584E-001 -2.84107E-001 -2.56928E-001 2.00294E-001 +528371442 -2.61618E-001 2.02058E-001 6.21926E-001 -2.79760E-001 6.20314E-001 -2.83316E-001 -2.58090E-001 2.01531E-001 +529254170 -2.61938E-001 2.01058E-001 6.17939E-001 -2.79644E-001 6.18452E-001 -2.85931E-001 -2.61947E-001 1.99832E-001 +530136898 -2.61836E-001 1.99321E-001 6.21158E-001 -2.81582E-001 6.19124E-001 -2.84796E-001 -2.58839E-001 2.02619E-001 +531019627 -2.63237E-001 2.02938E-001 6.17431E-001 -2.80499E-001 6.18387E-001 -2.85808E-001 -2.61316E-001 2.00720E-001 +531902355 -2.64268E-001 2.05870E-001 6.19191E-001 -2.79580E-001 6.19785E-001 -2.83244E-001 -2.57537E-001 2.05265E-001 +532785083 -2.61370E-001 2.02358E-001 6.19410E-001 -2.81017E-001 6.18015E-001 -2.84071E-001 -2.57691E-001 2.05220E-001 +533667811 -2.63820E-001 2.01691E-001 6.21418E-001 -2.85859E-001 6.17605E-001 -2.87955E-001 -2.56213E-001 2.03603E-001 +534550539 -2.62194E-001 2.03089E-001 6.21667E-001 -2.80934E-001 6.18622E-001 -2.86352E-001 -2.57977E-001 2.05336E-001 +535433267 -2.62503E-001 2.06666E-001 6.20967E-001 -2.84061E-001 6.17687E-001 -2.86256E-001 -2.57182E-001 2.06015E-001 +536315996 -2.62700E-001 2.04433E-001 6.17457E-001 -2.85557E-001 6.18314E-001 -2.89816E-001 -2.59058E-001 2.02891E-001 +537198724 -2.61204E-001 2.06279E-001 6.16695E-001 -2.82938E-001 6.15995E-001 -2.86898E-001 -2.53684E-001 2.03713E-001 +538081452 -2.59206E-001 2.02787E-001 6.21212E-001 -2.83485E-001 6.16858E-001 -2.87999E-001 -2.54299E-001 2.07720E-001 +538964180 -2.62252E-001 2.04368E-001 6.18969E-001 -2.85765E-001 6.17798E-001 -2.90718E-001 -2.55937E-001 2.08158E-001 +539846908 -2.57074E-001 2.04392E-001 6.17970E-001 -2.85611E-001 6.17360E-001 -2.88145E-001 -2.53552E-001 2.04664E-001 +540729636 -2.58769E-001 2.02365E-001 6.17717E-001 -2.87173E-001 6.16398E-001 -2.88783E-001 -2.53971E-001 2.07030E-001 +541612365 -2.60235E-001 2.08460E-001 6.20155E-001 -2.88977E-001 6.18427E-001 -2.91448E-001 -2.54494E-001 2.06576E-001 +542495093 -2.62839E-001 2.07957E-001 6.20151E-001 -2.89165E-001 6.16555E-001 -2.90756E-001 -2.56706E-001 2.09048E-001 +543377821 -2.58606E-001 2.09483E-001 6.17535E-001 -2.85858E-001 6.14741E-001 -2.91819E-001 -2.58721E-001 2.11583E-001 +544260549 -2.58921E-001 2.09552E-001 6.18441E-001 -2.87087E-001 6.17310E-001 -2.90721E-001 -2.55070E-001 2.08386E-001 +545143277 -2.57550E-001 2.07447E-001 6.18719E-001 -2.89867E-001 6.16583E-001 -2.93184E-001 -2.55465E-001 2.09032E-001 +546026005 -2.61138E-001 2.11114E-001 6.19242E-001 -2.87705E-001 6.16134E-001 -2.91901E-001 -2.54192E-001 2.10493E-001 +546908734 -2.59794E-001 2.11588E-001 6.16194E-001 -2.88716E-001 6.12041E-001 -2.91500E-001 -2.54520E-001 2.11579E-001 +547791462 -2.57269E-001 2.10787E-001 6.15561E-001 -2.88612E-001 6.14480E-001 -2.94494E-001 -2.54874E-001 2.09856E-001 +548674190 -2.59975E-001 2.08099E-001 6.16988E-001 -2.90745E-001 6.14790E-001 -2.92980E-001 -2.55659E-001 2.10318E-001 +549556918 -2.56596E-001 2.08607E-001 6.16598E-001 -2.89724E-001 6.15507E-001 -2.92035E-001 -2.51165E-001 2.10086E-001 +550439646 -2.55612E-001 2.11211E-001 6.11339E-001 -2.89450E-001 6.17834E-001 -2.99726E-001 -2.51433E-001 2.11366E-001 +551322374 -2.54540E-001 2.14574E-001 6.19209E-001 -2.90218E-001 6.15892E-001 -2.99565E-001 -2.53555E-001 2.14315E-001 +552205103 -2.58122E-001 2.10812E-001 6.17238E-001 -2.89999E-001 6.16906E-001 -2.95514E-001 -2.52789E-001 2.12817E-001 +553087831 -2.55489E-001 2.11916E-001 6.18819E-001 -2.90764E-001 6.17786E-001 -2.95358E-001 -2.52138E-001 2.08192E-001 +553970559 -2.56698E-001 2.14607E-001 6.19666E-001 -2.93604E-001 6.16160E-001 -2.98525E-001 -2.52854E-001 2.12306E-001 +554853287 -2.55601E-001 2.14015E-001 6.15935E-001 -2.94457E-001 6.17837E-001 -3.01233E-001 -2.53252E-001 2.15897E-001 +555736015 -2.55168E-001 2.10765E-001 6.12461E-001 -2.88535E-001 6.13573E-001 -2.92174E-001 -2.51998E-001 2.14002E-001 +556618743 -2.57411E-001 2.16513E-001 6.14807E-001 -2.92801E-001 6.11387E-001 -2.95181E-001 -2.52152E-001 2.16434E-001 +557501472 -2.57250E-001 2.15810E-001 6.18165E-001 -2.97384E-001 6.16867E-001 -3.00066E-001 -2.53699E-001 2.15951E-001 +558384200 -2.54892E-001 2.14885E-001 6.20259E-001 -2.92182E-001 6.15134E-001 -2.96923E-001 -2.48558E-001 2.14775E-001 +559266928 -2.52796E-001 2.17969E-001 6.15635E-001 -2.94771E-001 6.13075E-001 -2.96527E-001 -2.51107E-001 2.18073E-001 +560149656 -2.54535E-001 2.18230E-001 6.15344E-001 -2.99112E-001 6.14827E-001 -2.97811E-001 -2.50355E-001 2.17538E-001 +561032384 -2.54995E-001 2.16068E-001 6.17097E-001 -2.94161E-001 6.13747E-001 -3.01087E-001 -2.50749E-001 2.16928E-001 +561915112 -2.53147E-001 2.16406E-001 6.15125E-001 -2.96004E-001 6.13053E-001 -3.01443E-001 -2.52261E-001 2.15289E-001 +562797841 -2.53542E-001 2.18056E-001 6.14867E-001 -2.95912E-001 6.13782E-001 -3.01684E-001 -2.48073E-001 2.16904E-001 +563680569 -2.54183E-001 2.19416E-001 6.22296E-001 -2.95194E-001 6.14264E-001 -3.05778E-001 -2.50384E-001 2.20246E-001 +564563297 -2.49985E-001 2.17982E-001 6.14917E-001 -2.93975E-001 6.16016E-001 -2.97644E-001 -2.46604E-001 2.17231E-001 +565446025 -2.53092E-001 2.18970E-001 6.17204E-001 -2.93522E-001 6.17117E-001 -3.02043E-001 -2.46933E-001 2.19203E-001 +566328753 -2.53505E-001 2.19025E-001 6.16709E-001 -2.99097E-001 6.15016E-001 -3.04903E-001 -2.47142E-001 2.21274E-001 +567211481 -2.52275E-001 2.19500E-001 6.14765E-001 -2.97873E-001 6.15581E-001 -3.04446E-001 -2.46908E-001 2.18228E-001 +568094210 -2.50715E-001 2.21241E-001 6.13371E-001 -3.04260E-001 6.14744E-001 -3.06152E-001 -2.49004E-001 2.21354E-001 +568976938 -2.52584E-001 2.20412E-001 6.14995E-001 -3.05038E-001 6.17934E-001 -3.08474E-001 -2.48621E-001 2.19457E-001 +569859666 -2.49483E-001 2.22670E-001 6.13472E-001 -3.02128E-001 6.13523E-001 -3.06682E-001 -2.47470E-001 2.22565E-001 +570742394 -2.48275E-001 2.22685E-001 6.14868E-001 -3.04115E-001 6.11849E-001 -3.06693E-001 -2.44107E-001 2.20703E-001 +571625122 -2.48654E-001 2.19965E-001 6.15917E-001 -3.01548E-001 6.14645E-001 -3.05300E-001 -2.44557E-001 2.22197E-001 +572507850 -2.48671E-001 2.19546E-001 6.14765E-001 -3.04460E-001 6.14868E-001 -3.05418E-001 -2.45992E-001 2.21081E-001 +573390578 -2.49780E-001 2.24096E-001 6.14367E-001 -3.05179E-001 6.13414E-001 -3.09401E-001 -2.45496E-001 2.20233E-001 +574273307 -2.47432E-001 2.21985E-001 6.16500E-001 -3.01453E-001 6.13775E-001 -3.08120E-001 -2.46111E-001 2.23555E-001 +575156035 -2.49029E-001 2.22432E-001 6.13755E-001 -3.01234E-001 6.17405E-001 -3.11390E-001 -2.46330E-001 2.20314E-001 +576038763 -2.49947E-001 2.22907E-001 6.13380E-001 -3.04496E-001 6.11479E-001 -3.09443E-001 -2.42233E-001 2.22397E-001 +576921491 -2.48341E-001 2.23165E-001 6.16033E-001 -3.01890E-001 6.10388E-001 -3.10871E-001 -2.42287E-001 2.28451E-001 +577804219 -2.45153E-001 2.22298E-001 6.15090E-001 -3.01357E-001 6.11176E-001 -3.11505E-001 -2.41757E-001 2.23862E-001 +578686947 -2.46340E-001 2.19153E-001 6.11954E-001 -3.03217E-001 6.14107E-001 -3.12788E-001 -2.43424E-001 2.24635E-001 +579569676 -2.46269E-001 2.26981E-001 6.17618E-001 -3.06371E-001 6.16238E-001 -3.13764E-001 -2.41762E-001 2.22589E-001 +580452404 -2.46934E-001 2.24598E-001 6.15565E-001 -3.06575E-001 6.10501E-001 -3.08580E-001 -2.45611E-001 2.21569E-001 +581335132 -2.46507E-001 2.25778E-001 6.14449E-001 -3.08471E-001 6.11126E-001 -3.15005E-001 -2.41755E-001 2.25914E-001 +582217860 -2.45987E-001 2.24240E-001 6.13889E-001 -3.07707E-001 6.12669E-001 -3.10128E-001 -2.40818E-001 2.25955E-001 +583100588 -2.43460E-001 2.25516E-001 6.10919E-001 -3.08301E-001 6.12126E-001 -3.13931E-001 -2.40164E-001 2.24685E-001 +583983316 -2.48103E-001 2.27075E-001 6.11740E-001 -3.09377E-001 6.13497E-001 -3.16221E-001 -2.41852E-001 2.25446E-001 +584866045 -2.40280E-001 2.30270E-001 6.12636E-001 -3.11148E-001 6.14134E-001 -3.18496E-001 -2.34514E-001 2.27112E-001 +585748773 -2.41437E-001 2.24612E-001 6.13008E-001 -3.11925E-001 6.12310E-001 -3.17836E-001 -2.37916E-001 2.27495E-001 +586631501 -2.42827E-001 2.25614E-001 6.13319E-001 -3.11650E-001 6.10380E-001 -3.19617E-001 -2.37537E-001 2.25306E-001 +587514229 -2.40522E-001 2.29634E-001 6.09796E-001 -3.13198E-001 6.10319E-001 -3.13314E-001 -2.38837E-001 2.26314E-001 +588396957 -2.44496E-001 2.27137E-001 6.12051E-001 -3.11623E-001 6.12174E-001 -3.19334E-001 -2.38883E-001 2.27238E-001 +589279685 -2.41081E-001 2.27322E-001 6.15544E-001 -3.16371E-001 6.12711E-001 -3.18440E-001 -2.39853E-001 2.27741E-001 +590162414 -2.37151E-001 2.25745E-001 6.10781E-001 -3.12940E-001 6.10001E-001 -3.15588E-001 -2.38596E-001 2.29345E-001 +591045142 -2.44604E-001 2.28290E-001 6.09675E-001 -3.11428E-001 6.06560E-001 -3.20789E-001 -2.35702E-001 2.27563E-001 +591927870 -2.37677E-001 2.27780E-001 6.12908E-001 -3.13531E-001 6.10891E-001 -3.16314E-001 -2.38186E-001 2.25887E-001 +592810598 -2.41490E-001 2.24057E-001 6.09834E-001 -3.15939E-001 6.12181E-001 -3.22694E-001 -2.39335E-001 2.26346E-001 +593693326 -2.37656E-001 2.27883E-001 6.11308E-001 -3.15323E-001 6.10482E-001 -3.23646E-001 -2.39655E-001 2.29196E-001 +594576054 -2.40507E-001 2.30470E-001 6.13237E-001 -3.14856E-001 6.08827E-001 -3.23301E-001 -2.33345E-001 2.27558E-001 +595458783 -2.35795E-001 2.30578E-001 6.10523E-001 -3.11098E-001 6.11368E-001 -3.23019E-001 -2.38520E-001 2.29396E-001 +596341511 -2.37394E-001 2.27073E-001 6.11890E-001 -3.16640E-001 6.07625E-001 -3.22538E-001 -2.35173E-001 2.31202E-001 +597224239 -2.41356E-001 2.25458E-001 6.08698E-001 -3.17342E-001 6.11501E-001 -3.25436E-001 -2.38448E-001 2.30867E-001 +598106967 -2.36733E-001 2.30294E-001 6.10278E-001 -3.20333E-001 6.12046E-001 -3.24814E-001 -2.36384E-001 2.30447E-001 +598989695 -2.37670E-001 2.29004E-001 6.13821E-001 -3.19796E-001 6.10785E-001 -3.22212E-001 -2.35446E-001 2.28613E-001 +599872423 -2.33119E-001 2.29362E-001 6.10179E-001 -3.20596E-001 6.08611E-001 -3.25777E-001 -2.34403E-001 2.30071E-001 +600755152 -2.36278E-001 2.27760E-001 6.08393E-001 -3.17637E-001 6.05309E-001 -3.25672E-001 -2.33368E-001 2.27987E-001 +601637880 -2.39652E-001 2.32724E-001 6.10979E-001 -3.22300E-001 6.09817E-001 -3.23382E-001 -2.36672E-001 2.28774E-001 +602520608 -2.35038E-001 2.29240E-001 6.13845E-001 -3.25758E-001 6.10158E-001 -3.24883E-001 -2.32735E-001 2.27823E-001 +603403336 -2.37005E-001 2.31249E-001 6.14573E-001 -3.25700E-001 6.09983E-001 -3.26514E-001 -2.34815E-001 2.33758E-001 +604286064 -2.35033E-001 2.30694E-001 6.10823E-001 -3.22951E-001 6.04929E-001 -3.30612E-001 -2.34038E-001 2.30214E-001 +605168792 -2.33393E-001 2.31453E-001 6.08304E-001 -3.23274E-001 6.08335E-001 -3.26533E-001 -2.31663E-001 2.32195E-001 +606051521 -2.30161E-001 2.32019E-001 6.09935E-001 -3.22507E-001 6.06064E-001 -3.27538E-001 -2.33780E-001 2.30911E-001 +606934249 -2.33761E-001 2.33537E-001 6.09352E-001 -3.21111E-001 6.07779E-001 -3.26287E-001 -2.30586E-001 2.32651E-001 +607816977 -2.32155E-001 2.30537E-001 6.08972E-001 -3.22043E-001 6.06878E-001 -3.29263E-001 -2.30690E-001 2.33545E-001 +608699705 -2.31304E-001 2.31721E-001 6.08834E-001 -3.27265E-001 6.04921E-001 -3.29021E-001 -2.31423E-001 2.31282E-001 +609582433 -2.31362E-001 2.29260E-001 6.08073E-001 -3.22060E-001 6.05194E-001 -3.29619E-001 -2.32801E-001 2.33490E-001 +610465161 -2.30000E-001 2.34457E-001 6.04902E-001 -3.26354E-001 6.04187E-001 -3.26584E-001 -2.29138E-001 2.31010E-001 +611347890 -2.30658E-001 2.29040E-001 6.06725E-001 -3.27486E-001 6.01648E-001 -3.30289E-001 -2.31148E-001 2.31559E-001 +612230618 -2.29720E-001 2.32330E-001 6.07942E-001 -3.26077E-001 6.03452E-001 -3.32157E-001 -2.30378E-001 2.30368E-001 +613113346 -2.28431E-001 2.33376E-001 6.10532E-001 -3.27321E-001 6.08255E-001 -3.33716E-001 -2.25862E-001 2.34013E-001 +613996074 -2.31987E-001 2.28241E-001 6.09134E-001 -3.29788E-001 6.05795E-001 -3.32636E-001 -2.30243E-001 2.36099E-001 +614878802 -2.28951E-001 2.33420E-001 6.09049E-001 -3.27996E-001 6.06375E-001 -3.31826E-001 -2.25840E-001 2.38017E-001 +615761530 -2.31862E-001 2.34032E-001 6.10456E-001 -3.29964E-001 6.06253E-001 -3.35467E-001 -2.26040E-001 2.27963E-001 +616644259 -2.29395E-001 2.31888E-001 6.09393E-001 -3.31472E-001 6.06180E-001 -3.30810E-001 -2.28838E-001 2.28920E-001 +617526987 -2.23877E-001 2.33004E-001 6.06883E-001 -3.31542E-001 6.06799E-001 -3.37119E-001 -2.27060E-001 2.33285E-001 +618409715 -2.31795E-001 2.32908E-001 6.10120E-001 -3.31596E-001 6.09459E-001 -3.36085E-001 -2.28440E-001 2.33447E-001 +619292443 -2.27324E-001 2.34310E-001 6.06085E-001 -3.33716E-001 6.10350E-001 -3.36850E-001 -2.24292E-001 2.30902E-001 +620175171 -2.29778E-001 2.33090E-001 6.07552E-001 -3.27941E-001 6.05643E-001 -3.36862E-001 -2.23661E-001 2.35086E-001 +621057899 -2.29882E-001 2.32942E-001 6.05392E-001 -3.30622E-001 6.03209E-001 -3.36734E-001 -2.27802E-001 2.33559E-001 +621940628 -2.28436E-001 2.33501E-001 6.07610E-001 -3.38766E-001 6.01891E-001 -3.39326E-001 -2.23723E-001 2.35630E-001 +622823356 -2.26609E-001 2.31269E-001 6.06041E-001 -3.35458E-001 6.05615E-001 -3.39452E-001 -2.24763E-001 2.34324E-001 +623706084 -2.29235E-001 2.32041E-001 6.05357E-001 -3.35826E-001 6.04672E-001 -3.39203E-001 -2.26698E-001 2.32956E-001 +624588812 -2.26422E-001 2.28072E-001 6.03476E-001 -3.33833E-001 6.02730E-001 -3.37317E-001 -2.24247E-001 2.30478E-001 +625471540 -2.27103E-001 2.29271E-001 6.09137E-001 -3.36808E-001 6.00500E-001 -3.40443E-001 -2.26212E-001 2.29824E-001 +626354268 -2.25483E-001 2.31418E-001 6.06621E-001 -3.38862E-001 6.01296E-001 -3.39792E-001 -2.22919E-001 2.32104E-001 +627236997 -2.25476E-001 2.32759E-001 6.04398E-001 -3.32557E-001 6.04152E-001 -3.39449E-001 -2.22155E-001 2.35939E-001 +628119725 -2.22753E-001 2.34075E-001 6.04230E-001 -3.32123E-001 6.02980E-001 -3.40799E-001 -2.25772E-001 2.37667E-001 +629002453 -2.26892E-001 2.34195E-001 6.06729E-001 -3.33996E-001 6.03692E-001 -3.42650E-001 -2.25159E-001 2.35212E-001 +629885181 -2.24447E-001 2.32928E-001 6.06650E-001 -3.35818E-001 6.02377E-001 -3.42365E-001 -2.22599E-001 2.35956E-001 +630767909 -2.24962E-001 2.31718E-001 6.10315E-001 -3.35203E-001 6.06301E-001 -3.42409E-001 -2.20230E-001 2.32863E-001 +631650637 -2.22500E-001 2.31355E-001 6.04790E-001 -3.37191E-001 6.02192E-001 -3.40748E-001 -2.21619E-001 2.35889E-001 +632533366 -2.25671E-001 2.28830E-001 6.07248E-001 -3.38401E-001 6.05368E-001 -3.41753E-001 -2.21006E-001 2.32052E-001 +633416094 -2.27065E-001 2.35594E-001 6.07657E-001 -3.40421E-001 6.02840E-001 -3.41486E-001 -2.22684E-001 2.34441E-001 +634298822 -2.24447E-001 2.35824E-001 6.05644E-001 -3.38571E-001 6.05466E-001 -3.46361E-001 -2.20908E-001 2.36084E-001 +635181550 -2.21945E-001 2.37448E-001 6.06144E-001 -3.42733E-001 6.02280E-001 -3.45272E-001 -2.28790E-001 2.37042E-001 +636064278 -2.26276E-001 2.34118E-001 6.04300E-001 -3.43046E-001 6.04241E-001 -3.46332E-001 -2.24116E-001 2.33253E-001 +636947006 -2.21490E-001 2.32963E-001 6.04759E-001 -3.41565E-001 6.03162E-001 -3.51746E-001 -2.21335E-001 2.35262E-001 +637829735 -2.23051E-001 2.31738E-001 6.06526E-001 -3.40773E-001 5.98705E-001 -3.44985E-001 -2.24924E-001 2.32376E-001 +638712463 -2.20850E-001 2.35335E-001 6.04479E-001 -3.42257E-001 6.02667E-001 -3.44381E-001 -2.22702E-001 2.36543E-001 +639595191 -2.22959E-001 2.33380E-001 6.06603E-001 -3.40617E-001 6.02551E-001 -3.46684E-001 -2.20977E-001 2.36034E-001 +640477919 -2.21383E-001 2.33348E-001 6.07669E-001 -3.43293E-001 6.05941E-001 -3.50571E-001 -2.17244E-001 2.35138E-001 +641360647 -2.20025E-001 2.36959E-001 6.07229E-001 -3.42080E-001 6.05320E-001 -3.51792E-001 -2.21921E-001 2.37840E-001 +642243375 -2.18651E-001 2.34675E-001 6.04827E-001 -3.44958E-001 6.04921E-001 -3.46914E-001 -2.17243E-001 2.33267E-001 +643126104 -2.23733E-001 2.30517E-001 6.06685E-001 -3.44803E-001 6.03667E-001 -3.50060E-001 -2.20929E-001 2.37241E-001 +644008832 -2.23856E-001 2.31721E-001 6.00537E-001 -3.36870E-001 6.01302E-001 -3.51329E-001 -2.18370E-001 2.37479E-001 +644891560 -2.20848E-001 2.34269E-001 6.05601E-001 -3.41660E-001 6.04562E-001 -3.50898E-001 -2.16746E-001 2.36782E-001 +645774288 -2.28384E-001 2.34612E-001 6.04547E-001 -3.47670E-001 6.01504E-001 -3.52909E-001 -2.19040E-001 2.40730E-001 +646657016 -2.24405E-001 2.37546E-001 6.01389E-001 -3.48272E-001 6.02132E-001 -3.50133E-001 -2.20203E-001 2.34008E-001 +647539744 -2.17916E-001 2.36552E-001 6.07366E-001 -3.44912E-001 6.00114E-001 -3.48039E-001 -2.17318E-001 2.39247E-001 +648422473 -2.17577E-001 2.38425E-001 6.08215E-001 -3.46245E-001 6.02101E-001 -3.50521E-001 -2.19743E-001 2.37954E-001 +649305201 -2.20164E-001 2.38433E-001 6.03868E-001 -3.46368E-001 6.04263E-001 -3.52006E-001 -2.21111E-001 2.38612E-001 +650187929 -2.15995E-001 2.36165E-001 6.04711E-001 -3.44738E-001 6.00307E-001 -3.54426E-001 -2.16022E-001 2.41384E-001 +651070657 -2.20032E-001 2.36646E-001 6.02693E-001 -3.45456E-001 6.00437E-001 -3.51446E-001 -2.19933E-001 2.38782E-001 +651953385 -2.20321E-001 2.33058E-001 6.06821E-001 -3.49224E-001 6.00858E-001 -3.52138E-001 -2.23532E-001 2.42365E-001 +652836113 -2.16362E-001 2.43171E-001 6.05339E-001 -3.48457E-001 6.02708E-001 -3.52988E-001 -2.17482E-001 2.41904E-001 +653718842 -2.14962E-001 2.36305E-001 6.07598E-001 -3.50309E-001 6.03970E-001 -3.56529E-001 -2.13275E-001 2.36506E-001 +654601570 -2.17492E-001 2.38744E-001 6.03156E-001 -3.48045E-001 6.01890E-001 -3.53361E-001 -2.15357E-001 2.37700E-001 +655484298 -2.16452E-001 2.34175E-001 6.05955E-001 -3.49639E-001 5.99167E-001 -3.59097E-001 -2.16599E-001 2.38998E-001 +656367026 -2.22344E-001 2.38688E-001 6.03491E-001 -3.47606E-001 5.99706E-001 -3.57195E-001 -2.12042E-001 2.42627E-001 +657249754 -2.16778E-001 2.35719E-001 6.04912E-001 -3.46185E-001 6.00245E-001 -3.53178E-001 -2.12895E-001 2.37939E-001 +658132482 -2.18663E-001 2.33093E-001 6.03063E-001 -3.48881E-001 6.03786E-001 -3.53929E-001 -2.16881E-001 2.40389E-001 +659015210 -2.09485E-001 2.38551E-001 6.03913E-001 -3.54218E-001 6.01318E-001 -3.56536E-001 -2.11660E-001 2.37088E-001 +659897939 -2.14695E-001 2.36329E-001 6.02591E-001 -3.53352E-001 6.04167E-001 -3.57973E-001 -2.15871E-001 2.35734E-001 +660780667 -2.16056E-001 2.38964E-001 6.05549E-001 -3.55088E-001 6.04094E-001 -3.60676E-001 -2.14474E-001 2.43427E-001 +661663395 -2.14903E-001 2.38583E-001 6.06287E-001 -3.51915E-001 6.00796E-001 -3.61210E-001 -2.11572E-001 2.37446E-001 +662546123 -2.14031E-001 2.36494E-001 6.03820E-001 -3.53294E-001 6.02274E-001 -3.57999E-001 -2.05792E-001 2.37246E-001 +663428851 -2.11879E-001 2.37409E-001 6.02956E-001 -3.51323E-001 5.99470E-001 -3.53108E-001 -2.08115E-001 2.42265E-001 +664311579 -2.15137E-001 2.35618E-001 6.04351E-001 -3.54174E-001 5.98302E-001 -3.60218E-001 -2.12006E-001 2.40892E-001 +665194308 -2.14838E-001 2.43077E-001 6.02400E-001 -3.54240E-001 6.04015E-001 -3.62937E-001 -2.14898E-001 2.45209E-001 +666077036 -2.12845E-001 2.36421E-001 6.05157E-001 -3.57803E-001 6.03438E-001 -3.62762E-001 -2.11551E-001 2.39096E-001 +666959764 -2.12947E-001 2.39808E-001 6.05702E-001 -3.59654E-001 6.03123E-001 -3.60866E-001 -2.12863E-001 2.39902E-001 +667842492 -2.11694E-001 2.38660E-001 6.02148E-001 -3.50593E-001 6.00654E-001 -3.54184E-001 -2.12091E-001 2.40613E-001 +668725220 -2.08229E-001 2.38293E-001 6.00820E-001 -3.56793E-001 5.97010E-001 -3.61099E-001 -2.09049E-001 2.46664E-001 +669607948 -2.11374E-001 2.38245E-001 6.05353E-001 -3.56842E-001 6.01934E-001 -3.59486E-001 -2.13034E-001 2.41496E-001 +670490677 -2.12124E-001 2.32070E-001 6.00979E-001 -3.56677E-001 5.97453E-001 -3.60513E-001 -2.06665E-001 2.40154E-001 +671373405 -2.13151E-001 2.41584E-001 6.02923E-001 -3.59626E-001 6.01548E-001 -3.63978E-001 -2.09813E-001 2.42634E-001 +672256133 -2.12893E-001 2.42426E-001 6.02584E-001 -3.52144E-001 6.01594E-001 -3.63135E-001 -2.15809E-001 2.43794E-001 +673138861 -2.11063E-001 2.41951E-001 6.03062E-001 -3.56479E-001 5.98680E-001 -3.61750E-001 -2.12646E-001 2.39971E-001 +674021589 -2.14229E-001 2.37858E-001 6.04777E-001 -3.57799E-001 6.00530E-001 -3.57692E-001 -2.06118E-001 2.40455E-001 +674904317 -2.14694E-001 2.31141E-001 6.02221E-001 -3.56098E-001 5.99339E-001 -3.65016E-001 -2.16546E-001 2.36958E-001 +675787046 -2.11191E-001 2.42231E-001 6.05040E-001 -3.57134E-001 6.00266E-001 -3.62031E-001 -2.10689E-001 2.46107E-001 +676669774 -2.10517E-001 2.45218E-001 6.04271E-001 -3.57636E-001 6.03124E-001 -3.61636E-001 -2.06369E-001 2.40118E-001 +677552502 -2.09787E-001 2.35695E-001 6.04524E-001 -3.59004E-001 6.02076E-001 -3.65150E-001 -2.08128E-001 2.39437E-001 +678435230 -2.11429E-001 2.40544E-001 6.02078E-001 -3.64986E-001 6.02006E-001 -3.65105E-001 -2.06905E-001 2.41761E-001 +679317958 -2.17083E-001 2.35465E-001 6.05715E-001 -3.59864E-001 6.06774E-001 -3.65373E-001 -2.12584E-001 2.40900E-001 +680200686 -2.11944E-001 2.41220E-001 6.02655E-001 -3.59957E-001 6.04582E-001 -3.68107E-001 -2.06534E-001 2.41000E-001 +681083415 -2.12946E-001 2.38649E-001 6.09405E-001 -3.64490E-001 6.03347E-001 -3.69330E-001 -2.08350E-001 2.37198E-001 +681966143 -2.12581E-001 2.42020E-001 6.03747E-001 -3.62518E-001 6.00175E-001 -3.66365E-001 -2.06207E-001 2.47224E-001 +682848871 -2.11793E-001 2.44458E-001 6.03453E-001 -3.60566E-001 6.03647E-001 -3.68489E-001 -2.04459E-001 2.42712E-001 +683731599 -2.13621E-001 2.45073E-001 6.00927E-001 -3.64204E-001 6.03958E-001 -3.69473E-001 -2.07119E-001 2.46832E-001 +684614327 -2.09411E-001 2.44929E-001 6.04916E-001 -3.60689E-001 6.03820E-001 -3.67673E-001 -2.09513E-001 2.44123E-001 +685497055 -2.08953E-001 2.38700E-001 6.06825E-001 -3.55391E-001 6.03359E-001 -3.71519E-001 -2.12728E-001 2.43880E-001 +686379784 -2.03787E-001 2.42355E-001 6.10569E-001 -3.64656E-001 6.03435E-001 -3.65100E-001 -2.06377E-001 2.43459E-001 +687262512 -2.07782E-001 2.47215E-001 6.05817E-001 -3.66027E-001 6.01014E-001 -3.65721E-001 -2.01609E-001 2.49502E-001 +688145240 -2.08779E-001 2.41728E-001 6.05999E-001 -3.60678E-001 6.05679E-001 -3.65364E-001 -2.07562E-001 2.40289E-001 +689027968 -2.11840E-001 2.39055E-001 6.10290E-001 -3.66403E-001 6.04265E-001 -3.70129E-001 -2.05265E-001 2.41848E-001 +689910696 -2.09013E-001 2.42001E-001 6.13091E-001 -3.66421E-001 6.05749E-001 -3.73326E-001 -2.09158E-001 2.44378E-001 +690793424 -2.11029E-001 2.45463E-001 6.07097E-001 -3.61112E-001 6.06557E-001 -3.69273E-001 -2.07985E-001 2.44782E-001 +691676153 -2.04188E-001 2.43320E-001 6.10090E-001 -3.69424E-001 6.02988E-001 -3.68712E-001 -2.04392E-001 2.44236E-001 +692558881 -2.11075E-001 2.40345E-001 6.10059E-001 -3.69764E-001 6.06611E-001 -3.71287E-001 -2.03241E-001 2.42374E-001 +693441609 -2.07590E-001 2.44724E-001 6.05639E-001 -3.61215E-001 6.05543E-001 -3.73754E-001 -2.07728E-001 2.40014E-001 +694324337 -2.06679E-001 2.40099E-001 6.08443E-001 -3.64222E-001 6.02762E-001 -3.73549E-001 -2.05854E-001 2.47901E-001 +695207065 -2.05614E-001 2.45294E-001 6.06204E-001 -3.65202E-001 6.07024E-001 -3.67987E-001 -2.03642E-001 2.41332E-001 +696089793 -2.03487E-001 2.37506E-001 6.05904E-001 -3.65197E-001 6.03109E-001 -3.67746E-001 -2.09420E-001 2.38654E-001 +696972522 -2.12134E-001 2.44702E-001 6.04920E-001 -3.66064E-001 6.04008E-001 -3.73708E-001 -1.99837E-001 2.42820E-001 +697855250 -2.10073E-001 2.41646E-001 6.06951E-001 -3.61643E-001 6.05029E-001 -3.73076E-001 -2.02859E-001 2.43424E-001 +698737978 -2.01686E-001 2.39705E-001 6.11443E-001 -3.66219E-001 6.06769E-001 -3.69797E-001 -2.01107E-001 2.42897E-001 +699620706 -2.08730E-001 2.44435E-001 6.08583E-001 -3.65010E-001 6.05040E-001 -3.71878E-001 -2.06316E-001 2.42241E-001 +700503434 -2.05718E-001 2.44054E-001 6.09938E-001 -3.64758E-001 6.09292E-001 -3.68960E-001 -2.01182E-001 2.41038E-001 +701386162 -2.07472E-001 2.44287E-001 6.09789E-001 -3.63410E-001 6.09553E-001 -3.72133E-001 -1.99336E-001 2.41575E-001 +702268891 -2.08060E-001 2.41301E-001 6.09393E-001 -3.66139E-001 6.02570E-001 -3.71168E-001 -2.07413E-001 2.44387E-001 +703151619 -2.07788E-001 2.47339E-001 6.06140E-001 -3.68844E-001 6.05920E-001 -3.73316E-001 -2.03062E-001 2.45564E-001 +704034347 -2.08437E-001 2.41955E-001 6.09607E-001 -3.66125E-001 6.07280E-001 -3.74091E-001 -2.05058E-001 2.48896E-001 +704917075 -2.06460E-001 2.45604E-001 6.09756E-001 -3.67410E-001 6.04248E-001 -3.76399E-001 -2.05784E-001 2.46298E-001 +705799803 -2.07480E-001 2.41216E-001 6.08601E-001 -3.68534E-001 6.07371E-001 -3.75701E-001 -2.03559E-001 2.47346E-001 +706682531 -2.04707E-001 2.47161E-001 6.07190E-001 -3.71417E-001 6.06524E-001 -3.77625E-001 -2.03799E-001 2.43099E-001 +707565260 -2.07247E-001 2.44823E-001 6.06285E-001 -3.69059E-001 6.05924E-001 -3.81917E-001 -1.99666E-001 2.40780E-001 +708447988 -2.04987E-001 2.50565E-001 6.04908E-001 -3.71106E-001 6.05438E-001 -3.76385E-001 -2.00156E-001 2.49590E-001 +709330716 -2.01038E-001 2.44047E-001 6.07925E-001 -3.72858E-001 6.10004E-001 -3.74631E-001 -2.00811E-001 2.46909E-001 +710213444 -2.02531E-001 2.43208E-001 6.09668E-001 -3.68803E-001 6.05203E-001 -3.78122E-001 -2.02231E-001 2.43739E-001 +711096172 -2.07258E-001 2.46342E-001 6.07046E-001 -3.68414E-001 6.06281E-001 -3.76853E-001 -1.99380E-001 2.46005E-001 +711978900 -2.00129E-001 2.45007E-001 6.09424E-001 -3.72769E-001 6.05714E-001 -3.78767E-001 -2.01501E-001 2.42679E-001 +712861629 -2.02815E-001 2.48129E-001 6.11004E-001 -3.75833E-001 6.08717E-001 -3.82592E-001 -2.03609E-001 2.49722E-001 +713744357 -2.08742E-001 2.43828E-001 6.06960E-001 -3.77678E-001 6.07129E-001 -3.79448E-001 -1.98637E-001 2.46278E-001 +714627085 -2.09347E-001 2.43125E-001 6.06339E-001 -3.62714E-001 6.05533E-001 -3.74351E-001 -2.01322E-001 2.52445E-001 +715509813 -1.97680E-001 2.51162E-001 6.11500E-001 -3.70160E-001 6.06485E-001 -3.79341E-001 -2.00157E-001 2.52141E-001 +716392541 -2.07746E-001 2.44417E-001 6.14097E-001 -3.77730E-001 6.06944E-001 -3.78970E-001 -2.03353E-001 2.46381E-001 +717275269 -2.04662E-001 2.42912E-001 6.10858E-001 -3.73936E-001 6.08308E-001 -3.76017E-001 -1.98308E-001 2.48845E-001 +718157998 -2.03981E-001 2.45211E-001 6.09603E-001 -3.69201E-001 6.04792E-001 -3.74993E-001 -1.96154E-001 2.52689E-001 +719040726 -2.05116E-001 2.44944E-001 6.07846E-001 -3.71095E-001 6.09045E-001 -3.80465E-001 -1.99156E-001 2.50465E-001 +719923454 -2.05770E-001 2.47894E-001 6.10364E-001 -3.78178E-001 6.09442E-001 -3.77607E-001 -2.01174E-001 2.48242E-001 +720806182 -1.98203E-001 2.45823E-001 6.10822E-001 -3.72028E-001 6.09754E-001 -3.83030E-001 -1.99050E-001 2.53615E-001 +721688910 -1.99411E-001 2.47479E-001 6.10651E-001 -3.73219E-001 6.11100E-001 -3.84857E-001 -2.03187E-001 2.47523E-001 +722571638 -2.04807E-001 2.46460E-001 6.12058E-001 -3.77416E-001 6.06228E-001 -3.80628E-001 -2.00641E-001 2.47242E-001 +723454367 -2.05242E-001 2.46347E-001 6.13257E-001 -3.75873E-001 6.08913E-001 -3.82871E-001 -2.02492E-001 2.50585E-001 +724337095 -2.01574E-001 2.47607E-001 6.07515E-001 -3.71383E-001 6.10691E-001 -3.80652E-001 -1.98150E-001 2.48896E-001 +725219823 -1.98385E-001 2.46511E-001 6.09728E-001 -3.77790E-001 6.11541E-001 -3.83103E-001 -1.94711E-001 2.50301E-001 +726102551 -2.01138E-001 2.48826E-001 6.16160E-001 -3.76101E-001 6.12307E-001 -3.79247E-001 -1.97143E-001 2.49178E-001 +726985279 -2.05455E-001 2.48163E-001 6.13585E-001 -3.77187E-001 6.14344E-001 -3.82181E-001 -1.95050E-001 2.50001E-001 +727868007 -1.98331E-001 2.46095E-001 6.10761E-001 -3.75928E-001 6.10224E-001 -3.81939E-001 -1.91905E-001 2.54688E-001 +728750736 -1.94027E-001 2.45309E-001 6.12505E-001 -3.75205E-001 6.12629E-001 -3.85335E-001 -2.00652E-001 2.47399E-001 +729633464 -1.97806E-001 2.55148E-001 6.10658E-001 -3.76145E-001 6.08368E-001 -3.82902E-001 -1.96097E-001 2.60629E-001 +730516192 -1.96210E-001 2.46539E-001 6.12602E-001 -3.78274E-001 6.12792E-001 -3.81070E-001 -1.97330E-001 2.53047E-001 +731398920 -1.97008E-001 2.50418E-001 6.13249E-001 -3.81576E-001 6.08963E-001 -3.81248E-001 -2.01673E-001 2.53192E-001 +732281648 -2.03503E-001 2.46222E-001 6.16221E-001 -3.75854E-001 6.13577E-001 -3.84287E-001 -2.02026E-001 2.50307E-001 +733164376 -2.01958E-001 2.44926E-001 6.12051E-001 -3.79116E-001 6.12262E-001 -3.84958E-001 -2.00650E-001 2.49425E-001 +734047105 -1.98320E-001 2.58946E-001 6.10476E-001 -3.78672E-001 6.12455E-001 -3.85785E-001 -1.94472E-001 2.60016E-001 +734929833 -2.00099E-001 2.54570E-001 6.10611E-001 -3.73083E-001 6.14333E-001 -3.81548E-001 -1.94707E-001 2.52699E-001 +735812561 -1.99071E-001 2.58334E-001 6.09194E-001 -3.76554E-001 6.08728E-001 -3.83447E-001 -1.97585E-001 2.55573E-001 +736695289 -1.99178E-001 2.50137E-001 6.15872E-001 -3.79700E-001 6.11391E-001 -3.83802E-001 -1.95665E-001 2.56225E-001 +737578017 -2.03199E-001 2.51351E-001 6.11707E-001 -3.83397E-001 6.13787E-001 -3.84194E-001 -2.00899E-001 2.59954E-001 +738460745 -1.99993E-001 2.51240E-001 6.12960E-001 -3.81642E-001 6.10608E-001 -3.84372E-001 -1.91005E-001 2.55378E-001 +739343473 -1.99277E-001 2.47193E-001 6.13634E-001 -3.77859E-001 6.12039E-001 -3.87627E-001 -1.93382E-001 2.60118E-001 +740226202 -2.05568E-001 2.53013E-001 6.17029E-001 -3.84498E-001 6.15329E-001 -3.84113E-001 -1.93607E-001 2.48613E-001 +741108930 -2.05831E-001 2.49786E-001 6.12625E-001 -3.82399E-001 6.15366E-001 -3.95517E-001 -1.95772E-001 2.56270E-001 +741991658 -2.01088E-001 2.52118E-001 6.14256E-001 -3.78304E-001 6.13166E-001 -3.83166E-001 -1.98228E-001 2.57171E-001 +742874386 -2.00043E-001 2.53128E-001 6.14796E-001 -3.80220E-001 6.12483E-001 -3.90084E-001 -1.92628E-001 2.56760E-001 +743757114 -1.88422E-001 2.53754E-001 6.19200E-001 -3.80892E-001 6.13029E-001 -3.89725E-001 -1.85743E-001 2.58943E-001 +744639842 -1.98855E-001 2.57352E-001 6.15223E-001 -3.80620E-001 6.13961E-001 -3.84717E-001 -1.91779E-001 2.55943E-001 +745522571 -1.92836E-001 2.54450E-001 6.17528E-001 -3.81849E-001 6.09026E-001 -3.93251E-001 -1.93120E-001 2.54589E-001 +746405299 -2.02013E-001 2.56440E-001 6.12304E-001 -3.86972E-001 6.09615E-001 -3.91194E-001 -1.93849E-001 2.55919E-001 +747288027 -1.96466E-001 2.51244E-001 6.14281E-001 -3.84154E-001 6.13541E-001 -3.90465E-001 -1.91404E-001 2.56737E-001 +748170755 -1.89090E-001 2.53339E-001 6.12972E-001 -3.80357E-001 6.18147E-001 -3.90705E-001 -1.83399E-001 2.55258E-001 +749053483 -1.97695E-001 2.58860E-001 6.19307E-001 -3.85419E-001 6.13769E-001 -3.91113E-001 -1.94321E-001 2.58509E-001 +749936211 -1.93355E-001 2.57792E-001 6.19540E-001 -3.78522E-001 6.10560E-001 -3.89795E-001 -1.96013E-001 2.58399E-001 +750818940 -1.98237E-001 2.59827E-001 6.15610E-001 -3.77670E-001 6.18817E-001 -3.87076E-001 -1.87766E-001 2.58441E-001 +751701668 -1.94748E-001 2.53067E-001 6.18234E-001 -3.75049E-001 6.16425E-001 -3.90465E-001 -2.00395E-001 2.62491E-001 +752584396 -2.01633E-001 2.57227E-001 6.21647E-001 -3.83571E-001 6.15030E-001 -3.86905E-001 -1.88492E-001 2.58274E-001 +753467124 -1.97709E-001 2.60086E-001 6.16774E-001 -3.81193E-001 6.09832E-001 -3.89160E-001 -1.91969E-001 2.62085E-001 +754349852 -1.99002E-001 2.54971E-001 6.12664E-001 -3.83952E-001 6.18181E-001 -3.89623E-001 -1.88799E-001 2.62384E-001 +755232580 -1.97963E-001 2.56580E-001 6.22963E-001 -3.81371E-001 6.13671E-001 -3.85919E-001 -1.90860E-001 2.63248E-001 +756115309 -1.93358E-001 2.57439E-001 6.17485E-001 -3.85408E-001 6.16128E-001 -3.92723E-001 -1.91380E-001 2.59401E-001 +756998037 -2.04520E-001 2.62193E-001 6.18564E-001 -3.84516E-001 6.11800E-001 -3.93160E-001 -1.88164E-001 2.64625E-001 +757880765 -1.96027E-001 2.53534E-001 6.13719E-001 -3.85311E-001 6.18467E-001 -3.91192E-001 -1.91917E-001 2.64201E-001 +758763493 -1.98226E-001 2.63789E-001 6.16139E-001 -3.88973E-001 6.14668E-001 -3.88214E-001 -1.90141E-001 2.67048E-001 +759646221 -1.94552E-001 2.59058E-001 6.19987E-001 -3.87714E-001 6.15897E-001 -3.93153E-001 -1.91456E-001 2.66095E-001 +760528949 -1.90580E-001 2.61621E-001 6.16826E-001 -3.95219E-001 6.16439E-001 -3.92744E-001 -1.94648E-001 2.61150E-001 +761411678 -1.96747E-001 2.64566E-001 6.14214E-001 -3.88110E-001 6.16616E-001 -3.89457E-001 -1.85763E-001 2.65007E-001 +762294406 -1.95641E-001 2.62554E-001 6.15643E-001 -3.81063E-001 6.10779E-001 -3.89829E-001 -1.93064E-001 2.66407E-001 +763177134 -1.87480E-001 2.61159E-001 6.16268E-001 -3.93345E-001 6.11706E-001 -3.89244E-001 -1.83743E-001 2.64060E-001 +764059862 -2.03411E-001 2.62862E-001 6.19852E-001 -3.84030E-001 6.12238E-001 -3.94024E-001 -1.90356E-001 2.61449E-001 +764942590 -1.91524E-001 2.59661E-001 6.14154E-001 -3.89674E-001 6.15622E-001 -3.95567E-001 -1.82957E-001 2.65901E-001 +765825318 -1.88569E-001 2.62705E-001 6.17140E-001 -3.88228E-001 6.09792E-001 -3.95643E-001 -1.84818E-001 2.76342E-001 +766708047 -1.93845E-001 2.65992E-001 6.19144E-001 -3.90568E-001 6.17051E-001 -3.91807E-001 -1.90929E-001 2.68306E-001 +767590775 -1.95297E-001 2.68352E-001 6.16745E-001 -3.89425E-001 6.18853E-001 -3.93325E-001 -1.95596E-001 2.66493E-001 +768473503 -1.78754E-001 2.63565E-001 6.16814E-001 -3.86592E-001 6.16446E-001 -3.96437E-001 -1.84807E-001 2.68927E-001 +769356231 -1.97101E-001 2.61581E-001 6.23354E-001 -3.86997E-001 6.19659E-001 -3.97017E-001 -1.82976E-001 2.69618E-001 +770238959 -1.90539E-001 2.72526E-001 6.14978E-001 -3.86541E-001 6.18250E-001 -3.97856E-001 -1.87184E-001 2.63832E-001 +771121687 -1.98335E-001 2.61366E-001 6.24062E-001 -3.86639E-001 6.15684E-001 -3.98827E-001 -1.90074E-001 2.62314E-001 +772004416 -1.93679E-001 2.64097E-001 6.17336E-001 -3.91271E-001 6.16394E-001 -3.97877E-001 -1.90669E-001 2.74858E-001 +772887144 -1.99302E-001 2.70038E-001 6.17940E-001 -3.88008E-001 6.17479E-001 -3.99031E-001 -1.83915E-001 2.66887E-001 +773769872 -1.92859E-001 2.68539E-001 6.20157E-001 -3.88664E-001 6.20262E-001 -3.94725E-001 -1.83510E-001 2.75481E-001 +774652600 -1.91523E-001 2.67185E-001 6.20191E-001 -3.96477E-001 6.18282E-001 -4.02518E-001 -1.85073E-001 2.74165E-001 +775535328 -1.87649E-001 2.63596E-001 6.17288E-001 -3.92106E-001 6.18456E-001 -3.93362E-001 -1.83812E-001 2.72075E-001 +776418056 -1.89848E-001 2.67875E-001 6.19213E-001 -3.95210E-001 6.11269E-001 -3.98343E-001 -1.81282E-001 2.70724E-001 +777300785 -1.89991E-001 2.79376E-001 6.17667E-001 -3.94446E-001 6.11831E-001 -3.97385E-001 -1.72027E-001 2.75821E-001 +778183513 -1.96491E-001 2.62551E-001 6.22713E-001 -3.98455E-001 6.20365E-001 -3.96953E-001 -1.85122E-001 2.70700E-001 +779066241 -1.89363E-001 2.75899E-001 6.18638E-001 -3.95289E-001 6.17067E-001 -3.98761E-001 -1.85896E-001 2.74039E-001 +779948969 -1.88512E-001 2.73278E-001 6.19111E-001 -4.00634E-001 6.15996E-001 -3.99308E-001 -1.94929E-001 2.74484E-001 +780831697 -1.86624E-001 2.67605E-001 6.15416E-001 -3.93376E-001 6.13531E-001 -3.98342E-001 -1.78804E-001 2.65862E-001 +781714425 -1.88516E-001 2.65848E-001 6.27131E-001 -3.90113E-001 6.18591E-001 -4.03518E-001 -1.89813E-001 2.73149E-001 +782597154 -1.80541E-001 2.77002E-001 6.19256E-001 -4.01540E-001 6.13626E-001 -4.02033E-001 -1.81972E-001 2.79600E-001 +783479882 -1.87957E-001 2.70224E-001 6.27502E-001 -3.93591E-001 6.19562E-001 -4.02907E-001 -1.84561E-001 2.76334E-001 +784362610 -1.87287E-001 2.69965E-001 6.21060E-001 -3.93639E-001 6.18399E-001 -3.98833E-001 -1.76224E-001 2.71070E-001 +785245338 -1.86201E-001 2.64710E-001 6.23389E-001 -3.92470E-001 6.18063E-001 -4.00804E-001 -1.77323E-001 2.76464E-001 +786128066 -1.81037E-001 2.67109E-001 6.18969E-001 -3.98219E-001 6.15778E-001 -4.05165E-001 -1.77427E-001 2.73356E-001 +787010794 -1.82957E-001 2.62961E-001 6.20245E-001 -3.99575E-001 6.20731E-001 -4.07035E-001 -1.75420E-001 2.73155E-001 +787893523 -1.76995E-001 2.84212E-001 6.19514E-001 -3.97425E-001 6.16250E-001 -4.09563E-001 -1.80522E-001 2.74085E-001 +788776251 -1.75404E-001 2.80818E-001 6.22155E-001 -4.01428E-001 6.13698E-001 -4.07220E-001 -1.79587E-001 2.78302E-001 +789658979 -1.82752E-001 2.77111E-001 6.23504E-001 -3.96945E-001 6.14492E-001 -4.06730E-001 -1.72945E-001 2.80476E-001 +790541707 -1.85890E-001 2.70671E-001 6.20926E-001 -3.96236E-001 6.11313E-001 -4.00888E-001 -1.85883E-001 2.79778E-001 +791424435 -1.85503E-001 2.80301E-001 6.19440E-001 -3.99010E-001 6.17294E-001 -4.03342E-001 -1.73512E-001 2.78810E-001 +792307163 -1.81007E-001 2.78709E-001 6.15957E-001 -3.98544E-001 6.18706E-001 -4.05542E-001 -1.76019E-001 2.85360E-001 +793189892 -1.87922E-001 2.71178E-001 6.17367E-001 -3.97919E-001 6.11064E-001 -4.08532E-001 -1.78289E-001 2.90678E-001 +794072620 -1.86243E-001 2.83457E-001 6.14228E-001 -3.93190E-001 6.13521E-001 -4.08459E-001 -1.77963E-001 2.85909E-001 +794955348 -1.83910E-001 2.80074E-001 6.10158E-001 -4.04721E-001 6.13091E-001 -4.06317E-001 -1.78909E-001 2.90379E-001 +795838076 -1.84560E-001 2.90626E-001 6.16128E-001 -3.96485E-001 6.18693E-001 -4.06829E-001 -1.76777E-001 2.94387E-001 +796720804 -1.72561E-001 2.81020E-001 6.19113E-001 -3.94737E-001 6.16400E-001 -4.04052E-001 -1.76024E-001 2.89167E-001 +797603532 -1.86065E-001 2.82600E-001 6.18195E-001 -3.95663E-001 6.13984E-001 -4.09226E-001 -1.69317E-001 2.79307E-001 +798486261 -1.81939E-001 2.84233E-001 6.11793E-001 -3.96534E-001 6.18485E-001 -4.10653E-001 -1.69420E-001 2.81609E-001 +799368989 -1.80828E-001 2.78205E-001 6.16093E-001 -4.00502E-001 6.16466E-001 -4.12095E-001 -1.77234E-001 2.88969E-001 +800251717 -1.82979E-001 2.87173E-001 6.11479E-001 -4.01712E-001 6.10093E-001 -4.17744E-001 -1.73793E-001 2.76635E-001 +801134445 -1.88612E-001 2.79597E-001 6.26493E-001 -4.02050E-001 6.09271E-001 -4.12925E-001 -1.77955E-001 2.81407E-001 +802017173 -1.82296E-001 2.86446E-001 6.16408E-001 -4.01916E-001 6.20683E-001 -4.05577E-001 -1.80128E-001 2.82492E-001 +802899901 -1.73750E-001 2.70902E-001 6.21871E-001 -4.02869E-001 6.16831E-001 -4.16589E-001 -1.69849E-001 2.83518E-001 +803782630 -1.78765E-001 2.85418E-001 6.19831E-001 -4.06206E-001 6.22178E-001 -4.12856E-001 -1.75426E-001 2.90918E-001 +804665358 -1.70048E-001 2.84371E-001 6.15977E-001 -4.09560E-001 6.19587E-001 -4.14257E-001 -1.86063E-001 2.84113E-001 +805548086 -1.80065E-001 2.84527E-001 6.16541E-001 -4.11182E-001 6.11289E-001 -4.13987E-001 -1.76478E-001 2.87140E-001 +806430814 -1.78754E-001 2.87491E-001 6.19753E-001 -4.04506E-001 6.12225E-001 -4.12823E-001 -1.69275E-001 2.86277E-001 +807313542 -1.68317E-001 2.85575E-001 6.25695E-001 -4.08847E-001 6.15318E-001 -4.14313E-001 -1.66803E-001 2.88605E-001 +808196270 -1.69158E-001 2.82879E-001 6.14677E-001 -4.10225E-001 6.16131E-001 -4.08038E-001 -1.62661E-001 2.89720E-001 +809078999 -1.69620E-001 2.85670E-001 6.19541E-001 -4.02419E-001 6.13000E-001 -4.13313E-001 -1.64553E-001 2.82118E-001 +809961727 -1.74164E-001 2.75453E-001 6.21023E-001 -4.12357E-001 6.10352E-001 -4.19134E-001 -1.67070E-001 2.77863E-001 +810844455 -1.71422E-001 2.94314E-001 6.11312E-001 -4.15839E-001 6.12541E-001 -4.14026E-001 -1.66067E-001 2.88745E-001 +811727183 -1.59512E-001 2.87728E-001 6.14329E-001 -4.08720E-001 6.14830E-001 -4.06956E-001 -1.61656E-001 2.90785E-001 +812609911 -1.74518E-001 2.76778E-001 6.17407E-001 -4.14155E-001 6.08803E-001 -4.17863E-001 -1.64086E-001 2.83576E-001 +813492639 -1.74532E-001 2.92310E-001 6.22603E-001 -4.03787E-001 6.17369E-001 -4.11744E-001 -1.67154E-001 2.90446E-001 +814375368 -1.70346E-001 2.89111E-001 6.16107E-001 -4.11829E-001 6.07310E-001 -4.19799E-001 -1.70044E-001 2.89634E-001 +815258096 -1.68139E-001 2.90957E-001 6.16370E-001 -4.12902E-001 6.15059E-001 -4.22843E-001 -1.69699E-001 2.89663E-001 +816140824 -1.63916E-001 2.86723E-001 6.18417E-001 -4.08406E-001 6.12885E-001 -4.23613E-001 -1.57169E-001 2.80200E-001 +817023552 -1.62006E-001 2.82837E-001 6.17232E-001 -4.13062E-001 6.13315E-001 -4.22829E-001 -1.68379E-001 2.96273E-001 +817906280 -1.60317E-001 2.93629E-001 6.19766E-001 -4.12159E-001 6.14807E-001 -4.20517E-001 -1.67970E-001 2.88092E-001 +818789008 -1.64032E-001 2.86025E-001 6.16041E-001 -4.15619E-001 6.10969E-001 -4.14131E-001 -1.64620E-001 2.91666E-001 +819671736 -1.64647E-001 2.86974E-001 6.16643E-001 -4.14964E-001 6.10344E-001 -4.22205E-001 -1.66235E-001 2.83860E-001 +820554465 -1.82869E-001 2.82478E-001 6.14055E-001 -4.19950E-001 6.16906E-001 -4.24767E-001 -1.70354E-001 2.85698E-001 +821437193 -1.75187E-001 2.85823E-001 6.16175E-001 -4.19706E-001 6.14449E-001 -4.19241E-001 -1.67153E-001 2.85719E-001 +822319921 -1.64128E-001 2.99027E-001 6.17667E-001 -4.09966E-001 6.13527E-001 -4.25082E-001 -1.50840E-001 2.94670E-001 +823202649 -1.68863E-001 2.94493E-001 6.15054E-001 -4.16028E-001 6.13138E-001 -4.21828E-001 -1.57751E-001 2.94465E-001 +824085377 -1.60342E-001 2.92839E-001 6.12285E-001 -4.24923E-001 6.08714E-001 -4.27091E-001 -1.52573E-001 3.02322E-001 +824968105 -1.75623E-001 2.97938E-001 6.16616E-001 -4.21740E-001 6.05273E-001 -4.21391E-001 -1.49213E-001 3.01112E-001 +825850834 -1.67688E-001 2.87895E-001 6.16924E-001 -4.21402E-001 6.08832E-001 -4.22252E-001 -1.59745E-001 2.93818E-001 +826733562 -1.58230E-001 2.97839E-001 6.16533E-001 -4.24449E-001 6.09214E-001 -4.27079E-001 -1.52596E-001 2.96787E-001 +827616290 -1.66526E-001 3.04126E-001 6.12842E-001 -4.20462E-001 6.16018E-001 -4.24791E-001 -1.54939E-001 2.90837E-001 +828499018 -1.58269E-001 2.99732E-001 6.10763E-001 -4.23134E-001 6.18653E-001 -4.26614E-001 -1.64469E-001 2.92273E-001 +829381746 -1.68602E-001 2.90659E-001 6.14865E-001 -4.16378E-001 6.12602E-001 -4.20693E-001 -1.48234E-001 3.01364E-001 +830264474 -1.53382E-001 2.97030E-001 6.11315E-001 -4.16870E-001 6.08851E-001 -4.31236E-001 -1.58079E-001 2.95393E-001 +831147203 -1.59826E-001 2.91267E-001 6.11971E-001 -4.18920E-001 6.09442E-001 -4.32385E-001 -1.57986E-001 2.90841E-001 +832029931 -1.65042E-001 3.04067E-001 6.11590E-001 -4.22608E-001 6.07069E-001 -4.37805E-001 -1.59265E-001 2.94914E-001 +832912659 -1.52038E-001 3.02293E-001 6.16155E-001 -4.23377E-001 6.06157E-001 -4.35640E-001 -1.54257E-001 2.98847E-001 +833795387 -1.63019E-001 3.01988E-001 6.13323E-001 -4.21346E-001 6.05026E-001 -4.32209E-001 -1.50878E-001 2.97672E-001 +834678115 -1.51972E-001 2.88314E-001 6.13394E-001 -4.21141E-001 6.03646E-001 -4.31147E-001 -1.49254E-001 2.95710E-001 +835560843 -1.57457E-001 2.98369E-001 6.10381E-001 -4.26914E-001 6.06131E-001 -4.38724E-001 -1.58536E-001 3.04046E-001 +836443572 -1.66869E-001 2.96534E-001 6.18538E-001 -4.27849E-001 6.11420E-001 -4.32092E-001 -1.72596E-001 2.97033E-001 +837326300 -1.61085E-001 2.91973E-001 6.15387E-001 -4.22003E-001 6.07625E-001 -4.36015E-001 -1.51529E-001 3.00024E-001 +838209028 -1.52008E-001 3.06083E-001 6.14422E-001 -4.24082E-001 6.02193E-001 -4.30738E-001 -1.44931E-001 3.04085E-001 +839091756 -1.55873E-001 3.02351E-001 6.08780E-001 -4.29214E-001 6.01643E-001 -4.38207E-001 -1.52243E-001 2.93436E-001 +839974484 -1.50603E-001 3.07619E-001 6.08225E-001 -4.25148E-001 6.08296E-001 -4.43513E-001 -1.40103E-001 3.03954E-001 +840857212 -1.48181E-001 3.02049E-001 6.13638E-001 -4.27391E-001 6.05909E-001 -4.40207E-001 -1.43731E-001 3.11252E-001 +841739941 -1.54729E-001 3.02104E-001 6.14784E-001 -4.29682E-001 6.04699E-001 -4.39926E-001 -1.49604E-001 3.00780E-001 +842622669 -1.56449E-001 2.92565E-001 5.99002E-001 -4.31112E-001 6.02055E-001 -4.43129E-001 -1.43437E-001 3.04239E-001 +843505397 -1.57054E-001 3.03000E-001 6.07608E-001 -4.28283E-001 5.98163E-001 -4.41377E-001 -1.51051E-001 3.10142E-001 +844388125 -1.57792E-001 3.00225E-001 6.05722E-001 -4.31838E-001 6.01511E-001 -4.41500E-001 -1.52351E-001 3.02797E-001 +845270853 -1.56084E-001 3.01088E-001 6.08554E-001 -4.22787E-001 6.07281E-001 -4.36283E-001 -1.47349E-001 2.96756E-001 +846153581 -1.50066E-001 2.90006E-001 6.08349E-001 -4.36414E-001 6.06336E-001 -4.51687E-001 -1.54308E-001 3.05901E-001 +847036310 -1.39695E-001 2.93973E-001 6.07229E-001 -4.34886E-001 6.04428E-001 -4.40691E-001 -1.55446E-001 2.89842E-001 +847919038 -1.53887E-001 3.01122E-001 6.08280E-001 -4.35479E-001 6.08288E-001 -4.46109E-001 -1.44420E-001 3.02473E-001 +848801766 -1.49158E-001 2.92572E-001 6.01211E-001 -4.48086E-001 6.07145E-001 -4.46754E-001 -1.44362E-001 3.06142E-001 +849684494 -1.49376E-001 3.08257E-001 6.08300E-001 -4.37292E-001 6.06804E-001 -4.46106E-001 -1.42069E-001 2.92502E-001 +850567222 -1.51623E-001 3.06665E-001 6.05532E-001 -4.43330E-001 6.03584E-001 -4.45024E-001 -1.44011E-001 3.07559E-001 +851449950 -1.56667E-001 2.97590E-001 6.11124E-001 -4.31990E-001 6.07884E-001 -4.40409E-001 -1.44250E-001 3.09006E-001 +852332679 -1.48008E-001 2.96146E-001 6.09994E-001 -4.36750E-001 5.98316E-001 -4.52616E-001 -1.38472E-001 2.92862E-001 +853215407 -1.49451E-001 3.03301E-001 6.11378E-001 -4.44626E-001 6.01172E-001 -4.50778E-001 -1.42923E-001 2.91745E-001 +854098135 -1.45812E-001 3.10235E-001 6.06931E-001 -4.36880E-001 6.02400E-001 -4.58290E-001 -1.43867E-001 3.08537E-001 +854980863 -1.46091E-001 2.92511E-001 6.06089E-001 -4.51711E-001 6.04113E-001 -4.53400E-001 -1.43626E-001 2.93736E-001 +855863591 -1.49017E-001 3.13231E-001 6.04651E-001 -4.40509E-001 6.02452E-001 -4.59637E-001 -1.39047E-001 3.05019E-001 +856746319 -1.36012E-001 3.09566E-001 6.07069E-001 -4.40152E-001 5.96923E-001 -4.58491E-001 -1.28627E-001 3.01611E-001 +857629048 -1.39889E-001 3.11098E-001 6.08634E-001 -4.43393E-001 6.02945E-001 -4.58679E-001 -1.36070E-001 3.09549E-001 +858511776 -1.31581E-001 3.02888E-001 6.07293E-001 -4.56497E-001 6.00755E-001 -4.57551E-001 -1.34427E-001 3.00958E-001 +859394504 -1.32934E-001 3.10914E-001 6.04964E-001 -4.40640E-001 6.00310E-001 -4.57342E-001 -1.31471E-001 2.90436E-001 +860277232 -1.38366E-001 3.04383E-001 6.07305E-001 -4.50502E-001 5.92706E-001 -4.59397E-001 -1.38634E-001 3.01176E-001 +861159960 -1.41813E-001 2.94406E-001 6.06955E-001 -4.54540E-001 5.99341E-001 -4.54217E-001 -1.22762E-001 2.97417E-001 +862042688 -1.40874E-001 2.98428E-001 6.05968E-001 -4.56978E-001 5.96242E-001 -4.64029E-001 -1.39595E-001 3.05533E-001 +862925417 -1.42910E-001 3.03253E-001 6.04774E-001 -4.53035E-001 6.01722E-001 -4.58024E-001 -1.33713E-001 3.10493E-001 +863808145 -1.37710E-001 2.99988E-001 6.02954E-001 -4.50740E-001 6.03562E-001 -4.62251E-001 -1.26433E-001 3.01539E-001 +864690873 -1.46149E-001 3.05535E-001 6.04583E-001 -4.54004E-001 6.03528E-001 -4.62032E-001 -1.24852E-001 3.02300E-001 +865573601 -1.42640E-001 3.01771E-001 6.10582E-001 -4.54472E-001 6.07977E-001 -4.58907E-001 -1.16985E-001 3.05055E-001 +866456329 -1.31650E-001 3.00803E-001 6.08540E-001 -4.49156E-001 5.94393E-001 -4.69565E-001 -1.19982E-001 3.17105E-001 +867339057 -1.43300E-001 3.12586E-001 5.95066E-001 -4.62554E-001 5.95930E-001 -4.67012E-001 -1.36114E-001 3.15015E-001 +868221786 -1.26127E-001 3.07349E-001 5.95140E-001 -4.57601E-001 5.90659E-001 -4.70709E-001 -1.34471E-001 2.93231E-001 +869104514 -1.34644E-001 3.02203E-001 6.02849E-001 -4.64838E-001 5.96041E-001 -4.62410E-001 -1.32949E-001 3.12276E-001 +869987242 -1.37454E-001 3.10392E-001 6.04889E-001 -4.58406E-001 5.97229E-001 -4.72982E-001 -1.28214E-001 3.14643E-001 +870869970 -1.35998E-001 3.11785E-001 6.01477E-001 -4.74094E-001 5.93194E-001 -4.66048E-001 -1.38491E-001 3.08947E-001 +871752698 -1.34661E-001 2.99816E-001 6.02550E-001 -4.60603E-001 5.92144E-001 -4.72989E-001 -1.40178E-001 3.07092E-001 +872635426 -1.40461E-001 3.06291E-001 5.97311E-001 -4.56953E-001 6.00478E-001 -4.60316E-001 -1.34028E-001 3.15051E-001 +873518155 -1.25769E-001 3.16058E-001 6.04982E-001 -4.67593E-001 5.93920E-001 -4.74215E-001 -1.34440E-001 3.07321E-001 +874400883 -1.44058E-001 3.01854E-001 6.01623E-001 -4.64338E-001 5.91035E-001 -4.80481E-001 -1.15848E-001 3.09522E-001 +875283611 -1.40200E-001 3.09631E-001 6.05651E-001 -4.66836E-001 5.88821E-001 -4.71539E-001 -1.29022E-001 3.22828E-001 +876166339 -1.39657E-001 2.99969E-001 5.93738E-001 -4.73475E-001 5.83239E-001 -4.83661E-001 -1.24757E-001 2.99785E-001 +877049067 -1.28450E-001 3.06048E-001 5.88965E-001 -4.85202E-001 5.93177E-001 -4.88279E-001 -1.32164E-001 3.01505E-001 +877931795 -1.32421E-001 3.04680E-001 5.97406E-001 -4.68729E-001 5.99819E-001 -4.82406E-001 -1.32865E-001 2.94456E-001 +878814524 -1.34153E-001 3.04596E-001 5.97876E-001 -4.70933E-001 5.89371E-001 -4.68776E-001 -1.18740E-001 3.06963E-001 +879697252 -1.20352E-001 3.07178E-001 5.92183E-001 -4.75738E-001 5.94407E-001 -4.84907E-001 -1.22283E-001 3.13603E-001 +880579980 -1.52632E-001 3.08413E-001 5.97574E-001 -4.81185E-001 5.89722E-001 -4.88057E-001 -1.23500E-001 3.12014E-001 +881462708 -1.24858E-001 3.02729E-001 5.96133E-001 -4.79146E-001 5.86695E-001 -4.89756E-001 -1.27753E-001 3.08222E-001 +882345436 -1.28906E-001 3.13265E-001 6.02758E-001 -4.73953E-001 5.94351E-001 -4.80783E-001 -1.15857E-001 3.02546E-001 +883228164 -1.20056E-001 3.03897E-001 5.96037E-001 -4.72954E-001 5.93836E-001 -4.81225E-001 -1.02114E-001 3.09482E-001 +884110893 -1.23387E-001 3.05290E-001 5.94351E-001 -4.82746E-001 5.92013E-001 -5.00165E-001 -1.21643E-001 3.12817E-001 +884993621 -1.20897E-001 3.12434E-001 6.07702E-001 -4.76277E-001 5.89529E-001 -4.85012E-001 -1.09406E-001 3.17998E-001 +885876349 -1.26734E-001 3.00524E-001 6.00507E-001 -4.81199E-001 5.92832E-001 -4.90624E-001 -1.16913E-001 2.98242E-001 +886759077 -1.28122E-001 3.12840E-001 6.01082E-001 -4.83714E-001 5.91205E-001 -4.89380E-001 -1.19052E-001 3.10834E-001 +887641805 -1.16830E-001 3.20780E-001 5.99750E-001 -4.77109E-001 5.93105E-001 -4.97204E-001 -1.11743E-001 3.22502E-001 +888524533 -1.19492E-001 3.08013E-001 5.97425E-001 -4.80910E-001 5.92326E-001 -4.88089E-001 -1.13881E-001 3.14466E-001 +889407262 -1.22311E-001 3.24588E-001 5.83187E-001 -4.90424E-001 5.83478E-001 -4.90078E-001 -1.08409E-001 3.07955E-001 +890289990 -1.25885E-001 3.19584E-001 5.90889E-001 -4.91989E-001 5.88270E-001 -5.00524E-001 -1.03815E-001 3.12367E-001 +891172718 -1.15401E-001 3.04752E-001 5.91970E-001 -4.85523E-001 5.88265E-001 -4.99029E-001 -1.19818E-001 3.00403E-001 +892055446 -1.22403E-001 3.12040E-001 5.89443E-001 -4.90386E-001 5.95831E-001 -4.89955E-001 -9.68830E-002 3.08481E-001 +892938174 -1.05713E-001 3.03274E-001 6.05545E-001 -4.96673E-001 5.88506E-001 -5.05073E-001 -1.10026E-001 2.99379E-001 +893820902 -1.14271E-001 3.06127E-001 5.96141E-001 -4.91274E-001 5.90480E-001 -4.91720E-001 -1.15448E-001 3.05700E-001 +894703631 -1.13072E-001 3.07455E-001 5.91848E-001 -4.82504E-001 5.88534E-001 -5.05491E-001 -1.16623E-001 3.15257E-001 +895586359 -1.22407E-001 2.93460E-001 6.02187E-001 -4.90412E-001 5.89802E-001 -5.05240E-001 -1.16299E-001 2.98888E-001 +896469087 -1.15605E-001 3.03274E-001 5.93466E-001 -4.97160E-001 5.93214E-001 -4.96550E-001 -1.23550E-001 3.06594E-001 +897351815 -1.18613E-001 3.04429E-001 5.91251E-001 -4.98765E-001 5.84383E-001 -5.08579E-001 -1.19227E-001 3.12381E-001 +898234543 -1.22667E-001 3.11878E-001 5.90519E-001 -5.05873E-001 5.80462E-001 -5.12965E-001 -1.19033E-001 3.01665E-001 +899117271 -1.07956E-001 3.23431E-001 6.00740E-001 -5.10926E-001 5.91567E-001 -5.21520E-001 -1.02128E-001 3.06355E-001 +900000000 -1.27646E-001 3.19690E-001 5.96287E-001 -5.03453E-001 5.99076E-001 -5.01970E-001 -1.22713E-001 3.26965E-001 + From c3639b2557d0f9b3c7929bdf706d7f895735f432 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 19 Nov 2019 10:00:17 +0100 Subject: [PATCH 38/84] Rewrite of touchstone parser - keeped compatible with previous version - add test examples of touchstone files - commented out writing to loaded file to fix bug #99 --- NanoVNASaver/NanoVNASaver.py | 3 +- NanoVNASaver/Touchstone.py | 84 +++++++++++++++++------ test/data/broken_pair.s1p | 9 +++ test/data/missing_pair.s2p | 16 +++++ test/data/unordered.s1p | 8 +++ test/data/{valid_common.s1p => valid.s1p} | 0 test/data/{valid_common.s2p => valid.s2p} | 0 test/data/valid_with_datacomment.s1p | 12 ++++ 8 files changed, 110 insertions(+), 22 deletions(-) create mode 100644 test/data/broken_pair.s1p create mode 100644 test/data/missing_pair.s2p create mode 100644 test/data/unordered.s1p rename test/data/{valid_common.s1p => valid.s1p} (100%) rename test/data/{valid_common.s2p => valid.s2p} (100%) create mode 100644 test/data/valid_with_datacomment.s1p diff --git a/NanoVNASaver/NanoVNASaver.py b/NanoVNASaver/NanoVNASaver.py index ee6c7ea9..17ba4446 100644 --- a/NanoVNASaver/NanoVNASaver.py +++ b/NanoVNASaver/NanoVNASaver.py @@ -905,7 +905,8 @@ def loadSweepFile(self): self.data21 = [] t = Touchstone(filename) t.load() - self.saveData(t.s11data, t.s21data, filename) + # shouldn't modify read file. even destroys it if read fails + # self.saveData(t.s11data, t.s21data, filename) self.dataUpdated() def sizeHint(self) -> QtCore.QSize: diff --git a/NanoVNASaver/Touchstone.py b/NanoVNASaver/Touchstone.py index 784d834b..e6b7d328 100644 --- a/NanoVNASaver/Touchstone.py +++ b/NanoVNASaver/Touchstone.py @@ -16,6 +16,7 @@ # along with this program. If not, see . import logging import cmath +import io from NanoVNASaver.RFTools import Datapoint logger = logging.getLogger(__name__) @@ -61,30 +62,73 @@ def parse(self, line): class Touchstone: - def __init__(self, filename): + def __init__(self, filename: str): self.filename = filename - self.s11data = [] - self.s21data = [] - self.s12data = [] - self.s22data = [] + self.sdata = [[], [], [], []] # at max 4 data pairs self.comments = [] self.opts = Options() - def load(self): - logger.info("Attempting to open file %s", self.filename) - sdata = [[], [], [], []] # at max 4 data pairs + @property + def s11data(self) -> list: + return self.sdata[0] - with open(self.filename, "r") as file: - for line in file: - line = line.strip() - if line.startswith("!"): - logger.info(line) - self.comments.append(line) - continue - break - self.opts.parse(line) + @s11data.setter + def s11data(self, data: list): + self.sdata[0] = data[:] + + @property + def s21data(self) -> list: + return self.sdata[1] + + @s21data.setter + def s21data(self, data: list): + self.sdata[1] = data[:] + + @property + def s12data(self) -> list: + return self.sdata[2] + + @s12data.setter + def s12data(self, data: list): + self.sdata[2] = data[:] - prev_freq = prev_len = 0 + @property + def s22data(self) -> list: + return self.sdata[3] + + @s22data.setter + def s22data(self, data: list): + self.sdata[3] = data[:] + + def _parse_comments(self, fp) -> str: + for line in fp: + line = line.strip() + if line.startswith("!"): + logger.info(line) + self.comments.append(line) + else: + return line + + def load(self): + logger.info("Attempting to open file %s", self.filename) + try: + with open(self.filename) as infile: + self.loads(infile.read()) + except TypeError as e: + logger.exception("Failed to parse %s: %s", self.filename, e) + except IOError as e: + logger.exception("Failed to open %s: %s", self.filename, e) + + def loads(self, s: str): + """Parse touchstone 1.1 string input + appends to existing sdata if Touchstone object exists + """ + with io.StringIO(s) as file: + opts_line = self._parse_comments(file) + self.opts.parse(opts_line) + + prev_freq = 0.0 + prev_len = 0 for line in file: # ignore empty lines (even if not specified) if not line.strip(): @@ -108,7 +152,7 @@ def load(self): elif data_len != prev_len: raise TypeError("Inconsistent number of pairs: " + line) - data_list = iter(sdata) + data_list = iter(self.sdata) vals = iter(data) for v in vals: if self.opts.format == "ri": @@ -118,7 +162,5 @@ def load(self): z = cmath.polar(float(v), float(next(vals))) next(data_list).append(Datapoint(freq, z.real, z.imag)) - self.s11data, self.s21data, self.s12data, self.s22data = sdata[:] - def setFilename(self, filename): self.filename = filename diff --git a/test/data/broken_pair.s1p b/test/data/broken_pair.s1p new file mode 100644 index 00000000..d0c69d7b --- /dev/null +++ b/test/data/broken_pair.s1p @@ -0,0 +1,9 @@ +# Hz S RI R 50 +140000000 -0.720544874 -0.074467673 +140307234 -0.707615315 -0.045678697 +140614468 -0.694235622 -0.017205553 +140921702 -0.679476678 0.011064857 +141228936 0.037949264 +141536169 -0.645231842 0.06495472 +141843404 -0.625548779 0.090901531 +142150638 -0.605278372 0.116493001 diff --git a/test/data/missing_pair.s2p b/test/data/missing_pair.s2p new file mode 100644 index 00000000..9800325b --- /dev/null +++ b/test/data/missing_pair.s2p @@ -0,0 +1,16 @@ +! Vector Network Analyzer VNA R2 +! Tucson Amateur Packet Radio +! Saturday, 9 November, 2019 17:48:47 +! Frequency S11 S21 S12 S22 +! ListType=Lin +# HZ S RI R 50 +000500000 -3.33238E-001 1.80018E-004 6.74780E-001 -8.19510E-007 6.75290E-001 -8.20129E-007 -3.33238E-001 3.08078E-004 +001382728 -3.33017E-001 6.89580E-004 6.74251E-001 -3.70855E-004 6.74761E-001 -5.04361E-004 -3.33016E-001 9.45694E-004 +002265456 -3.33136E-001 1.06095E-003 6.74766E-001 -1.00228E-003 6.75276E-001 -1.00304E-003 -3.33136E-001 1.06095E-003 +003148184 -3.33120E-001 1.97467E-003 6.74773E-001 -1.65230E-003 6.74773E-001 -1.65230E-003 -3.33121E-001 1.91064E-003 +004030912 -3.32847E-001 2.45743E-003 6.74777E-001 -2.28839E-003 6.75288E-001 -2.15679E-003 +004913640 -3.32746E-001 2.93382E-003 6.75260E-001 -2.94645E-003 6.75261E-001 -2.81312E-003 -3.32990E-001 3.06364E-003 +005796368 -3.33479E-001 3.06528E-003 6.75798E-001 -2.32365E-003 6.76309E-001 -2.32540E-003 -3.33479E-001 3.06528E-003 +006679097 -3.32609E-001 3.80377E-003 6.74764E-001 -4.08250E-003 6.74764E-001 -4.08250E-003 -3.32854E-001 3.80608E-003 +007561825 -3.32448E-001 4.35906E-003 6.75247E-001 -4.96650E-003 6.75249E-001 -4.69986E-003 -3.32692E-001 4.36169E-003 +008444553 -3.32510E-001 4.94361E-003 6.74737E-001 -5.33508E-003 6.75248E-001 -5.20579E-003 -3.32508E-001 5.13540E-003 diff --git a/test/data/unordered.s1p b/test/data/unordered.s1p new file mode 100644 index 00000000..a81aabe8 --- /dev/null +++ b/test/data/unordered.s1p @@ -0,0 +1,8 @@ +# Hz S RI R 50 +140000000 -0.720544874 -0.074467673 +140307234 -0.707615315 -0.045678697 +140921702 -0.679476678 0.011064857 +140614468 -0.694235622 -0.017205553 +141536169 -0.645231842 0.06495472 +141843404 -0.625548779 0.090901531 +142150638 -0.605278372 0.116493001 diff --git a/test/data/valid_common.s1p b/test/data/valid.s1p similarity index 100% rename from test/data/valid_common.s1p rename to test/data/valid.s1p diff --git a/test/data/valid_common.s2p b/test/data/valid.s2p similarity index 100% rename from test/data/valid_common.s2p rename to test/data/valid.s2p diff --git a/test/data/valid_with_datacomment.s1p b/test/data/valid_with_datacomment.s1p new file mode 100644 index 00000000..25e54e26 --- /dev/null +++ b/test/data/valid_with_datacomment.s1p @@ -0,0 +1,12 @@ +# Hz S RI R 50 +140000000 -0.720544874 -0.074467673 +140307234 -0.707615315 -0.045678697 +140614468 -0.694235622 -0.017205553 +140921702 -0.679476678 0.011064857 +141228936 -0.662805676 0.037949264 +141536169 -0.645231842 0.06495472 ! just a test comment +141843404 -0.625548779 0.090901531 +142150638 -0.605278372 0.116493001 +142457872 -0.583680212 0.140287563 +142765106 -0.560637235 0.16401714 +143072339 -0.536502182 0.186390563 From 2add3f8dc9aca6f4244f19e07e17226ffe93e6ba Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 19 Nov 2019 10:03:46 +0100 Subject: [PATCH 39/84] fixed typo --- NanoVNASaver/Touchstone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NanoVNASaver/Touchstone.py b/NanoVNASaver/Touchstone.py index e6b7d328..e83e7e65 100644 --- a/NanoVNASaver/Touchstone.py +++ b/NanoVNASaver/Touchstone.py @@ -9,7 +9,7 @@ # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theen.DM00296349 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License From 6db03fc481051a5cce2c4e62b5d2ebba4e5bc9f6 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 19 Nov 2019 10:27:21 +0100 Subject: [PATCH 40/84] Touchstone: added db format parsing --- NanoVNASaver/Touchstone.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/NanoVNASaver/Touchstone.py b/NanoVNASaver/Touchstone.py index e83e7e65..2b809120 100644 --- a/NanoVNASaver/Touchstone.py +++ b/NanoVNASaver/Touchstone.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import logging +import math import cmath import io from NanoVNASaver.RFTools import Datapoint @@ -109,6 +110,20 @@ def _parse_comments(self, fp) -> str: else: return line + def _append_line_data(self, freq: float, data: list): + data_list = iter(self.sdata) + vals = iter(data) + for v in vals: + if self.opts.format == "ri": + next(data_list).append( + Datapoint(freq, float(v), float(next(vals)))) + if self.opts.format == "ma": + z = cmath.polar(float(v), float(next(vals))) + next(data_list).append(Datapoint(freq, z.real, z.imag)) + if self.opts.format == "db": + z = cmath.polar(math.exp(float(v) / 20), float(next(vals))) + next(data_list).append(Datapoint(freq, z.real, z.imag)) + def load(self): logger.info("Attempting to open file %s", self.filename) try: @@ -147,20 +162,12 @@ def loads(self, s: str): if prev_len == 0: prev_len = data_len - if data_len % 2: - raise TypeError("Data values aren't pairs: " + line) + if data_len % 2: + raise TypeError("Data values aren't pairs: " + line) elif data_len != prev_len: raise TypeError("Inconsistent number of pairs: " + line) - data_list = iter(self.sdata) - vals = iter(data) - for v in vals: - if self.opts.format == "ri": - next(data_list).append( - Datapoint(freq, float(v), float(next(vals)))) - if self.opts.format == "ma": - z = cmath.polar(float(v), float(next(vals))) - next(data_list).append(Datapoint(freq, z.real, z.imag)) + self._append_line_data(freq, data) def setFilename(self, filename): self.filename = filename From f9fafca0e2c1c87fcb28aef297875c10152ffdaf Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 19 Nov 2019 10:27:59 +0100 Subject: [PATCH 41/84] reverted to write after load in NanoVNASaver reverted as data is not updated on charts if save wouldn't be called ... bug #99 keeps open in this regard --- NanoVNASaver/NanoVNASaver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NanoVNASaver/NanoVNASaver.py b/NanoVNASaver/NanoVNASaver.py index 17ba4446..8e5a4108 100644 --- a/NanoVNASaver/NanoVNASaver.py +++ b/NanoVNASaver/NanoVNASaver.py @@ -906,7 +906,7 @@ def loadSweepFile(self): t = Touchstone(filename) t.load() # shouldn't modify read file. even destroys it if read fails - # self.saveData(t.s11data, t.s21data, filename) + self.saveData(t.s11data, t.s21data, filename) self.dataUpdated() def sizeHint(self) -> QtCore.QSize: From ee1f7bf909df2b5e16cbe017e3dbf86b3182e083 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 19 Nov 2019 11:27:05 +0100 Subject: [PATCH 42/84] Touchstone unit assertions --- NanoVNASaver/Touchstone.py | 43 ++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/NanoVNASaver/Touchstone.py b/NanoVNASaver/Touchstone.py index 2b809120..661bec09 100644 --- a/NanoVNASaver/Touchstone.py +++ b/NanoVNASaver/Touchstone.py @@ -32,13 +32,34 @@ class Options: "khz": 10**3, "hz": 10**0, } - - def __init__(self): + VALID_UNITS = UNIT_TO_FACTOR.keys() + VALID_PARAMETERS = "syzgh" + VALID_FORMATS = ("ma", "db", "ri") + + def __init__(self, + unit: str = "GHZ", + parameter: str = "S", + t_format: str = "ma", + resistance: int = 50): # set defaults - self.factor = Options.UNIT_TO_FACTOR["ghz"] - self.parameter = "s" - self.format = "ma" - self.resistance = 50 + assert unit.lower() in Options.VALID_UNITS + assert parameter.lower() in Options.VALID_PARAMETERS + assert t_format.lower() in Options.VALID_FORMATS + assert resistance > 0 + self.unit = unit.lower() + self.parameter = parameter.lower() + self.format = t_format.lower() + self.resistance = resistance + + @property + def factor(self): + return Options.UNIT_TO_FACTOR[self.unit] + + def __str__(self): + return ( + f"# {self.unit} {self.parameter}" + f" {self.format} r {self.resistance}" + ).upper() def parse(self, line): if not line.startswith("#"): @@ -46,13 +67,13 @@ def parse(self, line): pfact = pparam = pformat = presist = False params = iter(line[1:].lower().split()) for p in params: - if p in ("ghz", "mhz", "khz", "hz") and not pfact: + if p in Options.VALID_UNITS and not pfact: self.factor = Options.UNIT_TO_FACTOR[p] pfact = True - elif p in "syzgh" and not pparam: + elif p in Options.VALID_PARAMETERS and not pparam: self.parameter = p pparam = True - elif p in ("ma", "db", "ri") and not pformat: + elif p in Options.VALID_FORMATS and not pformat: self.format = p pformat = True elif p == "r" and not presist: @@ -107,8 +128,8 @@ def _parse_comments(self, fp) -> str: if line.startswith("!"): logger.info(line) self.comments.append(line) - else: - return line + continue + return line def _append_line_data(self, freq: float, data: list): data_list = iter(self.sdata) From f02524c4de7c4418c99d336eeba918d4b33ba168 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 19 Nov 2019 11:32:07 +0100 Subject: [PATCH 43/84] Touchstone: setter not needed for lists --- NanoVNASaver/Touchstone.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/NanoVNASaver/Touchstone.py b/NanoVNASaver/Touchstone.py index 661bec09..5d84a144 100644 --- a/NanoVNASaver/Touchstone.py +++ b/NanoVNASaver/Touchstone.py @@ -94,34 +94,18 @@ def __init__(self, filename: str): def s11data(self) -> list: return self.sdata[0] - @s11data.setter - def s11data(self, data: list): - self.sdata[0] = data[:] - @property def s21data(self) -> list: return self.sdata[1] - @s21data.setter - def s21data(self, data: list): - self.sdata[1] = data[:] - @property def s12data(self) -> list: return self.sdata[2] - @s12data.setter - def s12data(self, data: list): - self.sdata[2] = data[:] - @property def s22data(self) -> list: return self.sdata[3] - @s22data.setter - def s22data(self, data: list): - self.sdata[3] = data[:] - def _parse_comments(self, fp) -> str: for line in fp: line = line.strip() From c21ebba3fb9a15aaef50260b958227cd28c44ab6 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 19 Nov 2019 11:34:15 +0100 Subject: [PATCH 44/84] Touchstone: added type hints --- NanoVNASaver/Touchstone.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/NanoVNASaver/Touchstone.py b/NanoVNASaver/Touchstone.py index 5d84a144..c58ad3f9 100644 --- a/NanoVNASaver/Touchstone.py +++ b/NanoVNASaver/Touchstone.py @@ -52,16 +52,16 @@ def __init__(self, self.resistance = resistance @property - def factor(self): + def factor(self) -> int: return Options.UNIT_TO_FACTOR[self.unit] - def __str__(self): + def __str__(self) -> str: return ( f"# {self.unit} {self.parameter}" f" {self.format} r {self.resistance}" ).upper() - def parse(self, line): + def parse(self, line: str): if not line.startswith("#"): raise TypeError("Not an option line: " + line) pfact = pparam = pformat = presist = False @@ -174,5 +174,5 @@ def loads(self, s: str): self._append_line_data(freq, data) - def setFilename(self, filename): + def setFilename(self, filename: str): self.filename = filename From ed42cd311a9fbe5db03466ea00e41611125d4cdd Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 19 Nov 2019 11:49:43 +0100 Subject: [PATCH 45/84] Spelling errors and missleading comment removed --- NanoVNASaver/NanoVNASaver.py | 1 - NanoVNASaver/Touchstone.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/NanoVNASaver/NanoVNASaver.py b/NanoVNASaver/NanoVNASaver.py index 8e5a4108..ee6c7ea9 100644 --- a/NanoVNASaver/NanoVNASaver.py +++ b/NanoVNASaver/NanoVNASaver.py @@ -905,7 +905,6 @@ def loadSweepFile(self): self.data21 = [] t = Touchstone(filename) t.load() - # shouldn't modify read file. even destroys it if read fails self.saveData(t.s11data, t.s21data, filename) self.dataUpdated() diff --git a/NanoVNASaver/Touchstone.py b/NanoVNASaver/Touchstone.py index c58ad3f9..8c434036 100644 --- a/NanoVNASaver/Touchstone.py +++ b/NanoVNASaver/Touchstone.py @@ -79,7 +79,7 @@ def parse(self, line: str): elif p == "r" and not presist: self.resistance = int(next(params)) else: - raise TypeError("Illegial option line: " + line) + raise TypeError("Illegal option line: " + line) class Touchstone: @@ -162,7 +162,7 @@ def loads(self, s: str): # consistency checks if freq <= prev_freq: - raise TypeError("Frequeny not ascending: " + line) + raise TypeError("Frequency not ascending: " + line) prev_freq = freq if prev_len == 0: From 914e640217829d30a0d6dc429c048feeef3cb165 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 19 Nov 2019 12:26:54 +0100 Subject: [PATCH 46/84] fixed bug - Touchstone phase data is in degrees --- NanoVNASaver/Touchstone.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/NanoVNASaver/Touchstone.py b/NanoVNASaver/Touchstone.py index 8c434036..f917170e 100644 --- a/NanoVNASaver/Touchstone.py +++ b/NanoVNASaver/Touchstone.py @@ -123,10 +123,12 @@ def _append_line_data(self, freq: float, data: list): next(data_list).append( Datapoint(freq, float(v), float(next(vals)))) if self.opts.format == "ma": - z = cmath.polar(float(v), float(next(vals))) + z = cmath.polar(float(v), + math.radians(float(next(vals)))) next(data_list).append(Datapoint(freq, z.real, z.imag)) if self.opts.format == "db": - z = cmath.polar(math.exp(float(v) / 20), float(next(vals))) + z = cmath.polar(math.exp(float(v) / 20), + math.radians(float(next(vals)))) next(data_list).append(Datapoint(freq, z.real, z.imag)) def load(self): From a283148ec68a89eef9bce963b2e6366ae5bf9663 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 19 Nov 2019 19:13:45 +0100 Subject: [PATCH 47/84] fixed options factor prorperty parsing tried to update factor but factor is now a property --- NanoVNASaver/Touchstone.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/NanoVNASaver/Touchstone.py b/NanoVNASaver/Touchstone.py index f917170e..71158225 100644 --- a/NanoVNASaver/Touchstone.py +++ b/NanoVNASaver/Touchstone.py @@ -64,12 +64,12 @@ def __str__(self) -> str: def parse(self, line: str): if not line.startswith("#"): raise TypeError("Not an option line: " + line) - pfact = pparam = pformat = presist = False + punit = pparam = pformat = presist = False params = iter(line[1:].lower().split()) for p in params: - if p in Options.VALID_UNITS and not pfact: - self.factor = Options.UNIT_TO_FACTOR[p] - pfact = True + if p in Options.VALID_UNITS and not punit: + self.unit = p + punit = True elif p in Options.VALID_PARAMETERS and not pparam: self.parameter = p pparam = True From 686b4455620be909c4d5916045586289b7a7d009 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 19 Nov 2019 19:16:02 +0100 Subject: [PATCH 48/84] added unit tests to touchstone module --- test/test_touchstone.py | 74 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 test/test_touchstone.py diff --git a/test/test_touchstone.py b/test/test_touchstone.py new file mode 100644 index 00000000..3ae07479 --- /dev/null +++ b/test/test_touchstone.py @@ -0,0 +1,74 @@ +# NanoVNASaver +# A python program to view and export Touchstone data from a NanoVNA +# Copyright (C) 2019. Rune B. Broberg +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +import sys +import unittest + +# Import targets to be tested +from NanoVNASaver.Touchstone import Options, Touchstone + +class TestTouchstoneOptions(unittest.TestCase): + def setUp(self): + self.opts = Options() + + def test_defaults(self): + self.assertEqual(self.opts.unit, "ghz") + self.assertEqual(self.opts.parameter, "s") + self.assertEqual(self.opts.format, "ma") + self.assertEqual(self.opts.resistance, 50) + self.assertEqual(self.opts.factor, 1000000000) + self.assertEqual(str(self.opts), "# GHZ S MA R 50") + + def test_parse(self): + self.assertRaisesRegex( + TypeError, "Not an option line:", + self.opts.parse, "") + self.assertRaisesRegex( + TypeError, "Not an option line: !", + self.opts.parse, "!") + self.assertRaisesRegex( + TypeError, "Illegal option line: # ILLEGAL", + self.opts.parse, "# ILLEGAL") + self.assertRaisesRegex( + TypeError, "Illegal option line: # GHz mhz", + self.opts.parse, "# GHz mhz") + self.opts.parse('# khz') + self.assertEqual(str(self.opts), "# KHZ S MA R 50") + self.assertEqual(self.opts.factor, 1000) + self.opts.parse('# r 123 ri hz y') + self.assertEqual(str(self.opts), "# HZ Y RI R 123") + self.assertEqual(self.opts.factor, 1) + +class TestTouchstoneTouchstone(unittest.TestCase): + + def test_load(self): + ts = Touchstone("./test/data/valid.s1p") + ts.load() + self.assertEqual(str(ts.opts), "# HZ S RI R 50") + self.assertEqual(len(ts.s11data), 1010) + self.assertEqual(len(ts.s21data), 0) + + ts = Touchstone("./test/data/valid.s2p") + ts.load() + self.assertEqual(str(ts.opts), "# HZ S RI R 50") + self.assertEqual(len(ts.s11data), 1020) + self.assertEqual(len(ts.s21data), 1020) + self.assertEqual(len(ts.s12data), 1020) + self.assertEqual(len(ts.s22data), 1020) + self.assertIn("! Vector Network Analyzer VNA R2", ts.comments) + +if __name__ == '__main__': + unittest.main(verbosity=2) From dc2cd080c721ab331d530b34203c8a7f99c57356 Mon Sep 17 00:00:00 2001 From: Rune Broberg Date: Wed, 20 Nov 2019 14:10:24 +0100 Subject: [PATCH 49/84] Small error in the hardware interface --- NanoVNASaver/Hardware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NanoVNASaver/Hardware.py b/NanoVNASaver/Hardware.py index cce840e8..4cf20720 100644 --- a/NanoVNASaver/Hardware.py +++ b/NanoVNASaver/Hardware.py @@ -200,7 +200,7 @@ def readFrequencies(self) -> List[str]: return self.readValues("frequencies") def readValues11(self) -> List[str]: - return self.readValues("data 1") + return self.readValues("data 0") def readValues21(self) -> List[str]: return self.readValues("data 1") From 7f0bde67e7384feb506f068700334af01b972878 Mon Sep 17 00:00:00 2001 From: Rune Broberg Date: Wed, 20 Nov 2019 15:02:03 +0100 Subject: [PATCH 50/84] Early version of offset delay --- NanoVNASaver/Calibration.py | 24 ++++++++++++++++++++++++ NanoVNASaver/Chart.py | 12 ++++++------ NanoVNASaver/SweepWorker.py | 14 ++++++++++++++ 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/NanoVNASaver/Calibration.py b/NanoVNASaver/Calibration.py index f89e3691..02f29b65 100644 --- a/NanoVNASaver/Calibration.py +++ b/NanoVNASaver/Calibration.py @@ -84,6 +84,13 @@ def __init__(self, app): # btn_cal_isolation.setDisabled(True) self.cal_isolation_label = QtWidgets.QLabel("Uncalibrated") + self.input_offset_delay = QtWidgets.QDoubleSpinBox() + self.input_offset_delay.setValue(0) + self.input_offset_delay.setSuffix(" ps") + self.input_offset_delay.setAlignment(QtCore.Qt.AlignRight) + self.input_offset_delay.valueChanged.connect(self.setOffsetDelay) + self.input_offset_delay.setRange(-10e6, 10e6) + calibration_control_layout.addRow(btn_cal_short, self.cal_short_label) calibration_control_layout.addRow(btn_cal_open, self.cal_open_label) calibration_control_layout.addRow(btn_cal_load, self.cal_load_label) @@ -91,6 +98,7 @@ def __init__(self, app): calibration_control_layout.addRow(btn_cal_through, self.cal_through_label) calibration_control_layout.addRow(QtWidgets.QLabel("")) + calibration_control_layout.addRow("Offset delay", self.input_offset_delay) self.btn_automatic = QtWidgets.QPushButton("Calibration assistant") calibration_control_layout.addRow(self.btn_automatic) @@ -433,6 +441,10 @@ def reset(self): self.app.saveData(self.app.worker.rawData11, self.app.worker.rawData21, self.app.sweepSource) self.app.worker.signals.updated.emit() + def setOffsetDelay(self, value: float): + logger.debug("New offset delay value: %f ps", value) + self.app.worker.offsetDelay = value / 10e12 + def calculate(self): if self.app.btnStopSweep.isEnabled(): # Currently sweeping @@ -922,6 +934,18 @@ def correct21(self, re, im, freq): s21 = (s21m - self.e30[index]) / self.e10e32[index] return s21.real, s21.imag + @staticmethod + def correctDelay11(d: Datapoint, delay): + input_val = np.complex(d.re, d.im) + output = input_val * np.exp(np.complex(0, 1) * 2 * 2 * math.pi * d.freq * delay * -1) + return Datapoint(d.freq, output.real, output.imag) + + @staticmethod + def correctDelay21(d: Datapoint, delay): + input_val = np.complex(d.re, d.im) + output = input_val * np.exp(np.complex(0, 1) * 2 * math.pi * d.freq * delay * -1) + return Datapoint(d.freq, output.real, output.imag) + def saveCalibration(self, filename): # Save the calibration data to file if filename == "" or not self.isValid1Port(): diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index ef60126c..f92e4dc7 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -3785,7 +3785,7 @@ def drawValues(self, qp: QtGui.QPainter): minValue = 1 maxValue = -1 for d in self.data: - val = d.to_capacitive_equivalent() + val = d.capacitive_equivalent() if val > maxValue: maxValue = val if val < minValue: @@ -3793,7 +3793,7 @@ def drawValues(self, qp: QtGui.QPainter): for d in self.reference: # Also check min/max for the reference sweep if d.freq < self.fstart or d.freq > self.fstop: continue - val = d.to_capacitive_equivalent() + val = d.capacitive_equivalent() if val > maxValue: maxValue = val if val < minValue: @@ -3832,7 +3832,7 @@ def drawValues(self, qp: QtGui.QPainter): self.drawMarkers(qp) def getYPosition(self, d: Datapoint) -> int: - return self.topMargin + round((self.maxValue - d.to_capacitive_equivalent()) / self.span * self.chartHeight) + return self.topMargin + round((self.maxValue - d.capacitive_equivalent()) / self.span * self.chartHeight) def valueAtPosition(self, y) -> List[float]: absy = y - self.topMargin @@ -3908,7 +3908,7 @@ def drawValues(self, qp: QtGui.QPainter): minValue = 1 maxValue = -1 for d in self.data: - val = d.to_inductive_equivalent() + val = d.inductive_equivalent() if val > maxValue: maxValue = val if val < minValue: @@ -3916,7 +3916,7 @@ def drawValues(self, qp: QtGui.QPainter): for d in self.reference: # Also check min/max for the reference sweep if d.freq < self.fstart or d.freq > self.fstop: continue - val = d.to_inductive_equivalent() + val = d.inductive_equivalent() if val > maxValue: maxValue = val if val < minValue: @@ -3955,7 +3955,7 @@ def drawValues(self, qp: QtGui.QPainter): self.drawMarkers(qp) def getYPosition(self, d: Datapoint) -> int: - return self.topMargin + round((self.maxValue - d.to_inductive_equivalent()) / self.span * self.chartHeight) + return self.topMargin + round((self.maxValue - d.inductive_equivalent()) / self.span * self.chartHeight) def valueAtPosition(self, y) -> List[float]: absy = y - self.topMargin diff --git a/NanoVNASaver/SweepWorker.py b/NanoVNASaver/SweepWorker.py index 48f55c1f..a39abe0a 100644 --- a/NanoVNASaver/SweepWorker.py +++ b/NanoVNASaver/SweepWorker.py @@ -24,6 +24,7 @@ import NanoVNASaver import logging +from NanoVNASaver.Calibration import Calibration from NanoVNASaver.Hardware import VNA, InvalidVNA from NanoVNASaver.RFTools import RFTools, Datapoint @@ -57,6 +58,7 @@ def __init__(self, app: NanoVNASaver): self.averages = 3 self.truncates = 0 self.error_message = "" + self.offsetDelay = 0 @pyqtSlot() def run(self): @@ -183,6 +185,7 @@ def updateData(self, values11, values21, offset): re, im = self.app.calibration.correct11(re, im, freq) if self.app.calibration.isValid2Port(): re21, im21 = self.app.calibration.correct21(re21, im21, freq) + self.data11[offset*101 + i] = Datapoint(freq, re, im) self.data21[offset * 101 + i] = Datapoint(freq, re21, im21) self.rawData11[offset * 101 + i] = rawData11 @@ -212,6 +215,17 @@ def saveData(self, frequencies, values11, values21): def applyCalibration(self, raw_data11: List[Datapoint], raw_data21: List[Datapoint]) ->\ (List[Datapoint], List[Datapoint]): + if self.offsetDelay != 0: + logger.debug("Applying offset delay of %f ps.", self.offsetDelay) + tmp = [] + for d in raw_data11: + tmp.append(Calibration.correctDelay11(d, self.offsetDelay)) + raw_data11 = tmp + tmp = [] + for d in raw_data21: + tmp.append(Calibration.correctDelay21(d, self.offsetDelay)) + raw_data21 = tmp + if not self.app.calibration.isCalculated: return raw_data11, raw_data21 From 192423dfdd49f85babd0e36f44eb82ae33200f1a Mon Sep 17 00:00:00 2001 From: Rune Broberg Date: Wed, 20 Nov 2019 16:37:57 +0100 Subject: [PATCH 51/84] Apply immediately when the value is changed. --- NanoVNASaver/Calibration.py | 11 ++++++++++- NanoVNASaver/SweepWorker.py | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/NanoVNASaver/Calibration.py b/NanoVNASaver/Calibration.py index 02f29b65..36120c76 100644 --- a/NanoVNASaver/Calibration.py +++ b/NanoVNASaver/Calibration.py @@ -444,6 +444,14 @@ def reset(self): def setOffsetDelay(self, value: float): logger.debug("New offset delay value: %f ps", value) self.app.worker.offsetDelay = value / 10e12 + if len(self.app.worker.rawData11) > 0: + # There's raw data, so we can get corrected data + logger.debug("Applying new offset to existing sweep data.") + self.app.worker.data11, self.app.worker.data21 = self.app.worker.applyCalibration(self.app.worker.rawData11, + self.app.worker.rawData21) + logger.debug("Saving and displaying corrected data.") + self.app.saveData(self.app.worker.data11, self.app.worker.data21, self.app.sweepSource) + self.app.worker.signals.updated.emit() def calculate(self): if self.app.btnStopSweep.isEnabled(): @@ -512,7 +520,8 @@ def calculate(self): if len(self.app.worker.rawData11) > 0: # There's raw data, so we can get corrected data logger.debug("Applying calibration to existing sweep data.") - self.app.worker.data11, self.app.worker.data21 = self.app.worker.applyCalibration(self.app.worker.rawData11, self.app.worker.rawData21) + self.app.worker.data11, self.app.worker.data21 = self.app.worker.applyCalibration( + self.app.worker.rawData11, self.app.worker.rawData21) logger.debug("Saving and displaying corrected data.") self.app.saveData(self.app.worker.data11, self.app.worker.data21, self.app.sweepSource) self.app.worker.signals.updated.emit() diff --git a/NanoVNASaver/SweepWorker.py b/NanoVNASaver/SweepWorker.py index a39abe0a..ad976229 100644 --- a/NanoVNASaver/SweepWorker.py +++ b/NanoVNASaver/SweepWorker.py @@ -216,7 +216,7 @@ def saveData(self, frequencies, values11, values21): def applyCalibration(self, raw_data11: List[Datapoint], raw_data21: List[Datapoint]) ->\ (List[Datapoint], List[Datapoint]): if self.offsetDelay != 0: - logger.debug("Applying offset delay of %f ps.", self.offsetDelay) + logger.debug("Applying offset delay of %f ps.", self.offsetDelay * 10e12) tmp = [] for d in raw_data11: tmp.append(Calibration.correctDelay11(d, self.offsetDelay)) From 394b65f8442ad1894831a4ad4e32b810d7cf4981 Mon Sep 17 00:00:00 2001 From: Rune Broberg Date: Wed, 20 Nov 2019 16:53:56 +0100 Subject: [PATCH 52/84] Consistent naming with the rest of the software. --- NanoVNASaver/Chart.py | 16 ++++++++-------- NanoVNASaver/Marker.py | 2 +- NanoVNASaver/RFTools.py | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index f92e4dc7..9e90e9a4 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -2084,7 +2084,7 @@ def drawChart(self, qp: QtGui.QPainter): minQ = 0 maxQ = 0 for d in self.data: - Q = d.q_factor() + Q = d.qFactor() if Q > maxQ: maxQ = Q scale = 0 @@ -2157,7 +2157,7 @@ def drawValues(self, qp: QtGui.QPainter): self.drawMarkers(qp) def getYPosition(self, d: Datapoint) -> int: - Q = d.q_factor() + Q = d.qFactor() return self.topMargin + round((self.maxQ - Q) / self.span * self.chartHeight) def valueAtPosition(self, y) -> List[float]: @@ -3785,7 +3785,7 @@ def drawValues(self, qp: QtGui.QPainter): minValue = 1 maxValue = -1 for d in self.data: - val = d.capacitive_equivalent() + val = d.capacitiveEquivalent() if val > maxValue: maxValue = val if val < minValue: @@ -3793,7 +3793,7 @@ def drawValues(self, qp: QtGui.QPainter): for d in self.reference: # Also check min/max for the reference sweep if d.freq < self.fstart or d.freq > self.fstop: continue - val = d.capacitive_equivalent() + val = d.capacitiveEquivalent() if val > maxValue: maxValue = val if val < minValue: @@ -3832,7 +3832,7 @@ def drawValues(self, qp: QtGui.QPainter): self.drawMarkers(qp) def getYPosition(self, d: Datapoint) -> int: - return self.topMargin + round((self.maxValue - d.capacitive_equivalent()) / self.span * self.chartHeight) + return self.topMargin + round((self.maxValue - d.capacitiveEquivalent()) / self.span * self.chartHeight) def valueAtPosition(self, y) -> List[float]: absy = y - self.topMargin @@ -3908,7 +3908,7 @@ def drawValues(self, qp: QtGui.QPainter): minValue = 1 maxValue = -1 for d in self.data: - val = d.inductive_equivalent() + val = d.inductiveEquivalent() if val > maxValue: maxValue = val if val < minValue: @@ -3916,7 +3916,7 @@ def drawValues(self, qp: QtGui.QPainter): for d in self.reference: # Also check min/max for the reference sweep if d.freq < self.fstart or d.freq > self.fstop: continue - val = d.inductive_equivalent() + val = d.inductiveEquivalent() if val > maxValue: maxValue = val if val < minValue: @@ -3955,7 +3955,7 @@ def drawValues(self, qp: QtGui.QPainter): self.drawMarkers(qp) def getYPosition(self, d: Datapoint) -> int: - return self.topMargin + round((self.maxValue - d.inductive_equivalent()) / self.span * self.chartHeight) + return self.topMargin + round((self.maxValue - d.inductiveEquivalent()) / self.span * self.chartHeight) def valueAtPosition(self, y) -> List[float]: absy = y - self.topMargin diff --git a/NanoVNASaver/Marker.py b/NanoVNASaver/Marker.py index 4c77e60b..309461d2 100644 --- a/NanoVNASaver/Marker.py +++ b/NanoVNASaver/Marker.py @@ -381,7 +381,7 @@ def updateLabels(self, s11data: List[Datapoint], s21data: List[Datapoint]): if vswr < 0: vswr = "-" self.vswr_label.setText(str(vswr)) - q = s11data[self.location].q_factor() + q = s11data[self.location].qFactor() if q > 10000 or q < 0: q_str = "\N{INFINITY}" elif q > 1000: diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index d16d3c22..fdeb356a 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -38,12 +38,12 @@ class Datapoint(NamedTuple): @property def z(self): - """ return datapoint impedance as complex number """ + """ return the datapoint impedance as complex number """ return complex(self.re, self.im) @property def phase(self): - """ return datapoints phase value """ + """ return the datapoint's phase value """ return cmath.phase(self.z) @property @@ -65,13 +65,13 @@ def vswr(self) -> float: def impedance(self, ref_impedance: float = 50) -> complex: return ref_impedance * ((-self.z - 1) / (self.z - 1)) - def q_factor(self, ref_impedance: float = 50) -> float: + def qFactor(self, ref_impedance: float = 50) -> float: imp = self.impedance(ref_impedance) if imp.real == 0.0: return -1 return abs(imp.imag / imp.real) - def capacitive_equivalent(self, ref_impedance: float = 50) -> float: + def capacitiveEquivalent(self, ref_impedance: float = 50) -> float: if self.freq == 0: return math.inf imp = self.impedance(ref_impedance) @@ -79,7 +79,7 @@ def capacitive_equivalent(self, ref_impedance: float = 50) -> float: return math.inf return -(1 / (self.freq * 2 * math.pi * imp.imag)) - def inductive_equivalent(self, ref_impedance: float = 50) -> float: + def inductiveEquivalent(self, ref_impedance: float = 50) -> float: if self.freq == 0: return math.inf imp = self.impedance(ref_impedance) From f3ba3fe3a8f16f46576c0e7b4756cfd4f71799b8 Mon Sep 17 00:00:00 2001 From: Rune Broberg Date: Wed, 20 Nov 2019 16:56:09 +0100 Subject: [PATCH 53/84] Cleanup --- NanoVNASaver/Calibration.py | 6 +++--- NanoVNASaver/Hardware.py | 1 - NanoVNASaver/Marker.py | 1 - NanoVNASaver/SITools.py | 3 ++- NanoVNASaver/SweepWorker.py | 4 +--- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/NanoVNASaver/Calibration.py b/NanoVNASaver/Calibration.py index 36120c76..77605b37 100644 --- a/NanoVNASaver/Calibration.py +++ b/NanoVNASaver/Calibration.py @@ -14,15 +14,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import collections import logging import math import os +from typing import List +import numpy as np from PyQt5 import QtWidgets, QtCore -from typing import List + from .RFTools import Datapoint -import numpy as np logger = logging.getLogger(__name__) diff --git a/NanoVNASaver/Hardware.py b/NanoVNASaver/Hardware.py index 4cf20720..8ebd6a82 100644 --- a/NanoVNASaver/Hardware.py +++ b/NanoVNASaver/Hardware.py @@ -13,7 +13,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import collections import logging import re from time import sleep diff --git a/NanoVNASaver/Marker.py b/NanoVNASaver/Marker.py index 309461d2..7d4babed 100644 --- a/NanoVNASaver/Marker.py +++ b/NanoVNASaver/Marker.py @@ -13,7 +13,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import collections import math from typing import List diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py index 102cc9fa..25768526 100644 --- a/NanoVNASaver/SITools.py +++ b/NanoVNASaver/SITools.py @@ -21,6 +21,7 @@ PREFIXES = ("y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y") + class Format(NamedTuple): max_nr_digits: int = 6 fix_decimals: bool = False @@ -33,7 +34,7 @@ class Format(NamedTuple): parse_sloppy_kilo: bool = False -class Value(): +class Value: def __init__(self, value: Number = 0, unit: str = "", fmt=Format()): assert 3 <= fmt.max_nr_digits <= 27 assert -8 <= fmt.min_offset <= fmt.max_offset <= 8 diff --git a/NanoVNASaver/SweepWorker.py b/NanoVNASaver/SweepWorker.py index ad976229..f0a31228 100644 --- a/NanoVNASaver/SweepWorker.py +++ b/NanoVNASaver/SweepWorker.py @@ -13,7 +13,7 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import collections +import logging from time import sleep from typing import List @@ -22,8 +22,6 @@ from PyQt5.QtCore import pyqtSlot, pyqtSignal import NanoVNASaver -import logging - from NanoVNASaver.Calibration import Calibration from NanoVNASaver.Hardware import VNA, InvalidVNA from NanoVNASaver.RFTools import RFTools, Datapoint From ec8eb79168b7d7720f633e27ea44d212a038dd67 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Thu, 21 Nov 2019 13:17:34 +0100 Subject: [PATCH 54/84] Touchstone Class can now parses more lazy - float resistance value can be float now, but will be converted to int - comment lines can be placed everywhere - unordered frequencies will be sorted --- NanoVNASaver/Touchstone.py | 48 +++++++++++---- test/data/scikit_unordered.s2p | 104 +++++++++++++++++++++++++++++++++ test/test_touchstone.py | 13 ++++- 3 files changed, 153 insertions(+), 12 deletions(-) create mode 100644 test/data/scikit_unordered.s2p diff --git a/NanoVNASaver/Touchstone.py b/NanoVNASaver/Touchstone.py index 71158225..64bdf5ef 100644 --- a/NanoVNASaver/Touchstone.py +++ b/NanoVNASaver/Touchstone.py @@ -18,6 +18,7 @@ import math import cmath import io +from operator import attrgetter from NanoVNASaver.RFTools import Datapoint logger = logging.getLogger(__name__) @@ -77,12 +78,18 @@ def parse(self, line: str): self.format = p pformat = True elif p == "r" and not presist: - self.resistance = int(next(params)) + rstr = next(params) + try: + self.resistance = int(rstr) + except ValueError: + logger.warning("Non integer resistance value: %s", rstr) + self.resistance = int(float(rstr)) else: raise TypeError("Illegal option line: " + line) class Touchstone: + FIELD_ORDER = ("11", "21", "12", "22") def __init__(self, filename: str): self.filename = filename @@ -92,19 +99,22 @@ def __init__(self, filename: str): @property def s11data(self) -> list: - return self.sdata[0] + return self.s("11") @property - def s21data(self) -> list: - return self.sdata[1] + def s12data(self) -> list: + return self.s("12") @property - def s12data(self) -> list: - return self.sdata[2] + def s21data(self) -> list: + return self.s("21") @property def s22data(self) -> list: - return self.sdata[3] + return self.s("22") + + def s(self, name: str): + return self.sdata[Touchstone.FIELD_ORDER.index(name)] def _parse_comments(self, fp) -> str: for line in fp: @@ -136,8 +146,6 @@ def load(self): try: with open(self.filename) as infile: self.loads(infile.read()) - except TypeError as e: - logger.exception("Failed to parse %s: %s", self.filename, e) except IOError as e: logger.exception("Failed to open %s: %s", self.filename, e) @@ -145,6 +153,13 @@ def loads(self, s: str): """Parse touchstone 1.1 string input appends to existing sdata if Touchstone object exists """ + try: + self._loads(s) + except TypeError as e: + logger.exception("Failed to parse %s: %s", self.filename, e) + + def _loads(self, s: str): + need_reorder = False with io.StringIO(s) as file: opts_line = self._parse_comments(file) self.opts.parse(opts_line) @@ -152,8 +167,14 @@ def loads(self, s: str): prev_freq = 0.0 prev_len = 0 for line in file: + line = line.strip() # ignore empty lines (even if not specified) - if not line.strip(): + if line == "": + continue + # accept comment lines after header + if line.startswith("!"): + logger.warning("Comment after header: %s", line) + self.comments.append(line) continue # ignore comments at data end @@ -164,7 +185,8 @@ def loads(self, s: str): # consistency checks if freq <= prev_freq: - raise TypeError("Frequency not ascending: " + line) + logger.warning("Frequency not ascending: %s", line) + need_reorder = True prev_freq = freq if prev_len == 0: @@ -175,6 +197,10 @@ def loads(self, s: str): raise TypeError("Inconsistent number of pairs: " + line) self._append_line_data(freq, data) + if need_reorder: + logger.warning("Reordering data") + for datalist in self.sdata: + datalist.sort(key=attrgetter("freq")) def setFilename(self, filename: str): self.filename = filename diff --git a/test/data/scikit_unordered.s2p b/test/data/scikit_unordered.s2p new file mode 100644 index 00000000..57959f89 --- /dev/null +++ b/test/data/scikit_unordered.s2p @@ -0,0 +1,104 @@ +!Created with skrf (http://scikit-rf.org). +# Hz S RI R 50.0 +!freq ReS11 ImS11 ReS21 ImS21 ReS12 ImS12 ReS22 ImS22 +10000.0 0.997945666 -0.000203181 -2.0508e-05 -2.8303e-05 0.0 0.0 0.0 0.0 +15009900.0 0.998063921 0.000250377 -2.4179e-05 -1.7265e-05 0.0 0.0 0.0 0.0 +30009800.0 0.997875392 -0.000479933 -5.6028e-05 -1.4079e-05 0.0 0.0 0.0 0.0 +45009700.0 0.997830152 -0.000535284 -3.2141e-05 -2.2876e-05 0.0 0.0 0.0 0.0 +60009600.0 0.99804306 -0.001224545 1.5263e-05 1.9332e-05 0.0 0.0 0.0 0.0 +75009500.0 0.997991502 -0.000669958 1.6072e-05 -3.2965e-05 0.0 0.0 0.0 0.0 +90009400.0 0.997565209 -0.00161669 2.2068e-05 -2.0463e-05 0.0 0.0 0.0 0.0 +105009300.0 0.998001456 -0.001703771 0.000163192 0.000111491 0.0 0.0 0.0 0.0 +120009200.0 0.997912228 -0.002254315 3.4434e-05 -4.9813e-05 0.0 0.0 0.0 0.0 +135009100.0 0.997857272 -0.002554691 3.7397e-05 1.4562e-05 0.0 0.0 0.0 0.0 +150009000.0 0.998094379 -0.002690327 4.6955e-05 7.733e-05 0.0 0.0 0.0 0.0 +165008900.0 0.998075783 -0.003107617 -2.247e-05 6.7876e-05 0.0 0.0 0.0 0.0 +180008800.0 0.99791491 -0.00310471 5.7183e-05 6.5559e-05 0.0 0.0 0.0 0.0 +195008700.0 0.998371601 -0.003909578 2.1842e-05 4.178e-05 0.0 0.0 0.0 0.0 +210008600.0 0.997566342 -0.00470875 -8.3904e-05 4.7072e-05 0.0 0.0 0.0 0.0 +225008500.0 0.998071551 -0.004474181 -4.7161e-05 5.3963e-05 0.0 0.0 0.0 0.0 +240008400.0 0.997348725 -0.004639454 2.4776e-05 0.000107233 0.0 0.0 0.0 0.0 +255008300.0 0.99836862 -0.005756468 8.459e-06 -1.1133e-05 0.0 0.0 0.0 0.0 +270008200.0 0.99627453 -0.007404021 3.2551e-05 -3.3426e-05 0.0 0.0 0.0 0.0 +285008100.0 0.996389567 -0.006911531 7.9927e-05 -1.1805e-05 0.0 0.0 0.0 0.0 +300008000.0 0.984097003 -0.009154403 -0.000167907 0.001612333 0.0 0.0 0.0 0.0 +315007900.0 0.989603757 -0.003190903 9.3511e-05 -0.000254711 0.0 0.0 0.0 0.0 +330007800.0 0.990587413 -0.010738851 -2.6822e-05 -0.00020374 0.0 0.0 0.0 0.0 +345007700.0 0.992036402 -0.009486312 0.000147759 -0.000473813 0.0 0.0 0.0 0.0 +360007600.0 0.991972208 -0.006600862 0.000686447 0.00026418 0.0 0.0 0.0 0.0 +375007500.0 0.991577863 -0.008004215 -0.000124766 0.000645741 0.0 0.0 0.0 0.0 +390007400.0 0.996569514 -0.015393913 -6.3037e-05 0.000463298 0.0 0.0 0.0 0.0 +405007300.0 0.990179419 -0.01178324 -0.000387249 0.0005063 0.0 0.0 0.0 0.0 +420007200.0 0.992611587 -0.009658406 5.0784e-05 0.000525389 0.0 0.0 0.0 0.0 +435007100.0 0.994502842 -0.005429245 2.7807e-05 -0.000765338 0.0 0.0 0.0 0.0 +450007000.0 0.990648984 -0.011388606 0.000240157 -0.000835526 0.0 0.0 0.0 0.0 +465006900.0 0.990346252 -0.01049756 0.000115888 -9.6758e-05 0.0 0.0 0.0 0.0 +480006800.0 0.992519795 -0.009263195 0.000124362 0.000649891 0.0 0.0 0.0 0.0 +495006700.0 0.990916907 -0.010678514 -0.000208408 8.5402e-05 0.0 0.0 0.0 0.0 +510006600.0 0.990682482 -0.011856884 -8.8964e-05 0.000116707 0.0 0.0 0.0 0.0 +525006500.0 0.985233902 -0.010419548 -0.000585533 0.000515226 0.0 0.0 0.0 0.0 +540006400.0 0.995466768 -0.004720744 -0.000433898 -0.000461067 0.0 0.0 0.0 0.0 +555006300.0 0.991316795 -0.014557334 0.000266897 -0.000726262 0.0 0.0 0.0 0.0 +570006200.0 0.988762795 -0.011416017 0.000260244 -0.000191021 0.0 0.0 0.0 0.0 +585006100.0 0.993501067 -0.002398582 -9.156e-06 -0.000227457 0.0 0.0 0.0 0.0 +600006000.0 0.98844695 -0.018269769 0.000158602 0.000227051 0.0 0.0 0.0 0.0 +615005900.0 0.988067388 -0.014115263 0.000336775 -0.000126136 0.0 0.0 0.0 0.0 +630005800.0 0.998890399 -0.008659123 -0.00043222 -0.001015251 0.0 0.0 0.0 0.0 +645005700.0 0.990055859 -0.016014145 -0.001114644 -0.000278595 0.0 0.0 0.0 0.0 +660005600.0 0.990829944 -0.013342772 0.000793625 -0.000369359 0.0 0.0 0.0 0.0 +675005500.0 0.990757167 -0.01489226 0.000187729 -0.000386121 0.0 0.0 0.0 0.0 +690005400.0 0.992701351 -0.01520061 -0.000439427 0.000344602 0.0 0.0 0.0 0.0 +705005300.0 0.988954603 -0.015764433 -0.00073419 -0.000346192 0.0 0.0 0.0 0.0 +720005200.0 0.985275387 -0.02116522 0.000721478 -0.00026626 0.0 0.0 0.0 0.0 +735005100.0 0.988921165 -0.018076622 -0.000782531 -0.000888003 0.0 0.0 0.0 0.0 +750005000.0 0.997871518 -0.016042932 -0.00052584 -0.000241451 0.0 0.0 0.0 0.0 +765004900.0 0.991029024 -0.020595366 -5.3111e-05 4.5143e-05 0.0 0.0 0.0 0.0 +780004800.0 1.000474572 -0.014718874 0.000403745 0.000271099 0.0 0.0 0.0 0.0 +795004700.0 0.991873502 -0.017990043 -0.001036695 0.001309779 0.0 0.0 0.0 0.0 +810004600.0 1.001269459 -0.022523099 0.000904293 0.000838528 0.0 0.0 0.0 0.0 +825004500.0 0.995802938 -0.017430307 0.00096513 -0.001949188 0.0 0.0 0.0 0.0 +840004400.0 0.990553498 -0.022784827 0.000942723 -0.000526193 0.0 0.0 0.0 0.0 +855004300.0 0.992622613 -0.018191326 -0.000479641 0.001357597 0.0 0.0 0.0 0.0 +870004200.0 0.998473942 -0.0215917 0.000626809 -0.001388891 0.0 0.0 0.0 0.0 +885004100.0 0.991835117 -0.018580982 -4.4587e-05 0.000902399 0.0 0.0 0.0 0.0 +900004000.0 1.00122404 -0.018105072 -1.8327e-05 -0.002446518 0.0 0.0 0.0 0.0 +915003900.0 1.016865491 -0.026983406 0.001391073 0.001833796 0.0 0.0 0.0 0.0 +930003800.0 1.004885792 -0.017144799 0.002953082 0.004407539 0.0 0.0 0.0 0.0 +945003700.0 0.982209146 -0.019811017 -0.000444983 0.000584642 0.0 0.0 0.0 0.0 +960003600.0 1.001144766 -0.042434174 0.006346683 0.000898356 0.0 0.0 0.0 0.0 +975003500.0 0.991372644 -0.019519817 -0.004440182 -0.004724822 0.0 0.0 0.0 0.0 +990003400.0 1.014019727 -0.030354205 -0.006528417 -0.002219166 0.0 0.0 0.0 0.0 +1005003300.0 1.016555309 -0.039133321 0.001244856 -0.000631911 0.0 0.0 0.0 0.0 +1020003200.0 0.993585586 -0.027140568 -0.003237228 0.004080757 0.0 0.0 0.0 0.0 +1035003100.0 1.003273963 -0.018961952 0.003909113 -0.000963084 0.0 0.0 0.0 0.0 +1050003000.0 1.01273334 -0.062777623 0.003801429 0.004669127 0.0 0.0 0.0 0.0 +1065002900.0 1.005700349 -0.04565446 0.006958209 0.002412536 0.0 0.0 0.0 0.0 +1080002800.0 1.011170148 -0.042020332 -0.00354105 -0.001727906 0.0 0.0 0.0 0.0 +1095002700.0 1.001092314 -0.060577046 0.000117853 0.003003872 0.0 0.0 0.0 0.0 +1110002600.0 1.032729864 -0.057148627 -0.000723557 0.005098437 0.0 0.0 0.0 0.0 +1125002500.0 1.00830686 -0.062419198 0.0002536 0.007275069 0.0 0.0 0.0 0.0 +1140002400.0 1.004439353 -0.072881937 0.001709716 0.002529229 0.0 0.0 0.0 0.0 +1155002300.0 1.017983794 -0.08438082 -0.009064296 0.006464344 0.0 0.0 0.0 0.0 +1170002200.0 0.999595582 -0.030973853 0.000407358 -0.005522909 0.0 0.0 0.0 0.0 +1185002100.0 0.99132055 -0.071514025 -0.008105291 -0.000783289 0.0 0.0 0.0 0.0 +1200002000.0 0.991782605 -0.096986889 -0.014142063 0.001001663 0.0 0.0 0.0 0.0 +1215001900.0 0.976635456 -0.079574368 0.00104919 -0.006847375 0.0 0.0 0.0 0.0 +1230001800.0 1.02203989 -0.053924381 -0.013649494 0.000128238 0.0 0.0 0.0 0.0 +1245001700.0 0.996935963 -0.10235808 -0.002535079 -0.005486325 0.0 0.0 0.0 0.0 +1260001600.0 0.997368097 -0.110300377 -0.009988283 -0.001133779 0.0 0.0 0.0 0.0 +1275001500.0 0.969451427 -0.108227171 0.009824976 -0.004308064 0.0 0.0 0.0 0.0 +1290001400.0 1.005417943 -0.095492035 -0.003404723 -0.000240547 0.0 0.0 0.0 0.0 +1305001300.0 0.94099164 -0.097261205 -0.003829509 0.003687269 0.0 0.0 0.0 0.0 +1320001200.0 1.036064267 -0.109387516 -0.006938047 -0.006778006 0.0 0.0 0.0 0.0 +1335001100.0 0.982966244 -0.147038042 0.001936204 -0.004359447 0.0 0.0 0.0 0.0 +1350001000.0 0.943482697 -0.112556122 0.001491426 0.0052864 0.0 0.0 0.0 0.0 +1365000900.0 1.04384756 -0.175450965 -0.000125052 -0.006158494 0.0 0.0 0.0 0.0 +1380000800.0 1.08729124 -0.111472949 -0.002034726 -0.00124251 0.0 0.0 0.0 0.0 +1395000700.0 0.975226998 -0.151159316 0.015185886 0.003684978 0.0 0.0 0.0 0.0 +1410000600.0 1.006498575 -0.192365437 0.001370865 -0.002877861 0.0 0.0 0.0 0.0 +1425000500.0 0.972706973 -0.11881756 -0.003922176 0.004854394 0.0 0.0 0.0 0.0 +1440000400.0 0.986198782 -0.189747989 0.004291551 -0.005275334 0.0 0.0 0.0 0.0 +1455000300.0 0.913244247 -0.06428568 -0.014572597 0.003062129 0.0 0.0 0.0 0.0 +1470000200.0 0.869476377 -0.12276107 -0.0028179 -0.001525241 0.0 0.0 0.0 0.0 +1485000100.0 0.94566971 -0.199387297 0.006458432 0.018461674 0.0 0.0 0.0 0.0 +15000000.0 0.849810063 -0.4147357 -0.000306106 0.0041482 0.0 0.0 0.0 0.0 diff --git a/test/test_touchstone.py b/test/test_touchstone.py index 3ae07479..3747c952 100644 --- a/test/test_touchstone.py +++ b/test/test_touchstone.py @@ -14,12 +14,12 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import sys import unittest # Import targets to be tested from NanoVNASaver.Touchstone import Options, Touchstone + class TestTouchstoneOptions(unittest.TestCase): def setUp(self): self.opts = Options() @@ -52,6 +52,7 @@ def test_parse(self): self.assertEqual(str(self.opts), "# HZ Y RI R 123") self.assertEqual(self.opts.factor, 1) + class TestTouchstoneTouchstone(unittest.TestCase): def test_load(self): @@ -70,5 +71,15 @@ def test_load(self): self.assertEqual(len(ts.s22data), 1020) self.assertIn("! Vector Network Analyzer VNA R2", ts.comments) + def test_load_scikit(self): + ts = Touchstone("./test/data/scikit_unordered.s2p") + ts.load() + print(len(ts.s("12"))) + self.assertEqual(str(ts.opts), "# HZ S RI R 50") + self.assertEqual(len(ts.s11data), 101) + self.assertIn("!freq ReS11 ImS11 ReS21 ImS21 ReS12 ImS12 ReS22 ImS22", + ts.comments) + + if __name__ == '__main__': unittest.main(verbosity=2) From b6d041e999c778fbbbcd3c8f7817fdaa714563e9 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Thu, 21 Nov 2019 13:23:17 +0100 Subject: [PATCH 55/84] Added r as attribute to Touchstone --- NanoVNASaver/Touchstone.py | 6 +++++- test/test_touchstone.py | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/NanoVNASaver/Touchstone.py b/NanoVNASaver/Touchstone.py index 64bdf5ef..7a9f5f76 100644 --- a/NanoVNASaver/Touchstone.py +++ b/NanoVNASaver/Touchstone.py @@ -113,7 +113,11 @@ def s21data(self) -> list: def s22data(self) -> list: return self.s("22") - def s(self, name: str): + @property + def r(self) -> int: + return self.opts.resistance + + def s(self, name: str) -> list: return self.sdata[Touchstone.FIELD_ORDER.index(name)] def _parse_comments(self, fp) -> str: diff --git a/test/test_touchstone.py b/test/test_touchstone.py index 3747c952..2841d09c 100644 --- a/test/test_touchstone.py +++ b/test/test_touchstone.py @@ -61,6 +61,7 @@ def test_load(self): self.assertEqual(str(ts.opts), "# HZ S RI R 50") self.assertEqual(len(ts.s11data), 1010) self.assertEqual(len(ts.s21data), 0) + self.assertEqual(ts.r, 50) ts = Touchstone("./test/data/valid.s2p") ts.load() From 28222efbb8d9782331edda49f719b7b2d74315af Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Thu, 21 Nov 2019 13:25:21 +0100 Subject: [PATCH 56/84] added .vscode to .gitignore --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 9fb65b88..825f7196 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,11 @@ /venv/ .idea +.vscode /build/ /dist/ /nanovna-saver.spec *.egg-info/ *.pyc *.cal -*.s1p settings.json -.vscode .gitignore From 0e211fbb0b1f1548b1b8d2666d1d5710e5e40e79 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Thu, 21 Nov 2019 13:43:31 +0100 Subject: [PATCH 57/84] Tochstone.py removed unused setFilename method --- NanoVNASaver/Touchstone.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/NanoVNASaver/Touchstone.py b/NanoVNASaver/Touchstone.py index 7a9f5f76..255ab94e 100644 --- a/NanoVNASaver/Touchstone.py +++ b/NanoVNASaver/Touchstone.py @@ -205,6 +205,3 @@ def _loads(self, s: str): logger.warning("Reordering data") for datalist in self.sdata: datalist.sort(key=attrgetter("freq")) - - def setFilename(self, filename: str): - self.filename = filename From 91a8e3af2d4174f5b143e7540facaf0bb5d38d1b Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Thu, 21 Nov 2019 22:25:40 +0100 Subject: [PATCH 58/84] get more si precision with using Decimal --- NanoVNASaver/SITools.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py index 25768526..fc55c88f 100644 --- a/NanoVNASaver/SITools.py +++ b/NanoVNASaver/SITools.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import math +import decimal from typing import NamedTuple from numbers import Number @@ -35,32 +36,37 @@ class Format(NamedTuple): class Value: + CTX = decimal.Context(prec=60, Emin=-27, Emax=27) + def __init__(self, value: Number = 0, unit: str = "", fmt=Format()): - assert 3 <= fmt.max_nr_digits <= 27 + assert 3 <= fmt.max_nr_digits <= 30 assert -8 <= fmt.min_offset <= fmt.max_offset <= 8 - self.value = value + self._value = decimal.Decimal(value, context=Value.CTX) self._unit = unit self.fmt = fmt def __repr__(self) -> str: - return f"{self.__class__.__name__}({self.value}, '{self._unit}', {self.fmt})" + return (f"{self.__class__.__name__}({self.value}, " + f"'{self._unit}', {self.fmt})") def __str__(self) -> str: fmt = self.fmt - if fmt.assume_infinity and abs(self.value) >= 10 ** ((fmt.max_offset + 1) * 3): - return ("-" if self.value < 0 else "") + "\N{INFINITY}" + fmt.space_str + self._unit + if fmt.assume_infinity and \ + abs(self._value) >= 10 ** ((fmt.max_offset + 1) * 3): + return (("-" if self._value < 0 else "") + + "\N{INFINITY}" + fmt.space_str + self._unit) - if self.value == 0: + if self._value == 0: offset = 0 else: - offset = int(math.log10(abs(self.value)) // 3) + offset = int(math.log10(abs(self._value)) // 3) if offset < fmt.min_offset: offset = fmt.min_offset elif offset > fmt.max_offset: offset = fmt.max_offset - real = self.value / (10 ** (offset * 3)) + real = self._value / (10 ** (offset * 3)) if fmt.max_nr_digits < 4: formstr = ".0f" @@ -77,9 +83,13 @@ def __str__(self) -> str: if self.fmt.allow_strip and "." in result: result = result.rstrip("0").rstrip(".") - + return result + fmt.space_str + PREFIXES[offset + 8] + self._unit + @property + def value(self): + return float(self._value) + def parse(self, value: str) -> float: value = value.replace(" ", "") # Ignore spaces @@ -101,8 +111,8 @@ def parse(self, value: str) -> float: elif self.fmt.assume_infinity and value == "-\N{INFINITY}": self.value = -math.inf else: - self.value = float(value) * factor - return self.value + self.value = decimal.Decimal(value, context=Value.CTX) * factor + return float(self.value) @property def unit(self) -> str: From 8216f7d1a0642434599c4e37291332f1858b77ab Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Thu, 21 Nov 2019 22:50:52 +0100 Subject: [PATCH 59/84] Fixes --- NanoVNASaver/SITools.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py index fc55c88f..ac89f0da 100644 --- a/NanoVNASaver/SITools.py +++ b/NanoVNASaver/SITools.py @@ -107,12 +107,12 @@ def parse(self, value: str) -> float: value = value[:-1] if self.fmt.assume_infinity and value == "\N{INFINITY}": - self.value = math.inf + self._value = math.inf elif self.fmt.assume_infinity and value == "-\N{INFINITY}": - self.value = -math.inf + self._value = -math.inf else: - self.value = decimal.Decimal(value, context=Value.CTX) * factor - return float(self.value) + self._value = decimal.Decimal(value, context=Value.CTX) * factor + return float(self._value) @property def unit(self) -> str: From a5b10559f133e3fcae4dc6babb5fe6a27955499a Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Thu, 21 Nov 2019 22:56:40 +0100 Subject: [PATCH 60/84] fixes --- NanoVNASaver/SITools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py index ac89f0da..aab140d0 100644 --- a/NanoVNASaver/SITools.py +++ b/NanoVNASaver/SITools.py @@ -46,8 +46,8 @@ def __init__(self, value: Number = 0, unit: str = "", fmt=Format()): self.fmt = fmt def __repr__(self) -> str: - return (f"{self.__class__.__name__}({self.value}, " - f"'{self._unit}', {self.fmt})") + return (f"{self.__class__.__name__}(" + repr(self._value) + + f", '{self._unit}', {self.fmt})") def __str__(self) -> str: fmt = self.fmt From ba3f31e60b663c447694dfb6fd5c2416d1165590 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Fri, 22 Nov 2019 09:41:04 +0100 Subject: [PATCH 61/84] SITools: add min and max for parsing added parse_clamp_min and parse_clamp_min fixed tests for RFTools --- NanoVNASaver/SITools.py | 16 ++++++++++++++-- test/test_parseFrequency.py | 18 +++++++++++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py index aab140d0..12e86d4a 100644 --- a/NanoVNASaver/SITools.py +++ b/NanoVNASaver/SITools.py @@ -33,6 +33,8 @@ class Format(NamedTuple): allow_strip: bool = False parse_sloppy_unit: bool = False parse_sloppy_kilo: bool = False + parse_clamp_min: float = -math.inf + parse_clamp_max: float = math.inf class Value: @@ -66,7 +68,7 @@ def __str__(self) -> str: elif offset > fmt.max_offset: offset = fmt.max_offset - real = self._value / (10 ** (offset * 3)) + real = float(self._value) / (10 ** (offset * 3)) if fmt.max_nr_digits < 4: formstr = ".0f" @@ -111,7 +113,17 @@ def parse(self, value: str) -> float: elif self.fmt.assume_infinity and value == "-\N{INFINITY}": self._value = -math.inf else: - self._value = decimal.Decimal(value, context=Value.CTX) * factor + try: + self._value = (decimal.Decimal(value, context=Value.CTX) * + decimal.Decimal(factor, context=Value.CTX)) + except decimal.InvalidOperation: + raise ValueError + # TODO: get formating out of RFTools to be able to import clamp + # and reuse code + if self._value < self.fmt.parse_clamp_min: + self._value = self.fmt.parse_clamp_min + elif self._value > self.fmt.parse_clamp_max: + self._value = self.fmt.parse_clamp_max return float(self._value) @property diff --git a/test/test_parseFrequency.py b/test/test_parseFrequency.py index 8a2e6310..860b20be 100644 --- a/test/test_parseFrequency.py +++ b/test/test_parseFrequency.py @@ -1,4 +1,5 @@ -# NanoVNASaver - a python program to view and export Touchstone data from a NanoVNA +# NanoVNASaver +# A python program to view and export Touchstone data from a NanoVNA # Copyright (C) 2019. Rune B. Broberg # # This program is free software: you can redistribute it and/or modify @@ -14,13 +15,20 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import sys import unittest # Import targets to be tested -from NanoVNASaver import RFTools -rft = RFTools.RFTools() +from NanoVNASaver.RFTools import RFTools +rft = RFTools() + +# TODO: should be tested against SITools.Value +# RFTools.parseFrequency will hopefully go away in future +# and be specialised by input field and device, like +# parse_clamp_min=50000 for sweep input with +# a nanovna version 1 attached ... +# the hardware developer already announced a successor +# which will have different limits class TestCases(unittest.TestCase): def test_basicSIUnits(self): @@ -58,7 +66,7 @@ def test_unusualSIUnits(self): # Current behavior: unusual SI values that are legal, but inappropriate # for this application provide unexpected outputs. This behavior is # based on the FULL set of SI prefixes defined in SITools (below). - #PREFIXES = ("y", "z", "a", "f", "p", "n", "µ", "m", + # PREFIXES = ("y", "z", "a", "f", "p", "n", "µ", "m", # "", "k", "M", "G", "T", "P", "E", "Z", "Y") ####################################################################### self.assertEqual(rft.parseFrequency('123EHz'), 123000000000000000000) From 09b364f0f53fc1327c319124ee4b885f610ff401 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Fri, 22 Nov 2019 09:44:06 +0100 Subject: [PATCH 62/84] SITools: added a format assertion --- NanoVNASaver/SITools.py | 1 + 1 file changed, 1 insertion(+) diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py index 12e86d4a..b1fac914 100644 --- a/NanoVNASaver/SITools.py +++ b/NanoVNASaver/SITools.py @@ -43,6 +43,7 @@ class Value: def __init__(self, value: Number = 0, unit: str = "", fmt=Format()): assert 3 <= fmt.max_nr_digits <= 30 assert -8 <= fmt.min_offset <= fmt.max_offset <= 8 + assert fmt.parse_clamp_min < fmt.parse_clamp_max self._value = decimal.Decimal(value, context=Value.CTX) self._unit = unit self.fmt = fmt From d1696c594ff0594b2916a2462ed6cc77a684fe62 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Fri, 22 Nov 2019 10:05:03 +0100 Subject: [PATCH 63/84] SITools: Value now calls parser if input type is str --- NanoVNASaver/SITools.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py index b1fac914..edab569f 100644 --- a/NanoVNASaver/SITools.py +++ b/NanoVNASaver/SITools.py @@ -16,7 +16,7 @@ # along with this program. If not, see . import math import decimal -from typing import NamedTuple +from typing import NamedTuple, Union from numbers import Number PREFIXES = ("y", "z", "a", "f", "p", "n", "µ", "m", @@ -40,13 +40,20 @@ class Format(NamedTuple): class Value: CTX = decimal.Context(prec=60, Emin=-27, Emax=27) - def __init__(self, value: Number = 0, unit: str = "", fmt=Format()): + def __init__(self, + value: Union[Number, str] = 0, + unit: str = "", + fmt=Format()): assert 3 <= fmt.max_nr_digits <= 30 assert -8 <= fmt.min_offset <= fmt.max_offset <= 8 assert fmt.parse_clamp_min < fmt.parse_clamp_max - self._value = decimal.Decimal(value, context=Value.CTX) self._unit = unit self.fmt = fmt + if isinstance(value, str): + self._value = math.nan + self.parse(value) + else: + self._value = decimal.Decimal(value, context=Value.CTX) def __repr__(self) -> str: return (f"{self.__class__.__name__}(" + repr(self._value) + From e8c26a680eb825f52f8f65033accbf29492211cb Mon Sep 17 00:00:00 2001 From: Rune Broberg Date: Fri, 22 Nov 2019 13:54:14 +0100 Subject: [PATCH 64/84] Warn users the first time they try to use the manual calibration buttons --- NanoVNASaver/Calibration.py | 52 +++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/NanoVNASaver/Calibration.py b/NanoVNASaver/Calibration.py index 77605b37..cc4eb902 100644 --- a/NanoVNASaver/Calibration.py +++ b/NanoVNASaver/Calibration.py @@ -63,24 +63,24 @@ def __init__(self, app): calibration_control_group = QtWidgets.QGroupBox("Calibrate") calibration_control_layout = QtWidgets.QFormLayout(calibration_control_group) btn_cal_short = QtWidgets.QPushButton("Short") - btn_cal_short.clicked.connect(self.saveShort) + btn_cal_short.clicked.connect(self.manualSaveShort) self.cal_short_label = QtWidgets.QLabel("Uncalibrated") btn_cal_open = QtWidgets.QPushButton("Open") - btn_cal_open.clicked.connect(self.saveOpen) + btn_cal_open.clicked.connect(self.manualSaveOpen) self.cal_open_label = QtWidgets.QLabel("Uncalibrated") btn_cal_load = QtWidgets.QPushButton("Load") - btn_cal_load.clicked.connect(self.saveLoad) + btn_cal_load.clicked.connect(self.manualSaveLoad) self.cal_load_label = QtWidgets.QLabel("Uncalibrated") btn_cal_through = QtWidgets.QPushButton("Through") - btn_cal_through.clicked.connect(self.saveThrough) + btn_cal_through.clicked.connect(self.manualSaveThrough) # btn_cal_through.setDisabled(True) self.cal_through_label = QtWidgets.QLabel("Uncalibrated") btn_cal_isolation = QtWidgets.QPushButton("Isolation") - btn_cal_isolation.clicked.connect(self.saveIsolation) + btn_cal_isolation.clicked.connect(self.manualSaveIsolation) # btn_cal_isolation.setDisabled(True) self.cal_isolation_label = QtWidgets.QLabel("Uncalibrated") @@ -222,22 +222,64 @@ def __init__(self, app): cal_standard_layout.addWidget(self.cal_standard_save_box) right_layout.addWidget(cal_standard_box) + def checkExpertUser(self): + if not self.app.settings.value("ExpertCalibrationUser", False, bool): + response = QtWidgets.QMessageBox.question(self, "Are you sure?", "Use of the manual calibration buttons " + + "is non-intuitive, and primarily suited for users with very " + + "specialized needs. The buttons do not sweep for you, nor do " + + "they interact with the NanoVNA calibration.\n\n" + + "If you are trying to do a calibration of the NanoVNA, do so " + + "on the device itself instead. If you are trying to do a " + + "calibration with NanoVNA-Saver, use the Calibration Assistant " + + "if possible.\n\n" + + "If you are certain you know what you are doing, click Yes.", + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel, + QtWidgets.QMessageBox.Cancel) + + if response == QtWidgets.QMessageBox.Yes: + self.app.settings.setValue("ExpertCalibrationUser", True) + return True + else: + return False + else: + return True + + def manualSaveShort(self): + if self.checkExpertUser(): + self.saveShort() + def saveShort(self): self.app.calibration.s11short = self.app.data self.cal_short_label.setText("Data set (" + str(len(self.app.calibration.s11short)) + " points)") + def manualSaveOpen(self): + if self.checkExpertUser(): + self.saveOpen() + def saveOpen(self): self.app.calibration.s11open = self.app.data self.cal_open_label.setText("Data set (" + str(len(self.app.calibration.s11open)) + " points)") + def manualSaveLoad(self): + if self.checkExpertUser(): + self.saveLoad() + def saveLoad(self): self.app.calibration.s11load = self.app.data self.cal_load_label.setText("Data set (" + str(len(self.app.calibration.s11load)) + " points)") + def manualSaveIsolation(self): + if self.checkExpertUser(): + self.saveIsolation() + def saveIsolation(self): self.app.calibration.s21isolation = self.app.data21 self.cal_isolation_label.setText("Data set (" + str(len(self.app.calibration.s21isolation)) + " points)") + def manualSaveThrough(self): + if self.checkExpertUser(): + self.saveThrough() + def saveThrough(self): self.app.calibration.s21through = self.app.data21 self.cal_through_label.setText("Data set (" + str(len(self.app.calibration.s21through)) + " points)") From 2a67e1cc415eaa795ddcf921f7d6e1a4d5eff375 Mon Sep 17 00:00:00 2001 From: Rune Broberg Date: Fri, 22 Nov 2019 14:07:38 +0100 Subject: [PATCH 65/84] Make sure fixed/automatic menu options behave. Cleanup. --- NanoVNASaver/Chart.py | 134 +++++++++++++++++++++++++++++------------- 1 file changed, 92 insertions(+), 42 deletions(-) diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index 9e90e9a4..5d77976b 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -417,6 +417,16 @@ def contextMenuEvent(self, event): self.action_set_fixed_minimum.setText("Minimum (" + str(self.minDisplayValue) + ")") self.action_set_fixed_maximum.setText("Maximum (" + str(self.maxDisplayValue) + ")") + if self.fixedSpan: + self.action_fixed_span.setChecked(True) + else: + self.action_automatic.setChecked(True) + + if self.fixedValues: + self.y_action_fixed_span.setChecked(True) + else: + self.y_action_automatic.setChecked(True) + self.menu.exec_(event.globalPos()) def setFixedSpan(self, fixed_span: bool): @@ -489,6 +499,7 @@ def resetDisplayLimits(self): self.fixedSpan = False self.action_automatic.setChecked(True) self.logarithmicX = False + self.action_set_linear_x.setChecked(True) self.update() def getXPosition(self, d: Datapoint) -> int: @@ -646,7 +657,8 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: self.drawValues(qp) if len(self.data) > 0\ and (self.data[0].freq > self.fstop or self.data[len(self.data)-1].freq < self.fstart) \ - and (len(self.reference) == 0 or self.reference[0].freq > self.fstop or self.reference[len(self.reference)-1].freq < self.fstart): + and (len(self.reference) == 0 or self.reference[0].freq > self.fstop or + self.reference[len(self.reference)-1].freq < self.fstart): # Data outside frequency range qp.setBackgroundMode(QtCore.Qt.OpaqueMode) qp.setBackground(self.backgroundColor) @@ -842,8 +854,10 @@ def __init__(self, name=""): self.minDisplayValue = -180 self.maxDisplayValue = 180 - self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, self.chartHeight + self.topMargin + self.bottomMargin) - self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)) + self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, + self.chartHeight + self.topMargin + self.bottomMargin) + self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, + QtWidgets.QSizePolicy.MinimumExpanding)) pal = QtGui.QPalette() pal.setColor(QtGui.QPalette.Background, self.backgroundColor) self.setPalette(pal) @@ -869,8 +883,9 @@ def drawChart(self, qp: QtGui.QPainter): qp.setPen(QtGui.QPen(self.textColor)) qp.drawText(3, 15, self.name) qp.setPen(QtGui.QPen(self.foregroundColor)) - qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.chartHeight+5) - qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) + qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin + self.chartHeight + 5) + qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, + self.leftMargin + self.chartWidth, self.topMargin + self.chartHeight) def drawValues(self, qp: QtGui.QPainter): if len(self.data) == 0 and len(self.reference) == 0: @@ -1323,24 +1338,38 @@ def drawSmithChart(self, qp: QtGui.QPainter): qp.drawEllipse(QtCore.QPoint(centerX, centerY), int(self.chartWidth/2), int(self.chartHeight/2)) qp.drawLine(centerX - int(self.chartWidth/2), centerY, centerX + int(self.chartWidth/2), centerY) - qp.drawEllipse(QtCore.QPoint(centerX + int(self.chartWidth/4), centerY), int(self.chartWidth/4), int(self.chartHeight/4)) # Re(Z) = 1 - qp.drawEllipse(QtCore.QPoint(centerX + int(2/3*self.chartWidth/2), centerY), int(self.chartWidth/6), int(self.chartHeight/6)) # Re(Z) = 2 - qp.drawEllipse(QtCore.QPoint(centerX + int(3 / 4 * self.chartWidth / 2), centerY), int(self.chartWidth / 8), int(self.chartHeight / 8)) # Re(Z) = 3 - qp.drawEllipse(QtCore.QPoint(centerX + int(5 / 6 * self.chartWidth / 2), centerY), int(self.chartWidth / 12), int(self.chartHeight / 12)) # Re(Z) = 5 - - qp.drawEllipse(QtCore.QPoint(centerX + int(1 / 3 * self.chartWidth / 2), centerY), int(self.chartWidth / 3), int(self.chartHeight / 3)) # Re(Z) = 0.5 - qp.drawEllipse(QtCore.QPoint(centerX + int(1 / 6 * self.chartWidth / 2), centerY), int(self.chartWidth / 2.4), int(self.chartHeight / 2.4)) # Re(Z) = 0.2 - - qp.drawArc(centerX + int(3/8*self.chartWidth), centerY, int(self.chartWidth/4), int(self.chartWidth/4), 90*16, 152*16) # Im(Z) = -5 - qp.drawArc(centerX + int(3/8*self.chartWidth), centerY, int(self.chartWidth/4), -int(self.chartWidth/4), -90 * 16, -152 * 16) # Im(Z) = 5 - qp.drawArc(centerX + int(self.chartWidth/4), centerY, int(self.chartWidth/2), int(self.chartHeight/2), 90*16, 127*16) # Im(Z) = -2 - qp.drawArc(centerX + int(self.chartWidth/4), centerY, int(self.chartWidth/2), -int(self.chartHeight/2), -90*16, -127*16) # Im(Z) = 2 + qp.drawEllipse(QtCore.QPoint(centerX + int(self.chartWidth/4), centerY), + int(self.chartWidth/4), int(self.chartHeight/4)) # Re(Z) = 1 + qp.drawEllipse(QtCore.QPoint(centerX + int(2/3*self.chartWidth/2), centerY), + int(self.chartWidth/6), int(self.chartHeight/6)) # Re(Z) = 2 + qp.drawEllipse(QtCore.QPoint(centerX + int(3 / 4 * self.chartWidth / 2), centerY), + int(self.chartWidth / 8), int(self.chartHeight / 8)) # Re(Z) = 3 + qp.drawEllipse(QtCore.QPoint(centerX + int(5 / 6 * self.chartWidth / 2), centerY), + int(self.chartWidth / 12), int(self.chartHeight / 12)) # Re(Z) = 5 + + qp.drawEllipse(QtCore.QPoint(centerX + int(1 / 3 * self.chartWidth / 2), centerY), + int(self.chartWidth / 3), int(self.chartHeight / 3)) # Re(Z) = 0.5 + qp.drawEllipse(QtCore.QPoint(centerX + int(1 / 6 * self.chartWidth / 2), centerY), + int(self.chartWidth / 2.4), int(self.chartHeight / 2.4)) # Re(Z) = 0.2 + + qp.drawArc(centerX + int(3/8*self.chartWidth), centerY, int(self.chartWidth/4), + int(self.chartWidth/4), 90*16, 152*16) # Im(Z) = -5 + qp.drawArc(centerX + int(3/8*self.chartWidth), centerY, int(self.chartWidth/4), + -int(self.chartWidth/4), -90 * 16, -152 * 16) # Im(Z) = 5 + qp.drawArc(centerX + int(self.chartWidth/4), centerY, int(self.chartWidth/2), + int(self.chartHeight/2), 90*16, 127*16) # Im(Z) = -2 + qp.drawArc(centerX + int(self.chartWidth/4), centerY, int(self.chartWidth/2), + -int(self.chartHeight/2), -90*16, -127*16) # Im(Z) = 2 qp.drawArc(centerX, centerY, self.chartWidth, self.chartHeight, 90*16, 90*16) # Im(Z) = -1 qp.drawArc(centerX, centerY, self.chartWidth, -self.chartHeight, -90 * 16, -90 * 16) # Im(Z) = 1 - qp.drawArc(centerX - int(self.chartWidth/2), centerY, self.chartWidth*2, self.chartHeight*2, int(99.5*16), int(43.5*16)) # Im(Z) = -0.5 - qp.drawArc(centerX - int(self.chartWidth/2), centerY, self.chartWidth*2, -self.chartHeight*2, int(-99.5 * 16), int(-43.5 * 16)) # Im(Z) = 0.5 - qp.drawArc(centerX - self.chartWidth*2, centerY, self.chartWidth*5, self.chartHeight*5, int(93.85*16), int(18.85*16)) # Im(Z) = -0.2 - qp.drawArc(centerX - self.chartWidth*2, centerY, self.chartWidth*5, -self.chartHeight*5, int(-93.85 * 16), int(-18.85 * 16)) # Im(Z) = 0.2 + qp.drawArc(centerX - int(self.chartWidth/2), centerY, + self.chartWidth*2, self.chartHeight*2, int(99.5*16), int(43.5*16)) # Im(Z) = -0.5 + qp.drawArc(centerX - int(self.chartWidth/2), centerY, + self.chartWidth*2, -self.chartHeight*2, int(-99.5 * 16), int(-43.5 * 16)) # Im(Z) = 0.5 + qp.drawArc(centerX - self.chartWidth*2, centerY, + self.chartWidth*5, self.chartHeight*5, int(93.85*16), int(18.85*16)) # Im(Z) = -0.2 + qp.drawArc(centerX - self.chartWidth*2, centerY, + self.chartWidth*5, -self.chartHeight*5, int(-93.85 * 16), int(-18.85 * 16)) # Im(Z) = 0.2 qp.setPen(self.swrColor) for swr in self.swrMarkers: @@ -1456,8 +1485,10 @@ def __init__(self, name=""): self.isInverted = False - self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, self.chartHeight + self.topMargin + self.bottomMargin) - self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)) + self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, + self.chartHeight + self.topMargin + self.bottomMargin) + self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, + QtWidgets.QSizePolicy.MinimumExpanding)) pal = QtGui.QPalette() pal.setColor(QtGui.QPalette.Background, self.backgroundColor) self.setPalette(pal) @@ -1468,7 +1499,8 @@ def drawChart(self, qp: QtGui.QPainter): qp.drawText(3, 15, self.name + " (dB)") qp.setPen(QtGui.QPen(self.foregroundColor)) qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.chartHeight+5) - qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) + qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, + self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) def drawValues(self, qp: QtGui.QPainter): if len(self.data) == 0 and len(self.reference) == 0: @@ -1650,8 +1682,10 @@ def __init__(self, name=""): self.isInverted = False - self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, self.chartHeight + self.topMargin + self.bottomMargin) - self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)) + self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, + self.chartHeight + self.topMargin + self.bottomMargin) + self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, + QtWidgets.QSizePolicy.MinimumExpanding)) pal = QtGui.QPalette() pal.setColor(QtGui.QPalette.Background, self.backgroundColor) self.setPalette(pal) @@ -1664,7 +1698,8 @@ def drawChart(self, qp: QtGui.QPainter): qp.drawText(self.leftMargin + self.chartWidth - 15, 15, "Imag") qp.setPen(QtGui.QPen(self.foregroundColor)) qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.chartHeight+5) - qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) + qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, + self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) def drawValues(self, qp: QtGui.QPainter): if len(self.data) == 0 and len(self.reference) == 0: @@ -1803,8 +1838,10 @@ def __init__(self, name=""): self.isInverted = False - self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, self.chartHeight + self.topMargin + self.bottomMargin) - self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)) + self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, + self.chartHeight + self.topMargin + self.bottomMargin) + self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, + QtWidgets.QSizePolicy.MinimumExpanding)) pal = QtGui.QPalette() pal.setColor(QtGui.QPalette.Background, self.backgroundColor) self.setPalette(pal) @@ -1832,7 +1869,8 @@ def drawChart(self, qp: QtGui.QPainter): qp.drawText(self.leftMargin + self.chartWidth - 8, 15, "S21") qp.setPen(QtGui.QPen(self.foregroundColor)) qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.chartHeight+5) - qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) + qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, + self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) def drawValues(self, qp: QtGui.QPainter): if len(self.data11) == 0 and len(self.reference11) == 0: @@ -2936,8 +2974,10 @@ def __init__(self, name=""): self.maxValue = 1 self.span = 1 - self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, self.chartHeight + self.topMargin + self.bottomMargin) - self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)) + self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, + self.chartHeight + self.topMargin + self.bottomMargin) + self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, + QtWidgets.QSizePolicy.MinimumExpanding)) pal = QtGui.QPalette() pal.setColor(QtGui.QPalette.Background, self.backgroundColor) self.setPalette(pal) @@ -2948,7 +2988,8 @@ def drawChart(self, qp: QtGui.QPainter): qp.drawText(3, 15, self.name) qp.setPen(QtGui.QPen(self.foregroundColor)) qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.chartHeight+5) - qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) + qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, + self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) def drawValues(self, qp: QtGui.QPainter): if len(self.data) == 0 and len(self.reference) == 0: @@ -3079,8 +3120,10 @@ def __init__(self, name=""): self.maxValue = 1 self.span = 1 - self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, self.chartHeight + self.topMargin + self.bottomMargin) - self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)) + self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, + self.chartHeight + self.topMargin + self.bottomMargin) + self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, + QtWidgets.QSizePolicy.MinimumExpanding)) pal = QtGui.QPalette() pal.setColor(QtGui.QPalette.Background, self.backgroundColor) self.setPalette(pal) @@ -3091,7 +3134,8 @@ def drawChart(self, qp: QtGui.QPainter): qp.drawText(3, 15, self.name) qp.setPen(QtGui.QPen(self.foregroundColor)) qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.chartHeight+5) - qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) + qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, + self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) def drawValues(self, qp: QtGui.QPainter): if len(self.data) == 0 and len(self.reference) == 0: @@ -3735,8 +3779,10 @@ def __init__(self, name=""): self.maxValue = 1 self.span = 1 - self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, self.chartHeight + self.topMargin + self.bottomMargin) - self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)) + self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, + self.chartHeight + self.topMargin + self.bottomMargin) + self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, + QtWidgets.QSizePolicy.MinimumExpanding)) pal = QtGui.QPalette() pal.setColor(QtGui.QPalette.Background, self.backgroundColor) self.setPalette(pal) @@ -3747,7 +3793,8 @@ def drawChart(self, qp: QtGui.QPainter): qp.drawText(3, 15, self.name + " (F)") qp.setPen(QtGui.QPen(self.foregroundColor)) qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.chartHeight+5) - qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) + qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, + self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) def drawValues(self, qp: QtGui.QPainter): if len(self.data) == 0 and len(self.reference) == 0: @@ -3858,8 +3905,10 @@ def __init__(self, name=""): self.maxValue = 1 self.span = 1 - self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, self.chartHeight + self.topMargin + self.bottomMargin) - self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)) + self.setMinimumSize(self.chartWidth + self.rightMargin + self.leftMargin, + self.chartHeight + self.topMargin + self.bottomMargin) + self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, + QtWidgets.QSizePolicy.MinimumExpanding)) pal = QtGui.QPalette() pal.setColor(QtGui.QPalette.Background, self.backgroundColor) self.setPalette(pal) @@ -3870,7 +3919,8 @@ def drawChart(self, qp: QtGui.QPainter): qp.drawText(3, 15, self.name + " (H)") qp.setPen(QtGui.QPen(self.foregroundColor)) qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.chartHeight+5) - qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) + qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, + self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) def drawValues(self, qp: QtGui.QPainter): if len(self.data) == 0 and len(self.reference) == 0: From b2f2e4c208c7792ec6c94d05c0123de530249617 Mon Sep 17 00:00:00 2001 From: Rune Broberg Date: Fri, 22 Nov 2019 14:13:39 +0100 Subject: [PATCH 66/84] When zooming using the scroll wheel: Hold Shift to zoom only values, or Control to zoom only frequencies. --- NanoVNASaver/Chart.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index 5d77976b..8506bcf3 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -514,7 +514,7 @@ def getXPosition(self, d: Datapoint) -> int: else: return math.floor(self.width()/2) - def frequencyAtPosition(self, x, limit = True) -> int: + def frequencyAtPosition(self, x, limit=True) -> int: """ Calculates the frequency at a given X-position :param limit: Determines whether frequencies outside the currently displayed span can be returned. @@ -553,14 +553,25 @@ def wheelEvent(self, a0: QtGui.QWheelEvent) -> None: if len(self.data) == 0 and len(self.reference) == 0: a0.ignore() return + do_zoom_x = do_zoom_y = True + if a0.modifiers() == QtCore.Qt.ShiftModifier: + do_zoom_x = False + if a0.modifiers() == QtCore.Qt.ControlModifier: + do_zoom_y = False if a0.angleDelta().y() > 0: # Zoom in a0.accept() # Center of zoom = a0.x(), a0.y() # We zoom in by 1/10 of the width/height. rate = a0.angleDelta().y() / 120 - zoomx = rate * self.chartWidth / 10 - zoomy = rate * self.chartHeight / 10 + if do_zoom_x: + zoomx = rate * self.chartWidth / 10 + else: + zoomx = 0 + if do_zoom_y: + zoomy = rate * self.chartHeight / 10 + else: + zoomy = 0 absx = max(0, a0.x() - self.leftMargin) absy = max(0, a0.y() - self.topMargin) ratiox = absx/self.chartWidth @@ -576,8 +587,14 @@ def wheelEvent(self, a0: QtGui.QWheelEvent) -> None: # Center of zoom = a0.x(), a0.y() # We zoom out by 1/9 of the width/height, to match zoom in. rate = -a0.angleDelta().y() / 120 - zoomx = rate * self.chartWidth / 9 - zoomy = rate * self.chartHeight / 9 + if do_zoom_x: + zoomx = rate * self.chartWidth / 9 + else: + zoomx = 0 + if do_zoom_y: + zoomy = rate * self.chartHeight / 9 + else: + zoomy = 0 absx = max(0, a0.x() - self.leftMargin) absy = max(0, a0.y() - self.topMargin) ratiox = absx/self.chartWidth From bab15a73d09f095ae36bb8074930edb6c613f26b Mon Sep 17 00:00:00 2001 From: "Rune B. Broberg" Date: Fri, 22 Nov 2019 16:41:30 +0100 Subject: [PATCH 67/84] Fix for updating Y scale zoom (Thanks Harry!), cleanup --- NanoVNASaver/Chart.py | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index 8506bcf3..e6f2e82b 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -1409,11 +1409,11 @@ def drawValues(self, qp: QtGui.QPainter): qp.setPen(pen) for i in range(len(self.data)): x = self.getXPosition(self.data[i]) - y = self.height()/2 + self.data[i].im * -1 * self.chartHeight/2 - qp.drawPoint(int(x), int(y)) + y = int(self.height()/2 + self.data[i].im * -1 * self.chartHeight/2) + qp.drawPoint(x, y) if self.drawLines and i > 0: prevx = self.getXPosition(self.data[i-1]) - prevy = self.height() / 2 + self.data[i-1].im * -1 * self.chartHeight / 2 + prevy = int(self.height() / 2 + self.data[i-1].im * -1 * self.chartHeight / 2) qp.setPen(line_pen) qp.drawLine(x, y, prevx, prevy) qp.setPen(pen) @@ -1431,11 +1431,11 @@ def drawValues(self, qp: QtGui.QPainter): if data.freq < fstart or data.freq > fstop: continue x = self.getXPosition(data) - y = self.height()/2 + data.im * -1 * self.chartHeight/2 - qp.drawPoint(int(x), int(y)) + y = int(self.height()/2 + data.im * -1 * self.chartHeight/2) + qp.drawPoint(x, y) if self.drawLines and i > 0: prevx = self.getXPosition(self.reference[i-1]) - prevy = self.height() / 2 + self.reference[i-1].im * -1 * self.chartHeight / 2 + prevy = int(self.height() / 2 + self.reference[i-1].im * -1 * self.chartHeight / 2) qp.setPen(line_pen) qp.drawLine(x, y, prevx, prevy) qp.setPen(pen) @@ -1447,10 +1447,10 @@ def drawValues(self, qp: QtGui.QPainter): self.drawMarker(x, y, qp, m.color, self.markers.index(m)+1) def getXPosition(self, d: Datapoint) -> int: - return self.width()/2 + d.re * self.chartWidth/2 + return int(self.width()/2 + d.re * self.chartWidth/2) def getYPosition(self, d: Datapoint) -> int: - return self.height()/2 + d.im * -1 * self.chartHeight/2 + return int(self.height()/2 + d.im * -1 * self.chartHeight/2) def heightForWidth(self, a0: int) -> int: return a0 @@ -2362,24 +2362,26 @@ def setFixedValues(self, fixed_values): def setMinimumImpedance(self): min_val, selected = QtWidgets.QInputDialog.getDouble(self, "Minimum impedance (\N{OHM SIGN})", - "Set minimum impedance (\N{OHM SIGN})", value=self.minDisplayLength, + "Set minimum impedance (\N{OHM SIGN})", + value=self.minDisplayLength, min=0, decimals=1) if not selected: return if not (self.fixedValues and min_val >= self.maxImpedance): self.minImpedance = min_val - if self.fixedSpan: + if self.fixedValues: self.update() def setMaximumImpedance(self): max_val, selected = QtWidgets.QInputDialog.getDouble(self, "Maximum impedance (\N{OHM SIGN})", - "Set maximum impedance (\N{OHM SIGN})", value=self.minDisplayLength, + "Set maximum impedance (\N{OHM SIGN})", + value=self.minDisplayLength, min=0, decimals=1) if not selected: return if not (self.fixedValues and max_val <= self.minImpedance): self.maxImpedance = max_val - if self.fixedSpan: + if self.fixedValues: self.update() def copy(self): @@ -2460,7 +2462,11 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: qp.drawLine(x, self.topMargin, x, self.topMargin + height) qp.setPen(QtGui.QPen(self.textColor)) qp.drawText(x - 15, self.topMargin + height + 15, - str(round(self.tdrWindow.distance_axis[min_index + int((x - self.leftMargin) * x_step) - 1]/2, 1)) + "m") + str(round(self.tdrWindow.distance_axis[min_index + + int((x - self.leftMargin) * x_step) - 1]/2, + 1) + ) + + "m") qp.setPen(QtGui.QPen(self.textColor)) qp.drawText(self.leftMargin - 10, self.topMargin + height + 15, @@ -2494,7 +2500,8 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: qp.drawPoint(x, y) x = self.leftMargin + int((i - min_index) / x_step) - y = (self.topMargin + height) - int((self.tdrWindow.step_response_Z[i]-min_impedance) / y_impedance_step) + y = (self.topMargin + height) -\ + int((self.tdrWindow.step_response_Z[i]-min_impedance) / y_impedance_step) if self.isPlotable(x, y): pen.setColor(self.secondarySweepColor) qp.setPen(pen) @@ -3694,7 +3701,8 @@ def drawChart(self, qp: QtGui.QPainter): qp.drawText(3, 15, self.name + " (ns)") qp.setPen(QtGui.QPen(self.foregroundColor)) qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.chartHeight+5) - qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) + qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, + self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) def drawValues(self, qp: QtGui.QPainter): if len(self.data) == 0 and len(self.reference) == 0: From 0d99293e9859cd004be6a50a6c2bb661075f7392 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Sat, 23 Nov 2019 11:45:34 +0100 Subject: [PATCH 68/84] Code cleanups moved groupDelay out of unneeded RFTools class splited some Marker code to functions --- NanoVNASaver/Marker.py | 204 ++++++++++++++++++++-------------------- NanoVNASaver/RFTools.py | 59 +++++++----- 2 files changed, 142 insertions(+), 121 deletions(-) diff --git a/NanoVNASaver/Marker.py b/NanoVNASaver/Marker.py index 7d4babed..0e20cb74 100644 --- a/NanoVNASaver/Marker.py +++ b/NanoVNASaver/Marker.py @@ -1,4 +1,5 @@ -# NanoVNASaver - a python program to view and export Touchstone data from a NanoVNA +# NanoVNASaver +# A python program to view and export Touchstone data from a NanoVNA # Copyright (C) 2019. Rune B. Broberg # # This program is free software: you can redistribute it and/or modify @@ -20,7 +21,15 @@ from PyQt5.QtCore import pyqtSignal from NanoVNASaver import SITools -from NanoVNASaver.RFTools import Datapoint, RFTools +from NanoVNASaver.RFTools import Datapoint, RFTools, groupDelay + +FMT_Q_FACTOR = SITools.Format(max_nr_digits=4, assume_infinity=False, + min_offset=0, max_offset=0, allow_strip=True) + +def format_q_factor(val: float) -> str: + if 0 > val or val > 10000.0: + return "\N{INFINITY}" + return str(SITools.Value(val, fmt=FMT_Q_FACTOR)) class Marker(QtCore.QObject): @@ -73,7 +82,6 @@ def __init__(self, name, initialColor, frequency=""): self.frequency_label.setMinimumWidth(100) self.impedance_label = QtWidgets.QLabel("") self.admittance_label = QtWidgets.QLabel("") - # self.admittance_label = QtWidgets.QLabel("") self.parallel_r_label = QtWidgets.QLabel("") self.parallel_x_label = QtWidgets.QLabel("") self.parallel_c_label = QtWidgets.QLabel("") @@ -231,8 +239,11 @@ def setColor(self, color): if self.coloredText: color_string = QtCore.QVariant(color) color_string.convert(QtCore.QVariant.String) - self.group_box.setStyleSheet('QGroupBox { color: ' + color_string.value() + '; font-size: ' + \ - str(self.group_box.font().pointSize()) + '};') + self.group_box.setStyleSheet( + 'QGroupBox { color: ' + color_string.value() + + '; font-size: ' + str(self.group_box.font().pointSize()) + + '};' + ) else: self.group_box.setStyleSheet('QGroupBox { font-size: ' + str(self.group_box.font().pointSize()) + '};') @@ -304,103 +315,96 @@ def resetLabels(self): self.quality_factor_label.setText("") def updateLabels(self, s11data: List[Datapoint], s21data: List[Datapoint]): - if self.location != -1: - imp = s11data[self.location].impedance() - re50, im50 = imp.real, imp.imag - vswr = s11data[self.location].vswr - if re50 > 0: - rp = (re50 ** 2 + im50 ** 2) / re50 - rp = round(rp, 3 - max(0, math.floor(math.log10(abs(rp))))) - if rp > 10000: - rpstr = str(round(rp/1000, 2)) + "k" - elif rp > 1000: - rpstr = str(round(rp)) - else: - rpstr = str(rp) - - re50 = round(re50, 3 - max(0, math.floor(math.log10(abs(re50))))) - if re50 > 10000: - re50str = str(round(re50/1000, 2)) + "k" - elif re50 > 1000: - re50str = str(round(re50)) # Remove the ".0" - else: - re50str = str(re50) - else: - rpstr = "-" - re50 = 0 - re50str = "-" - - if im50 != 0: - xp = (re50 ** 2 + im50 ** 2) / im50 - xp = round(xp, 3 - max(0, math.floor(math.log10(abs(xp))))) - xpcstr = RFTools.capacitanceEquivalent(xp, s11data[self.location].freq) - xplstr = RFTools.inductanceEquivalent(xp, s11data[self.location].freq) - if xp < 0: - xpstr = xpcstr - xp50str = " -j" + str(-1 * xp) - else: - xpstr = xplstr - xp50str = " +j" + str(xp) - xp50str += " \N{OHM SIGN}" + if self.location == -1: + return + s11 = s11data[self.location] + if s21data: + s21 = s21data[self.location] + imp = s11.impedance() + re50, im50 = imp.real, imp.imag + vswr = s11.vswr + if re50 > 0: + rp = (re50 ** 2 + im50 ** 2) / re50 + rp = round(rp, 3 - max(0, math.floor(math.log10(abs(rp))))) + if rp > 10000: + rpstr = str(round(rp/1000, 2)) + "k" + elif rp > 1000: + rpstr = str(round(rp)) else: - xp50str = " +j ? \N{OHM SIGN}" - xpstr = xpcstr = xplstr = "-" - - if im50 != 0: - im50 = round(im50, 3 - max(0, math.floor(math.log10(abs(im50))))) + rpstr = str(rp) - if im50 < 0: - im50str = " -j" + str(-1 * im50) - else: - im50str = " +j" + str(im50) - im50str += " \N{OHM SIGN}" - - self.frequency_label.setText(RFTools.formatFrequency(s11data[self.location].freq)) - self.impedance_label.setText(re50str + im50str) - self.admittance_label.setText(rpstr + xp50str) - self.series_r_label.setText(re50str + " \N{OHM SIGN}") - self.parallel_r_label.setText(rpstr + " \N{OHM SIGN}") - self.parallel_x_label.setText(xpstr) - if self.returnloss_is_positive: - returnloss = -round(s11data[self.location].gain, 3) + re50 = round(re50, 3 - max(0, math.floor(math.log10(abs(re50))))) + if re50 > 10000: + re50str = str(round(re50/1000, 2)) + "k" + elif re50 > 1000: + re50str = str(round(re50)) # Remove the ".0" else: - returnloss = round(s11data[self.location].gain, 3) - self.returnloss_label.setText(str(returnloss) + " dB") - capacitance = RFTools.capacitanceEquivalent(im50, s11data[self.location].freq) - inductance = RFTools.inductanceEquivalent(im50, s11data[self.location].freq) - self.inductance_label.setText(inductance) - self.capacitance_label.setText(capacitance) - self.parallel_c_label.setText(xpcstr) - self.parallel_l_label.setText(xplstr) - if im50 > 0: - self.series_lc_label.setText(inductance) - else: - self.series_lc_label.setText(capacitance) - vswr = round(vswr, 3) - if vswr < 0: - vswr = "-" - self.vswr_label.setText(str(vswr)) - q = s11data[self.location].qFactor() - if q > 10000 or q < 0: - q_str = "\N{INFINITY}" - elif q > 1000: - q_str = str(round(q, 0)) - elif q > 100: - q_str = str(round(q, 1)) - elif q > 10: - q_str = str(round(q, 2)) + re50str = str(re50) + else: + rpstr = "-" + re50 = 0 + re50str = "-" + + if im50 != 0: + xp = (re50 ** 2 + im50 ** 2) / im50 + xp = round(xp, 3 - max(0, math.floor(math.log10(abs(xp))))) + xpcstr = RFTools.capacitanceEquivalent(xp, s11data[self.location].freq) + xplstr = RFTools.inductanceEquivalent(xp, s11data[self.location].freq) + if xp < 0: + xpstr = xpcstr + xp50str = " -j" + str(-1 * xp) else: - q_str = str(round(q, 3)) - self.quality_factor_label.setText(q_str) - self.s11_phase_label.setText( - str(round(math.degrees(s11data[self.location].phase), 2)) + "\N{DEGREE SIGN}") - fmt = SITools.Format(max_nr_digits=5, space_str=" ") - self.s11_group_delay_label.setText(str(SITools.Value(RFTools.groupDelay(s11data, self.location), "s", fmt))) - - if len(s21data) == len(s11data): - self.gain_label.setText(str(round(s21data[self.location].gain, 3)) + " dB") - self.s21_phase_label.setText( - str(round(math.degrees(s21data[self.location].phase), 2)) + "\N{DEGREE SIGN}") - self.s21_group_delay_label.setText(str(SITools.Value(RFTools.groupDelay(s21data, self.location) / 2, - "s", fmt))) + xpstr = xplstr + xp50str = " +j" + str(xp) + xp50str += " \N{OHM SIGN}" + else: + xp50str = " +j ? \N{OHM SIGN}" + xpstr = xpcstr = xplstr = "-" + + if im50 != 0: + im50 = round(im50, 3 - max(0, math.floor(math.log10(abs(im50))))) + if im50 < 0: + im50str = " -j" + str(-1 * im50) + else: + im50str = " +j" + str(im50) + im50str += " \N{OHM SIGN}" + + self.frequency_label.setText(RFTools.formatFrequency(s11data[self.location].freq)) + self.impedance_label.setText(re50str + im50str) + self.admittance_label.setText(rpstr + xp50str) + self.series_r_label.setText(re50str + " \N{OHM SIGN}") + self.parallel_r_label.setText(rpstr + " \N{OHM SIGN}") + self.parallel_x_label.setText(xpstr) + if self.returnloss_is_positive: + returnloss = -round(s11data[self.location].gain, 3) + else: + returnloss = round(s11data[self.location].gain, 3) + self.returnloss_label.setText(str(returnloss) + " dB") + capacitance = RFTools.capacitanceEquivalent(im50, s11data[self.location].freq) + inductance = RFTools.inductanceEquivalent(im50, s11data[self.location].freq) + self.inductance_label.setText(inductance) + self.capacitance_label.setText(capacitance) + self.parallel_c_label.setText(xpcstr) + self.parallel_l_label.setText(xplstr) + if im50 > 0: + self.series_lc_label.setText(inductance) + else: + self.series_lc_label.setText(capacitance) + vswr = round(vswr, 3) + if vswr < 0: + vswr = "-" + self.vswr_label.setText(str(vswr)) + q = s11data[self.location].qFactor() + self.quality_factor_label.setText(format_q_factor(q)) + self.s11_phase_label.setText( + str(round(math.degrees(s11data[self.location].phase), 2)) + "\N{DEGREE SIGN}") + fmt = SITools.Format(max_nr_digits=5, space_str=" ") + self.s11_group_delay_label.setText(str(SITools.Value(groupDelay(s11data, self.location), "s", fmt))) + + if len(s21data) == len(s11data): + self.gain_label.setText(str(round(s21data[self.location].gain, 3)) + " dB") + self.s21_phase_label.setText( + str(round(math.degrees(s21data[self.location].phase), 2)) + "\N{DEGREE SIGN}") + self.s21_group_delay_label.setText(str(SITools.Value(groupDelay(s21data, self.location) / 2, + "s", fmt))) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index fdeb356a..15e3bb41 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -22,14 +22,22 @@ from NanoVNASaver.SITools import Value, Format -def clamp_value(value: Real, rmin: Real, rmax: Real) -> Real: - assert rmin <= rmax - if value < rmin: - return rmin - if value > rmax: - return rmax - return value +def normalize(z: complex, ref_impedance: float=50) -> complex: + """Normalize from ref_impedance to z""" + return z / ref_impedance + + +def norm_to_impedance(z: complex, ref_impedance: float = 50) -> complex: + """Calculate impedance from normalized z""" + return z * ref_impedance +def reflection_coefficient(z: complex, ref_impedance: float = 50) -> complex: + """Calculate reflection coefficient for z""" + return (z - ref_impedance) / (z + ref_impedance) + +def gamma_to_impedance(gamma: complex, ref_impedance: float = 50) -> complex: + """Calculate reflection coefficient for z""" + return ((-gamma - 1) / (gamma - 1)) * ref_impedance class Datapoint(NamedTuple): freq: int @@ -63,7 +71,7 @@ def vswr(self) -> float: return (1 + mag) / (1 - mag) def impedance(self, ref_impedance: float = 50) -> complex: - return ref_impedance * ((-self.z - 1) / (self.z - 1)) + return gamma_to_impedance(self.z, ref_impedance) def qFactor(self, ref_impedance: float = 50) -> float: imp = self.impedance(ref_impedance) @@ -87,6 +95,28 @@ def inductiveEquivalent(self, ref_impedance: float = 50) -> float: return 0 return imp.imag * 1 / (self.freq * 2 * math.pi) +def clamp_value(value: Real, rmin: Real, rmax: Real) -> Real: + assert rmin <= rmax + if value < rmin: + return rmin + if value > rmax: + return rmax + return value + + + +def groupDelay(data: List[Datapoint], index: int) -> float: + idx0 = 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) + if abs(delta_angle) > math.tau: + if delta_angle > 0: + delta_angle = delta_angle % math.tau + else: + delta_angle = -1 * (delta_angle % math.tau) + val = -delta_angle / math.tau / (data[idx1].freq - data[idx0].freq) + return val + class RFTools: @staticmethod @@ -122,16 +152,3 @@ def parseFrequency(freq: str) -> int: return round(parser.parse(freq)) except (ValueError, IndexError): return -1 - - @staticmethod - def groupDelay(data: List[Datapoint], index: int) -> float: - idx0 = 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) - if abs(delta_angle) > math.tau: - if delta_angle > 0: - delta_angle = delta_angle % math.tau - else: - delta_angle = -1 * (delta_angle % math.tau) - val = -delta_angle / math.tau / (data[idx1].freq - data[idx0].freq) - return val From 43f412f7f2c17b844da5af55b462101450d8a1b5 Mon Sep 17 00:00:00 2001 From: "Rune B. Broberg" Date: Sun, 24 Nov 2019 14:27:39 +0100 Subject: [PATCH 69/84] Reset reference on the combined logmag chart fixed --- NanoVNASaver/Chart.py | 5 +++++ NanoVNASaver/RFTools.py | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index e6f2e82b..59db7330 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -1874,6 +1874,11 @@ def setCombinedReference(self, data11, data21): self.reference21 = data21 self.update() + def resetReference(self): + self.reference11 = [] + self.reference21 = [] + self.update() + def resetDisplayLimits(self): self.reference11 = [] self.reference21 = [] diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index fdeb356a..1f94d7dc 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -127,11 +127,14 @@ def parseFrequency(freq: str) -> int: def groupDelay(data: List[Datapoint], index: int) -> float: idx0 = 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 + if delta_freq == 0: + return 0 if abs(delta_angle) > math.tau: if delta_angle > 0: delta_angle = delta_angle % math.tau else: delta_angle = -1 * (delta_angle % math.tau) - val = -delta_angle / math.tau / (data[idx1].freq - data[idx0].freq) + val = -delta_angle / math.tau / delta_freq return val From dedceedd870fdd5b55113b7759f6a209a0986c60 Mon Sep 17 00:00:00 2001 From: "Rune B. Broberg" Date: Sun, 24 Nov 2019 14:35:04 +0100 Subject: [PATCH 70/84] 0.2.1 alpha 1 --- NanoVNASaver/about.py | 2 +- README.md | 7 +++---- setup.py | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/NanoVNASaver/about.py b/NanoVNASaver/about.py index 69245aa0..92217c10 100644 --- a/NanoVNASaver/about.py +++ b/NanoVNASaver/about.py @@ -14,5 +14,5 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -version = '0.2.1alpha' +version = '0.2.1alpha1' debug = False diff --git a/README.md b/README.md index 660d747d..b0bf7a3f 100644 --- a/README.md +++ b/README.md @@ -100,10 +100,7 @@ For most users, this is already installed. git clone https://github.com/mihtjel/nanovna-saver cd nanovna-saver -4. Change PyQt restriction in setup.py - `PyQt5==5.11.2` to `PyQt5` - -5. Install local pip packages +4. Install local pip packages python3 -m pip install . NanoVNASaver @@ -172,6 +169,8 @@ changes back to the community. ## Credits Original application by Rune B. Broberg (5Q5R) +Contributions and changes by Holger Müller, David Hunt and others. + TDR inspiration shamelessly stolen from the work of Salil (VU2CWA) at https://nuclearrambo.com/wordpress/accurately-measuring-cable-length-with-nanovna/ TDR cable types by Larry Goga. diff --git a/setup.py b/setup.py index 595cd08a..eb9200a8 100644 --- a/setup.py +++ b/setup.py @@ -50,7 +50,7 @@ }, install_requires=[ 'pyserial', - 'PyQt5>=5.11.2', + 'PyQt5', 'numpy', 'scipy' ], From 9e09f8ec9fba9c4cf7ca9f4e26537f0420652dee Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Sun, 24 Nov 2019 17:48:15 +0100 Subject: [PATCH 71/84] fixed typo --- NanoVNASaver/RFTools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index 62977447..730cb1d1 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -18,7 +18,6 @@ import cmath from numbers import Number, Real from typing import List, NamedTuple -upstream_Development from NanoVNASaver.SITools import Value, Format From 39a1a8d18aadb10ee909bd03880dd9737d6607f1 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Sun, 24 Nov 2019 18:12:51 +0100 Subject: [PATCH 72/84] Updated test cases --- test/test_formatSweepFrequency.py | 73 ++++++++++++------------------- test/test_touchstone.py | 11 ++++- 2 files changed, 38 insertions(+), 46 deletions(-) diff --git a/test/test_formatSweepFrequency.py b/test/test_formatSweepFrequency.py index 4225be81..5032dfd9 100644 --- a/test/test_formatSweepFrequency.py +++ b/test/test_formatSweepFrequency.py @@ -28,8 +28,6 @@ def test_basicIntegerValues(self): # simple well-formed integers with no trailing zeros. Most importantly # there is no loss of accuracy in the result. Returned values are not # truncated if result would lose meaningful data. - ''' - Original Behavior: self.assertEqual(rft.formatSweepFrequency(1), '1Hz') self.assertEqual(rft.formatSweepFrequency(12), '12Hz') self.assertEqual(rft.formatSweepFrequency(123), '123Hz') @@ -39,49 +37,36 @@ def test_basicIntegerValues(self): self.assertEqual(rft.formatSweepFrequency(1234567), '1.234567MHz') self.assertEqual(rft.formatSweepFrequency(12345678), '12.345678MHz') self.assertEqual(rft.formatSweepFrequency(123456789), '123.456789MHz') - ''' - # New Behavior: results in loss of accuracy again. - self.assertEqual(rft.formatSweepFrequency(1), '1.0000Hz') - self.assertEqual(rft.formatSweepFrequency(12), '12.000Hz') - self.assertEqual(rft.formatSweepFrequency(123), '123.00Hz') - self.assertEqual(rft.formatSweepFrequency(1234), '1.2340kHz') - self.assertEqual(rft.formatSweepFrequency(12345), '12.345kHz') - self.assertEqual(rft.formatSweepFrequency(123456), '123.46kHz') - self.assertEqual(rft.formatSweepFrequency(1234567), '1.2346MHz') - self.assertEqual(rft.formatSweepFrequency(12345678), '12.346MHz') - self.assertEqual(rft.formatSweepFrequency(123456789), '123.46MHz') - ''' - def test_defaultMinDigits(self): - # simple integers with trailing zeros. - # DEFAULT behavior retains 2 digits after the period, mindigits=2. - self.assertEqual(rft.formatSweepFrequency(1000), '1.00kHz') - self.assertEqual(rft.formatSweepFrequency(10000), '10.00kHz') - self.assertEqual(rft.formatSweepFrequency(100000), '100.00kHz') - self.assertEqual(rft.formatSweepFrequency(1000000), '1.00MHz') - - def test_nonDefaultMinDigits(self): - # simple integers with trailing zeros. setting mindigit value to something - # other than default, where trailing zeros >= mindigits, the number of - # zeros shown is equal to mindigits value. - self.assertEqual(rft.formatSweepFrequency(1000000, mindigits=0), '1MHz') - self.assertEqual(rft.formatSweepFrequency(1000000, mindigits=1), '1.0MHz') - self.assertEqual(rft.formatSweepFrequency(1000000, mindigits=3), '1.000MHz') - self.assertEqual(rft.formatSweepFrequency(10000000, mindigits=4), '10.0000MHz') - self.assertEqual(rft.formatSweepFrequency(100000000, mindigits=5), '100.00000MHz') - self.assertEqual(rft.formatSweepFrequency(1000000000, mindigits=6), '1.000000GHz') - # where trailing zeros < mindigits, only available zeros are shown, if the - # result includes no decimal places (i.e. Hz values). - self.assertEqual(rft.formatSweepFrequency(1, mindigits=4), '1Hz') - self.assertEqual(rft.formatSweepFrequency(10, mindigits=4), '10Hz') - self.assertEqual(rft.formatSweepFrequency(100, mindigits=4), '100Hz') - # but where a decimal exists, and mindigits > number of available zeroes, - # this results in extra zeroes being padded into result, even into sub-Hz - # resolution. This is not useful for this application. - # 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=10), '1.0000000000kHz') - ''' + # def test_defaultMinDigits(self): + # # simple integers with trailing zeros. + # # DEFAULT behavior retains 2 digits after the period, mindigits=2. + # self.assertEqual(rft.formatSweepFrequency(1000), '1.00kHz') + # self.assertEqual(rft.formatSweepFrequency(10000), '10.00kHz') + # self.assertEqual(rft.formatSweepFrequency(100000), '100.00kHz') + # self.assertEqual(rft.formatSweepFrequency(1000000), '1.00MHz') + + # def test_nonDefaultMinDigits(self): + # # simple integers with trailing zeros. setting mindigit value to something + # # other than default, where trailing zeros >= mindigits, the number of + # # zeros shown is equal to mindigits value. + # self.assertEqual(rft.formatSweepFrequency(1000000, mindigits=0), '1MHz') + # self.assertEqual(rft.formatSweepFrequency(1000000, mindigits=1), '1.0MHz') + # self.assertEqual(rft.formatSweepFrequency(1000000, mindigits=3), '1.000MHz') + # self.assertEqual(rft.formatSweepFrequency(10000000, mindigits=4), '10.0000MHz') + # self.assertEqual(rft.formatSweepFrequency(100000000, mindigits=5), '100.00000MHz') + # self.assertEqual(rft.formatSweepFrequency(1000000000, mindigits=6), '1.000000GHz') + # # where trailing zeros < mindigits, only available zeros are shown, if the + # # result includes no decimal places (i.e. Hz values). + # self.assertEqual(rft.formatSweepFrequency(1, mindigits=4), '1Hz') + # self.assertEqual(rft.formatSweepFrequency(10, mindigits=4), '10Hz') + # self.assertEqual(rft.formatSweepFrequency(100, mindigits=4), '100Hz') + # # but where a decimal exists, and mindigits > number of available zeroes, + # # this results in extra zeroes being padded into result, even into sub-Hz + # # resolution. This is not useful for this application. + # # 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=10), '1.0000000000kHz') if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/test/test_touchstone.py b/test/test_touchstone.py index 2841d09c..7a39fa3e 100644 --- a/test/test_touchstone.py +++ b/test/test_touchstone.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import unittest +import logging # Import targets to be tested from NanoVNASaver.Touchstone import Options, Touchstone @@ -74,8 +75,14 @@ def test_load(self): def test_load_scikit(self): ts = Touchstone("./test/data/scikit_unordered.s2p") - ts.load() - print(len(ts.s("12"))) + with self.assertLogs(level=logging.WARNING) as cm: + ts.load() + self.assertEqual(cm.output, [ + 'WARNING:NanoVNASaver.Touchstone:Non integer resistance value: 50.0', + 'WARNING:NanoVNASaver.Touchstone:Comment after header: !freq ReS11 ImS11 ReS21 ImS21 ReS12 ImS12 ReS22 ImS22', + 'WARNING:NanoVNASaver.Touchstone:Frequency not ascending: 15000000.0 0.849810063 -0.4147357 -0.000306106 0.0041482 0.0 0.0 0.0 0.0', + 'WARNING:NanoVNASaver.Touchstone:Reordering data', + ]) self.assertEqual(str(ts.opts), "# HZ S RI R 50") self.assertEqual(len(ts.s11data), 101) self.assertIn("!freq ReS11 ImS11 ReS21 ImS21 ReS12 ImS12 ReS22 ImS22", From e9a850a877595deac2f60724b4b81fd5f65c6629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Holger=20M=C3=BCller?= Date: Sun, 24 Nov 2019 18:19:57 +0100 Subject: [PATCH 73/84] Create pythonapp.yml --- .github/workflows/pythonapp.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/pythonapp.yml diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml new file mode 100644 index 00000000..a01ac097 --- /dev/null +++ b/.github/workflows/pythonapp.yml @@ -0,0 +1,26 @@ +name: Python application + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Set up Python 3.8 + uses: actions/setup-python@v1 + with: + python-version: 3.8 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Lint with flake8 + run: | + pip install pylint + pylint --exit-zero NanoVNASaver + - name: Test with pytest + run: | + python3 ./test_master.py From 8d963dbe47fc06b636232d4123fb81e2ad1885e6 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Sun, 24 Nov 2019 18:25:46 +0100 Subject: [PATCH 74/84] modified python workflow --- .github/workflows/pythonapp.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index a01ac097..377dce74 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -17,10 +17,10 @@ jobs: run: | python -m pip install --upgrade pip pip install -r requirements.txt - - name: Lint with flake8 + - name: Lint with pylint run: | pip install pylint pylint --exit-zero NanoVNASaver - - name: Test with pytest + - name: Unittests run: | python3 ./test_master.py From 8e446dcfb67fef8321e66ce672a58426f2bb60fe Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Mon, 25 Nov 2019 10:33:07 +0100 Subject: [PATCH 75/84] More SITools tests Added more test for SITools Value Modified parse semantic a little bit, returns Value object now --- NanoVNASaver/RFTools.py | 2 +- NanoVNASaver/SITools.py | 20 +++++-- test/test_parseFrequency.py | 4 +- test/test_sitools.py | 104 ++++++++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 test/test_sitools.py diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index 730cb1d1..c5a08693 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -154,6 +154,6 @@ def formatSweepFrequency(freq: Number) -> str: def parseFrequency(freq: str) -> int: parser = Value(0, "Hz", Format(parse_sloppy_unit=True, parse_sloppy_kilo=True)) try: - return round(parser.parse(freq)) + return round(parser.parse(freq).value) except (ValueError, IndexError): return -1 diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py index edab569f..8d6eaf6c 100644 --- a/NanoVNASaver/SITools.py +++ b/NanoVNASaver/SITools.py @@ -31,6 +31,7 @@ class Format(NamedTuple): min_offset: int = -8 max_offset: int = 8 allow_strip: bool = False + allways_signed: bool = False parse_sloppy_unit: bool = False parse_sloppy_kilo: bool = False parse_clamp_min: float = -math.inf @@ -86,6 +87,8 @@ def __str__(self) -> str: (1 if not fmt.fix_decimals and abs(real) < 100 else 0)) formstr = "." + str(max_digits - 3) + "f" + if self.fmt.allways_signed: + formstr = "+" + formstr result = format(real, formstr) if float(result) == 0.0: @@ -96,11 +99,22 @@ def __str__(self) -> str: return result + fmt.space_str + PREFIXES[offset + 8] + self._unit + def __float__(self): + return float(self._value) + @property def value(self): - return float(self._value) + return self._value + + @value.setter + def value(self, value: Number): + self._value = decimal.Decimal(value, context=Value.CTX) + + def parse(self, value: str) -> Value: + if isinstance(value, Number): + self.value = value + return self - def parse(self, value: str) -> float: value = value.replace(" ", "") # Ignore spaces if self._unit and ( @@ -132,7 +146,7 @@ def parse(self, value: str) -> float: self._value = self.fmt.parse_clamp_min elif self._value > self.fmt.parse_clamp_max: self._value = self.fmt.parse_clamp_max - return float(self._value) + return self @property def unit(self) -> str: diff --git a/test/test_parseFrequency.py b/test/test_parseFrequency.py index 860b20be..d0f5ff7b 100644 --- a/test/test_parseFrequency.py +++ b/test/test_parseFrequency.py @@ -72,8 +72,8 @@ def test_unusualSIUnits(self): self.assertEqual(rft.parseFrequency('123EHz'), 123000000000000000000) self.assertEqual(rft.parseFrequency('123PHz'), 123000000000000000) self.assertEqual(rft.parseFrequency('123THz'), 123000000000000) - self.assertEqual(rft.parseFrequency('123YHz'), 122999999999999998473273344) - self.assertEqual(rft.parseFrequency('123ZHz'), 123000000000000002097152) + self.assertEqual(rft.parseFrequency('123YHz'), 123000000000000000000000000) + self.assertEqual(rft.parseFrequency('123ZHz'), 123000000000000000000000) self.assertEqual(rft.parseFrequency('123aHz'), 0) self.assertEqual(rft.parseFrequency('123fHz'), 0) self.assertEqual(rft.parseFrequency('123mHz'), 0) diff --git a/test/test_sitools.py b/test/test_sitools.py new file mode 100644 index 00000000..01b725db --- /dev/null +++ b/test/test_sitools.py @@ -0,0 +1,104 @@ +# NanoVNASaver +# A python program to view and export Touchstone data from a NanoVNA +# Copyright (C) 2019. Rune B. Broberg +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +import unittest + +# Import targets to be tested +from NanoVNASaver.SITools import Format, Value + +F_DEFAULT = Format() + +F_ASSERT_DIGITS_1 = Format(max_nr_digits=2) +F_ASSERT_DIGITS_2 = Format(max_nr_digits=31) +F_ASSERT_OFFSET_1 = Format(min_offset=-9) +F_ASSERT_OFFSET_2 = Format(max_offset=9) +F_ASSERT_OFFSET_3 = Format(min_offset=9) +F_ASSERT_CLAMP = Format(parse_clamp_min=10, parse_clamp_max=9) + +F_DIGITS_3 = Format(max_nr_digits=3) +F_DIGITS_4 = Format(max_nr_digits=3) +F_DIGITS_31 = Format(max_nr_digits=30) + +F_WITH_SPACE = Format(space_str=" ") +F_WITH_UNDERSCORE = Format(space_str="_") + + +class TestTSIToolsValue(unittest.TestCase): + + def test_format_asssertions(self): + self.assertRaises(AssertionError, Value, fmt=F_ASSERT_DIGITS_1) + self.assertRaises(AssertionError, Value, fmt=F_ASSERT_DIGITS_2) + self.assertRaises(AssertionError, Value, fmt=F_ASSERT_OFFSET_1) + self.assertRaises(AssertionError, Value, fmt=F_ASSERT_OFFSET_2) + self.assertRaises(AssertionError, Value, fmt=F_ASSERT_OFFSET_3) + self.assertRaises(AssertionError, Value, fmt=F_ASSERT_CLAMP) + + def test_default_format(self): + self.assertEqual(str(Value(0)), "0.00000") + self.assertEqual(str(Value(1)), "1.00000") + self.assertEqual(str(Value(10)), "10.0000") + self.assertEqual(str(Value(100)), "100.000") + self.assertEqual(str(Value(-1)), "-1.00000") + self.assertEqual(str(Value(-10)), "-10.0000") + self.assertEqual(str(Value(-100)), "-100.000") + + self.assertEqual(str(Value(1e3)), "1.00000k") + self.assertEqual(str(Value(1e4)), "10.0000k") + self.assertEqual(str(Value(1e5)), "100.000k") + self.assertEqual(str(Value(1e6)), "1.00000M") + self.assertEqual(str(Value(1e7)), "10.0000M") + self.assertEqual(str(Value(1e8)), "100.000M") + self.assertEqual(str(Value(1e9)), "1.00000G") + self.assertEqual(str(Value(1e12)), "1.00000T") + self.assertEqual(str(Value(1e15)), "1.00000P") + self.assertEqual(str(Value(1e18)), "1.00000E") + self.assertEqual(str(Value(1e21)), "1.00000Z") + self.assertEqual(str(Value(1e24)), "1.00000Y") + self.assertEqual(str(Value(1e27)), "\N{INFINITY}") + self.assertEqual(str(Value(-1e27)), "-\N{INFINITY}") + self.assertEqual(float(Value(1e27)), 1e27) + + self.assertEqual(str(Value(.1)), "100.000m") + self.assertEqual(str(Value(.01)), "10.0000m") + self.assertEqual(str(Value(.001)), "1.00000m") + self.assertEqual(str(Value(1e-6)), "1.00000µ") + self.assertEqual(str(Value(1e-9)), "1.00000n") + self.assertEqual(str(Value(1e-12)), "1.00000p") + self.assertEqual(str(Value(1e-15)), "1.00000f") + self.assertEqual(str(Value(1e-18)), "1.00000a") + self.assertEqual(str(Value(1e-21)), "1.00000z") + self.assertEqual(str(Value(1e-24)), "1.00000y") + self.assertEqual(str(Value(1e-27)), "0.00100y") + self.assertEqual(str(Value(1e-29)), "0.00001y") + self.assertEqual(str(Value(-1e-29)), "-0.00001y") + self.assertEqual(str(Value(1e-30)), "0.00000") + self.assertEqual(float(Value(1e-30)), 1e-30) + + def test_format_digits_3(self): + v = Value(fmt=F_DIGITS_3) + self.assertEqual(str(v.parse("1")), "1") + self.assertEqual(str(v.parse("10")), "10") + self.assertEqual(str(v.parse("100")), "100") + self.assertEqual(str(v.parse("1e3")), "1k") + self.assertEqual(str(v.parse("1e4")), "10k") + self.assertEqual(str(v.parse("1e5")), "100k") + self.assertEqual(str(v.parse("1e-1")), "100m") + self.assertEqual(str(v.parse("1e-2")), "10m") + self.assertEqual(str(v.parse("1e-3")), "1m") + + +if __name__ == '__main__': + unittest.main(verbosity=2) From 5cc454da14f4a671cfa203e248d04bcc72921b65 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Mon, 25 Nov 2019 11:00:30 +0100 Subject: [PATCH 76/84] Pep8 cleanups --- NanoVNASaver/RFTools.py | 13 ++++--------- test/test_sitools.py | 2 +- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index c5a08693..604d5406 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -21,27 +21,21 @@ from NanoVNASaver.SITools import Value, Format -def clamp_value(value: Real, rmin: Real, rmax: Real) -> Real: - assert rmin <= rmax - if value < rmin: - return rmin - if value > rmax: - return rmax - return value - - def norm_to_impedance(z: complex, ref_impedance: float = 50) -> complex: """Calculate impedance from normalized z""" return z * ref_impedance + def reflection_coefficient(z: complex, ref_impedance: float = 50) -> complex: """Calculate reflection coefficient for z""" return (z - ref_impedance) / (z + ref_impedance) + def gamma_to_impedance(gamma: complex, ref_impedance: float = 50) -> complex: """Calculate reflection coefficient for z""" return ((-gamma - 1) / (gamma - 1)) * ref_impedance + class Datapoint(NamedTuple): freq: int re: float @@ -98,6 +92,7 @@ def inductiveEquivalent(self, ref_impedance: float = 50) -> float: return 0 return imp.imag * 1 / (self.freq * 2 * math.pi) + def clamp_value(value: Real, rmin: Real, rmax: Real) -> Real: assert rmin <= rmax if value < rmin: diff --git a/test/test_sitools.py b/test/test_sitools.py index 01b725db..769d3abf 100644 --- a/test/test_sitools.py +++ b/test/test_sitools.py @@ -98,7 +98,7 @@ def test_format_digits_3(self): self.assertEqual(str(v.parse("1e-1")), "100m") self.assertEqual(str(v.parse("1e-2")), "10m") self.assertEqual(str(v.parse("1e-3")), "1m") - + if __name__ == '__main__': unittest.main(verbosity=2) From 5d188e67cc6f7ea67e87fbcd368b91146a656c46 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 26 Nov 2019 10:14:40 +0100 Subject: [PATCH 77/84] Added Value typehint to parse method --- NanoVNASaver/SITools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NanoVNASaver/SITools.py b/NanoVNASaver/SITools.py index 8d6eaf6c..e169b32c 100644 --- a/NanoVNASaver/SITools.py +++ b/NanoVNASaver/SITools.py @@ -110,7 +110,7 @@ def value(self): def value(self, value: Number): self._value = decimal.Decimal(value, context=Value.CTX) - def parse(self, value: str) -> Value: + def parse(self, value: str) -> "Value": if isinstance(value, Number): self.value = value return self From cec746cb6486580398db4794395a690479bc1bc8 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 26 Nov 2019 10:15:46 +0100 Subject: [PATCH 78/84] Import path fix for unittests --- test/test_sitools.py | 3 --- test_master.py | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/test/test_sitools.py b/test/test_sitools.py index 769d3abf..bc5332b8 100644 --- a/test/test_sitools.py +++ b/test/test_sitools.py @@ -99,6 +99,3 @@ def test_format_digits_3(self): self.assertEqual(str(v.parse("1e-2")), "10m") self.assertEqual(str(v.parse("1e-3")), "1m") - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/test_master.py b/test_master.py index df17a272..3749f87e 100644 --- a/test_master.py +++ b/test_master.py @@ -29,6 +29,7 @@ ############################################################################### if __name__ == '__main__': + sys.path.append('.') loader = unittest.TestLoader() tests = loader.discover('.') testRunner = unittest.runner.TextTestRunner( From 28bab998ec0a274970475e4c8aae02ebe7c64ec1 Mon Sep 17 00:00:00 2001 From: Rune Broberg Date: Wed, 27 Nov 2019 15:53:57 +0100 Subject: [PATCH 79/84] Reverting VSWR calculation for compatibility. --- NanoVNASaver/RFTools.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/NanoVNASaver/RFTools.py b/NanoVNASaver/RFTools.py index 604d5406..bc9a80ca 100644 --- a/NanoVNASaver/RFTools.py +++ b/NanoVNASaver/RFTools.py @@ -63,8 +63,6 @@ def vswr(self) -> float: mag = abs(self.z) if mag == 1: return 1 - if mag > 1: - return math.inf return (1 + mag) / (1 - mag) def impedance(self, ref_impedance: float = 50) -> complex: From 3f1ccb785ede4b1209541172d7c52e753582fbf5 Mon Sep 17 00:00:00 2001 From: Rune Broberg Date: Wed, 27 Nov 2019 16:41:55 +0100 Subject: [PATCH 80/84] Fixed setting markers where >1 data point exists at the same frequency --- NanoVNASaver/Chart.py | 1 + NanoVNASaver/Marker.py | 5 +++-- NanoVNASaver/SweepWorker.py | 13 ++++++------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index 59db7330..a0246156 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -3665,6 +3665,7 @@ def calculateGroupDelay(self): self.unwrappedData = np.degrees(np.unwrap(rawData)) self.groupDelay = [] for i in range(len(self.data)): + # TODO: Replace with call to RFTools.groupDelay if i == 0: phase_change = self.unwrappedData[1] - self.unwrappedData[0] freq_change = self.data[1].freq - self.data[0].freq diff --git a/NanoVNASaver/Marker.py b/NanoVNASaver/Marker.py index 0e20cb74..650fb918 100644 --- a/NanoVNASaver/Marker.py +++ b/NanoVNASaver/Marker.py @@ -26,8 +26,9 @@ FMT_Q_FACTOR = SITools.Format(max_nr_digits=4, assume_infinity=False, min_offset=0, max_offset=0, allow_strip=True) + def format_q_factor(val: float) -> str: - if 0 > val or val > 10000.0: + if 0 > val or val > 10000.0: return "\N{INFINITY}" return str(SITools.Value(val, fmt=FMT_Q_FACTOR)) @@ -275,7 +276,7 @@ def findLocation(self, data: List[Datapoint]): min_distance = max_freq for i in range(len(data)): - if abs(data[i].freq - self.frequency) < min_distance: + if abs(data[i].freq - self.frequency) <= min_distance: min_distance = abs(data[i].freq - self.frequency) else: # We have now started moving away from the nearest point diff --git a/NanoVNASaver/SweepWorker.py b/NanoVNASaver/SweepWorker.py index f0a31228..2d496265 100644 --- a/NanoVNASaver/SweepWorker.py +++ b/NanoVNASaver/SweepWorker.py @@ -168,14 +168,13 @@ def run(self): self.signals.finished.emit() return - def updateData(self, values11, values21, offset): + def updateData(self, values11, values21, offset, segment_size = 101): # Update the data from (i*101) to (i+1)*101 - # TODO: Get rid of the 101 point assumptions logger.debug("Calculating data and inserting in existing data at offset %d", offset) for i in range(len(values11)): re, im = values11[i] re21, im21 = values21[i] - freq = self.data11[offset*101 + i].freq + freq = self.data11[offset * segment_size + i].freq rawData11 = Datapoint(freq, re, im) rawData21 = Datapoint(freq, re21, im21) # TODO: Use applyCalibration instead @@ -184,10 +183,10 @@ def updateData(self, values11, values21, offset): if self.app.calibration.isValid2Port(): re21, im21 = self.app.calibration.correct21(re21, im21, freq) - self.data11[offset*101 + i] = Datapoint(freq, re, im) - self.data21[offset * 101 + i] = Datapoint(freq, re21, im21) - self.rawData11[offset * 101 + i] = rawData11 - self.rawData21[offset * 101 + i] = rawData21 + self.data11[offset * segment_size + i] = Datapoint(freq, re, im) + self.data21[offset * segment_size + i] = Datapoint(freq, re21, im21) + self.rawData11[offset * segment_size + i] = rawData11 + self.rawData21[offset * segment_size + i] = rawData21 logger.debug("Saving data to application (%d and %d points)", len(self.data11), len(self.data21)) self.app.saveData(self.data11, self.data21) logger.debug("Sending \"updated\" signal") From 1ff466991b9e7023165bbbafdf58db97accc755c Mon Sep 17 00:00:00 2001 From: Rune Broberg Date: Mon, 2 Dec 2019 10:38:36 +0100 Subject: [PATCH 81/84] 0.2.1 alpha2 --- NanoVNASaver/about.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NanoVNASaver/about.py b/NanoVNASaver/about.py index 92217c10..4e8f05a8 100644 --- a/NanoVNASaver/about.py +++ b/NanoVNASaver/about.py @@ -14,5 +14,5 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -version = '0.2.1alpha1' +version = '0.2.1alpha2' debug = False From 263abc8568e022766eb6fd7e255613026e1ac460 Mon Sep 17 00:00:00 2001 From: Rune Broberg Date: Mon, 2 Dec 2019 10:50:41 +0100 Subject: [PATCH 82/84] Fixed Touchstone loading --- NanoVNASaver/Touchstone.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/NanoVNASaver/Touchstone.py b/NanoVNASaver/Touchstone.py index 255ab94e..64299ce1 100644 --- a/NanoVNASaver/Touchstone.py +++ b/NanoVNASaver/Touchstone.py @@ -129,20 +129,17 @@ def _parse_comments(self, fp) -> str: continue return line - def _append_line_data(self, freq: float, data: list): + def _append_line_data(self, freq: int, data: list): data_list = iter(self.sdata) vals = iter(data) for v in vals: if self.opts.format == "ri": - next(data_list).append( - Datapoint(freq, float(v), float(next(vals)))) + next(data_list).append(Datapoint(freq, float(v), float(next(vals)))) if self.opts.format == "ma": - z = cmath.polar(float(v), - math.radians(float(next(vals)))) + z = cmath.rect(float(v), math.radians(float(next(vals)))) next(data_list).append(Datapoint(freq, z.real, z.imag)) if self.opts.format == "db": - z = cmath.polar(math.exp(float(v) / 20), - math.radians(float(next(vals)))) + z = cmath.rect(math.exp(float(v) / 20), math.radians(float(next(vals)))) next(data_list).append(Datapoint(freq, z.real, z.imag)) def load(self): @@ -184,7 +181,7 @@ def _loads(self, s: str): # ignore comments at data end data = line.split('!')[0] data = data.split() - freq, data = float(data[0]) * self.opts.factor, data[1:] + freq, data = round(float(data[0]) * self.opts.factor), data[1:] data_len = len(data) # consistency checks From 593d6b04694f46da6fdbd80f66f560edafa8efa2 Mon Sep 17 00:00:00 2001 From: "Rune B. Broberg" Date: Wed, 4 Dec 2019 11:08:07 +0100 Subject: [PATCH 83/84] Fixed crash on GroupDelay chart popout --- NanoVNASaver/Chart.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index a0246156..f5e439d3 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -3619,9 +3619,6 @@ def __init__(self, name="", reflective=True): self.reflective = reflective - self.unwrappedData = [] - self.unwrappedReference = [] - self.groupDelay = [] self.groupDelayReference = [] @@ -3640,6 +3637,8 @@ def __init__(self, name="", reflective=True): def copy(self): new_chart: GroupDelayChart = super().copy() new_chart.reflective = self.reflective + new_chart.groupDelay = self.groupDelay.copy() + new_chart.groupDelayReference = self.groupDelay.copy() return new_chart def setReference(self, data): @@ -3662,19 +3661,19 @@ def calculateGroupDelay(self): rawReference.append(d.phase) if len(self.data) > 0: - self.unwrappedData = np.degrees(np.unwrap(rawData)) + unwrappedData = np.degrees(np.unwrap(rawData)) self.groupDelay = [] for i in range(len(self.data)): # TODO: Replace with call to RFTools.groupDelay if i == 0: - phase_change = self.unwrappedData[1] - self.unwrappedData[0] + phase_change = unwrappedData[1] - unwrappedData[0] freq_change = self.data[1].freq - self.data[0].freq elif i == len(self.data)-1: idx = len(self.data)-1 - phase_change = self.unwrappedData[idx] - self.unwrappedData[idx-1] + phase_change = unwrappedData[idx] - unwrappedData[idx-1] freq_change = self.data[idx].freq - self.data[idx-1].freq else: - phase_change = self.unwrappedData[i+1] - self.unwrappedData[i-1] + phase_change = unwrappedData[i+1] - unwrappedData[i-1] freq_change = self.data[i+1].freq - self.data[i-1].freq delay = (-phase_change / (freq_change * 360)) * 10e8 if not self.reflective: @@ -3682,18 +3681,18 @@ def calculateGroupDelay(self): self.groupDelay.append(delay) if len(self.reference) > 0: - self.unwrappedReference = np.degrees(np.unwrap(rawReference)) + unwrappedReference = np.degrees(np.unwrap(rawReference)) self.groupDelayReference = [] for i in range(len(self.reference)): if i == 0: - phase_change = self.unwrappedReference[1] - self.unwrappedReference[0] + phase_change = unwrappedReference[1] - unwrappedReference[0] freq_change = self.reference[1].freq - self.reference[0].freq elif i == len(self.reference)-1: idx = len(self.reference)-1 - phase_change = self.unwrappedReference[idx] - self.unwrappedReference[idx-1] + phase_change = unwrappedReference[idx] - unwrappedReference[idx-1] freq_change = self.reference[idx].freq - self.reference[idx-1].freq else: - phase_change = self.unwrappedReference[i+1] - self.unwrappedReference[i-1] + phase_change = unwrappedReference[i+1] - unwrappedReference[i-1] freq_change = self.reference[i+1].freq - self.reference[i-1].freq delay = (-phase_change / (freq_change * 360)) * 10e8 if not self.reflective: From 41ec185b7aaaad5a5c7ddd6e474777275a7db03f Mon Sep 17 00:00:00 2001 From: "Rune B. Broberg" Date: Wed, 4 Dec 2019 11:33:18 +0100 Subject: [PATCH 84/84] 0.2.1 release --- NanoVNASaver/about.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NanoVNASaver/about.py b/NanoVNASaver/about.py index 4e8f05a8..a5d114a1 100644 --- a/NanoVNASaver/about.py +++ b/NanoVNASaver/about.py @@ -14,5 +14,5 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -version = '0.2.1alpha2' +version = '0.2.1' debug = False