kopia lustrzana https://github.com/NanoVNA-Saver/nanovna-saver
add interpolation for touchstone data
rodzic
948c04f154
commit
27d3f492d8
|
@ -292,42 +292,33 @@ class Calibration:
|
|||
e30.append(caldata["e30"])
|
||||
e10e32.append(caldata["e10e32"])
|
||||
|
||||
self.interp["e00"] = interp1d(freq, e00, kind="slinear")
|
||||
self.interp["e11"] = interp1d(freq, e11, kind="slinear")
|
||||
self.interp["delta_e"] = interp1d(freq, delta_e, kind="slinear")
|
||||
self.interp["e30"] = interp1d(freq, e30, kind="slinear")
|
||||
self.interp["e10e32"] = interp1d(freq, e10e32, kind="slinear")
|
||||
self.interp = {
|
||||
"e00": interp1d(freq, e00,
|
||||
kind="slinear", bounds_error=False,
|
||||
fill_value=(e00[0], e00[-1])),
|
||||
"e11": interp1d(freq, e11,
|
||||
kind="slinear", bounds_error=False,
|
||||
fill_value=(e11[0], e11[-1])),
|
||||
"delta_e": interp1d(freq, delta_e,
|
||||
kind="slinear", bounds_error=False,
|
||||
fill_value=(delta_e[0], delta_e[-1])),
|
||||
"e30": interp1d(freq, e30,
|
||||
kind="slinear", bounds_error=False,
|
||||
fill_value=(e30[0], e30[-1])),
|
||||
"e10e32": interp1d(freq, e10e32,
|
||||
kind="slinear", bounds_error=False,
|
||||
fill_value=(e10e32[0], e10e32[-1])),
|
||||
}
|
||||
|
||||
def correct11(self, dp: Datapoint):
|
||||
i = self.interp
|
||||
try:
|
||||
s11 = (dp.z - i["e00"](dp.freq)) / (
|
||||
(dp.z * i["e11"](dp.freq)) - i["delta_e"](dp.freq))
|
||||
return Datapoint(dp.freq, s11.real, s11.imag)
|
||||
except ValueError:
|
||||
# TODO: implement warn message in gui
|
||||
logger.info("Data outside calibration")
|
||||
|
||||
nearest = sorted(self.dataset.frequencies(),
|
||||
key=lambda k: abs(dp.freq - k))[0]
|
||||
ds = self.dataset.get(nearest)
|
||||
s11 = (dp.z - ds["e00"]) / (
|
||||
(dp.z * ds["e11"]) - ds["delta_e"])
|
||||
s11 = (dp.z - i["e00"](dp.freq)) / (
|
||||
(dp.z * i["e11"](dp.freq)) - i["delta_e"](dp.freq))
|
||||
return Datapoint(dp.freq, s11.real, s11.imag)
|
||||
|
||||
def correct21(self, dp: Datapoint):
|
||||
i = self.interp
|
||||
try:
|
||||
s21 = (dp.z - i["e30"](dp.freq)) / i["e10e32"](dp.freq)
|
||||
return Datapoint(dp.freq, s21.real, s21.imag)
|
||||
except ValueError:
|
||||
# TODO: implement warn message in gui
|
||||
logger.info("Data outside calibration")
|
||||
|
||||
nearest = sorted(self.dataset.frequencies(),
|
||||
key=lambda k: abs(dp.freq - k))[0]
|
||||
ds = self.dataset.get(nearest)
|
||||
s21 = (dp.z - ds["e30"]) / ds["e10e32"]
|
||||
s21 = (dp.z - i["e30"](dp.freq)) / i["e10e32"](dp.freq)
|
||||
return Datapoint(dp.freq, s21.real, s21.imag)
|
||||
|
||||
# TODO: implement tests
|
||||
|
|
|
@ -21,6 +21,11 @@ import math
|
|||
import cmath
|
||||
import io
|
||||
from operator import attrgetter
|
||||
|
||||
from typing import List
|
||||
|
||||
from scipy.interpolate import interp1d
|
||||
|
||||
from NanoVNASaver.RFTools import Datapoint
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -98,30 +103,78 @@ class Touchstone:
|
|||
self.sdata = [[], [], [], []] # at max 4 data pairs
|
||||
self.comments = []
|
||||
self.opts = Options()
|
||||
self._interp = {}
|
||||
|
||||
@property
|
||||
def s11data(self) -> list:
|
||||
def s11data(self) -> List[Datapoint]:
|
||||
return self.s("11")
|
||||
|
||||
@s11data.setter
|
||||
def s11data(self, value: List[Datapoint]):
|
||||
self.sdata[0] = value
|
||||
|
||||
@property
|
||||
def s12data(self) -> list:
|
||||
def s12data(self) -> List[Datapoint]:
|
||||
return self.s("12")
|
||||
|
||||
@property
|
||||
def s21data(self) -> list:
|
||||
return self.s("21")
|
||||
@s12data.setter
|
||||
def s12data(self, value: List[Datapoint]):
|
||||
self.sdata[2] = value
|
||||
|
||||
@property
|
||||
def s22data(self) -> list:
|
||||
def s21data(self) -> List[Datapoint]:
|
||||
return self.s("21")
|
||||
|
||||
@s21data.setter
|
||||
def s21data(self, value: List[Datapoint]):
|
||||
self.sdata[1] = value
|
||||
|
||||
@property
|
||||
def s22data(self) -> List[Datapoint]:
|
||||
return self.s("22")
|
||||
|
||||
@s22data.setter
|
||||
def s22data(self, value: List[Datapoint]):
|
||||
self.sdata[3] = value
|
||||
|
||||
@property
|
||||
def r(self) -> int:
|
||||
return self.opts.resistance
|
||||
|
||||
def s(self, name: str) -> list:
|
||||
def s(self, name: str) -> List[Datapoint]:
|
||||
return self.sdata[Touchstone.FIELD_ORDER.index(name)]
|
||||
|
||||
def s_freq(self, name: str, freq: int) -> Datapoint:
|
||||
return Datapoint(freq,
|
||||
float(self._interp[name]["real"](freq)),
|
||||
float(self._interp[name]["imag"](freq)))
|
||||
|
||||
def min_freq(self) -> int:
|
||||
return self.s("11")[0].freq
|
||||
|
||||
def max_freq(self) -> int:
|
||||
return self.s("11")[-1].freq
|
||||
|
||||
def gen_interpolation(self):
|
||||
for i in Touchstone.FIELD_ORDER:
|
||||
freq = []
|
||||
real = []
|
||||
imag = []
|
||||
|
||||
for dp in self.s(i):
|
||||
freq.append(dp.freq)
|
||||
real.append(dp.re)
|
||||
imag.append(dp.im)
|
||||
|
||||
self._interp[i] = {
|
||||
"real": interp1d(freq, real,
|
||||
kind="slinear", bounds_error=False,
|
||||
fill_value=(real[0], real[-1])),
|
||||
"imag": interp1d(freq, imag,
|
||||
kind="slinear", bounds_error=False,
|
||||
fill_value=(imag[0], imag[-1])),
|
||||
}
|
||||
|
||||
def _parse_comments(self, fp) -> str:
|
||||
for line in fp:
|
||||
line = line.strip()
|
||||
|
|
|
@ -69,12 +69,20 @@ class TestTouchstoneTouchstone(unittest.TestCase):
|
|||
|
||||
ts = Touchstone("./test/data/valid.s2p")
|
||||
ts.load()
|
||||
ts.gen_interpolation()
|
||||
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)
|
||||
self.assertEqual(ts.min_freq(), 500000)
|
||||
self.assertEqual(ts.max_freq(), 900000000)
|
||||
self.assertEqual(ts.s_freq("11", 1),
|
||||
Datapoint(1, -3.33238E-001, 1.80018E-004))
|
||||
self.assertEqual(ts.s_freq("11", 750000),
|
||||
Datapoint(750000, -0.3331754099382822,
|
||||
0.00032433255669243524))
|
||||
|
||||
ts = Touchstone("./test/data/ma.s2p")
|
||||
ts.load()
|
||||
|
@ -119,9 +127,13 @@ class TestTouchstoneTouchstone(unittest.TestCase):
|
|||
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:'
|
||||
'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")
|
||||
|
@ -129,6 +141,23 @@ class TestTouchstoneTouchstone(unittest.TestCase):
|
|||
self.assertIn("!freq ReS11 ImS11 ReS21 ImS21 ReS12 ImS12 ReS22 ImS22",
|
||||
ts.comments)
|
||||
|
||||
def test_setter(self):
|
||||
ts = Touchstone("")
|
||||
dp_list = [Datapoint(1, 0.0, 0.0), Datapoint(3, 1.0, 1.0)]
|
||||
ts.s11data = dp_list[:]
|
||||
ts.s21data = dp_list[:]
|
||||
ts.s12data = dp_list[:]
|
||||
ts.s22data = dp_list[:]
|
||||
self.assertEqual(ts.s11data, dp_list)
|
||||
self.assertEqual(ts.s21data, dp_list)
|
||||
self.assertEqual(ts.s12data, dp_list)
|
||||
self.assertEqual(ts.s22data, dp_list)
|
||||
self.assertEqual(ts.min_freq(), 1)
|
||||
self.assertEqual(ts.max_freq(), 3)
|
||||
ts.gen_interpolation()
|
||||
self.assertEqual(ts.s_freq("11", 2), Datapoint(2, 0.5, 0.5))
|
||||
|
||||
|
||||
def test_save(self):
|
||||
ts = Touchstone("./test/data/valid.s2p")
|
||||
self.assertEqual(ts.saves(), "# HZ S RI R 50\n")
|
||||
|
@ -141,8 +170,12 @@ class TestTouchstoneTouchstone(unittest.TestCase):
|
|||
lines = ts.saves(4).splitlines()
|
||||
self.assertEqual(len(lines), 1021)
|
||||
self.assertEqual(lines[0], "# HZ S RI R 50")
|
||||
self.assertEqual(lines[1], '500000 -0.333238 0.000180018 0.67478 -8.1951e-07 0.67529 -8.20129e-07 -0.333238 0.000308078')
|
||||
self.assertEqual(lines[-1], '900000000 -0.127646 0.31969 0.596287 -0.503453 0.599076 -0.50197 -0.122713 0.326965')
|
||||
self.assertEqual(lines[1],
|
||||
'500000 -0.333238 0.000180018 0.67478 -8.1951e-07'
|
||||
' 0.67529 -8.20129e-07 -0.333238 0.000308078')
|
||||
self.assertEqual(lines[-1],
|
||||
'900000000 -0.127646 0.31969 0.596287 -0.503453'
|
||||
' 0.599076 -0.50197 -0.122713 0.326965')
|
||||
ts.filename = "./test/data/output.s2p"
|
||||
ts.save(4)
|
||||
os.remove(ts.filename)
|
||||
|
|
Ładowanie…
Reference in New Issue