From c3639b2557d0f9b3c7929bdf706d7f895735f432 Mon Sep 17 00:00:00 2001 From: Holger Mueller Date: Tue, 19 Nov 2019 10:00:17 +0100 Subject: [PATCH] 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 | 80 +++++++++++++++++------ 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, 108 insertions(+), 20 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 ee6c7ea..17ba444 100644 --- a/NanoVNASaver/NanoVNASaver.py +++ b/NanoVNASaver/NanoVNASaver.py @@ -905,7 +905,8 @@ class NanoVNASaver(QtWidgets.QWidget): 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 784d834..e6b7d32 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 @@ class Options: 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() + @property + 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() + 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) - sdata = [[], [], [], []] # at max 4 data pairs + 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) - 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) + 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 = prev_len = 0 + 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 @@ class Touchstone: 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 @@ class Touchstone: 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 0000000..d0c69d7 --- /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 0000000..9800325 --- /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 0000000..a81aabe --- /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 0000000..25e54e2 --- /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