2-port SOLT cal
This commit is contained in:
@ -361,7 +361,7 @@ class Charon:
|
|||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def calibrate_sol(self, prompt: Callable[[str], None] | None = None, **kwargs) -> rf.calibration.Calibration:
|
def calibrate_sol(self, prompt: Callable[[str], None] | None = None, **kwargs) -> None:
|
||||||
if len(self.ports) != 1:
|
if len(self.ports) != 1:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"SOL calibration needs only one port but {len(self.ports)} ports are enabled. "
|
f"SOL calibration needs only one port but {len(self.ports)} ports are enabled. "
|
||||||
@ -378,14 +378,55 @@ class Charon:
|
|||||||
|
|
||||||
measured = list()
|
measured = list()
|
||||||
for name in names:
|
for name in names:
|
||||||
prompt(f"Connect standard: {name} to port {self.ports[0]}")
|
prompt(f"Connect standard {name} to port {self.ports[0]}")
|
||||||
measured.append(self.capture(**kwargs))
|
measured.append(self.capture(**kwargs))
|
||||||
|
|
||||||
cal = rf.OnePort(ideals=ideals, measured=[s2net(m) for m in measured])
|
cal = rf.OnePort(measured=[s2net(m) for m in measured], ideals=ideals)
|
||||||
|
|
||||||
self.calibration = cal
|
self.calibration = cal
|
||||||
|
|
||||||
return cal
|
def calibrate_solt(self, prompt: Callable[[str], None] | None = None, **kwargs) -> None:
|
||||||
|
if len(self.ports) < 2:
|
||||||
|
raise ValueError(
|
||||||
|
f"SOLT calibration needs at least two ports but {len(self.ports)} ports are enabled. "
|
||||||
|
"Did you mean to use SOL?"
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(self.ports) > 2:
|
||||||
|
raise NotImplementedError("SOLT calibration with more than two ports not yet supported")
|
||||||
|
|
||||||
|
if prompt is None:
|
||||||
|
prompt = lambda s: input(f"{s}\nENTER to continue...")
|
||||||
|
|
||||||
|
ideal = rf.media.DefinedGammaZ0(frequency=rf.media.Frequency.from_f(self.frequency, unit="Hz"))
|
||||||
|
ideals = [ideal.short(), ideal.open(), ideal.load(0)]
|
||||||
|
ideals = [rf.two_port_reflect(id, id) for id in ideals]
|
||||||
|
|
||||||
|
thru = np.zeros((len(self.frequency), 2, 2), dtype=np.complex128)
|
||||||
|
thru[:, 0, 1] = 1
|
||||||
|
thru[:, 1, 0] = 1
|
||||||
|
thru = rf.Network(frequency=self.frequency, f_unit="Hz", s=thru)
|
||||||
|
|
||||||
|
ideals.append(thru)
|
||||||
|
|
||||||
|
names_1p = ["short", "open", "load"]
|
||||||
|
names_2p = ["thru"]
|
||||||
|
|
||||||
|
measured = list()
|
||||||
|
for name in names_1p:
|
||||||
|
measured_param = list()
|
||||||
|
for port in self.ports:
|
||||||
|
prompt(f"Connect standard {name} to port {port}")
|
||||||
|
measured_param.append(self.capture(measurements=[(port, port)], **kwargs).sel(m=port, n=port))
|
||||||
|
measured.append(rf.two_port_reflect(*[s2net(m) for m in measured_param]))
|
||||||
|
|
||||||
|
for name in names_2p:
|
||||||
|
prompt(f"Connect standard {name} between ports {self.ports[0]} and {self.ports[1]}")
|
||||||
|
measured.append(s2net(self.capture(**kwargs)))
|
||||||
|
|
||||||
|
cal = rf.SOLT(measured=measured, ideals=ideals)
|
||||||
|
|
||||||
|
self.calibration = cal
|
||||||
|
|
||||||
def save_calibration(self, path: Path | str):
|
def save_calibration(self, path: Path | str):
|
||||||
path = Path(path)
|
path = Path(path)
|
||||||
|
@ -9,7 +9,7 @@ from charon_vna.vna import Charon
|
|||||||
frequency = np.linspace(80e6, 280e6, 301)
|
frequency = np.linspace(80e6, 280e6, 301)
|
||||||
|
|
||||||
# %%
|
# %%
|
||||||
vna = Charon(frequency=frequency, ports=1)
|
vna = Charon(frequency=frequency, ports=2)
|
||||||
|
|
||||||
# %%
|
# %%
|
||||||
s = vna.capture()
|
s = vna.capture()
|
||||||
@ -26,11 +26,19 @@ plt.show()
|
|||||||
# %%
|
# %%
|
||||||
vna.calibrate_sol()
|
vna.calibrate_sol()
|
||||||
|
|
||||||
|
# %%
|
||||||
|
vna.calibrate_solt()
|
||||||
|
|
||||||
|
# %%
|
||||||
|
vna.save_calibration("./calibration.pkl")
|
||||||
|
|
||||||
# %%
|
# %%
|
||||||
vna.load_calibration("./calibration.pkl")
|
vna.load_calibration("./calibration.pkl")
|
||||||
|
|
||||||
# %%
|
# %%
|
||||||
s2 = net2s(vna.calibration.apply_cal(s2net(s)))
|
s2 = net2s(vna.calibration.apply_cal(s2net(s)))
|
||||||
|
# s2.coords["m"] = s.m
|
||||||
|
# s2.coords["n"] = s.n
|
||||||
|
|
||||||
for m in s.m.data:
|
for m in s.m.data:
|
||||||
for n in s.n.data:
|
for n in s.n.data:
|
||||||
@ -39,12 +47,22 @@ for m in s.m.data:
|
|||||||
plt.grid(True)
|
plt.grid(True)
|
||||||
plt.legend()
|
plt.legend()
|
||||||
plt.ylabel("Magnitude [dB]")
|
plt.ylabel("Magnitude [dB]")
|
||||||
|
# plt.ylim(-30, 5)
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
for m in s.m.data:
|
for m in s.m.data:
|
||||||
for n in s.n.data:
|
for n in s.n.data:
|
||||||
plt.plot(s.frequency, np.angle(s.sel(m=m, n=n), deg=True), label="$S_{" + str(m) + str(n) + "}$ (uncalibrated)")
|
if m != n:
|
||||||
plt.plot(s2.frequency, np.angle(s2.sel(m=m, n=n), deg=True), label="$S_{" + str(m) + str(n) + "}$ (calibrated)")
|
plt.plot(
|
||||||
|
s.frequency,
|
||||||
|
np.angle(s.sel(m=m, n=n), deg=True),
|
||||||
|
label="$S_{" + str(m) + str(n) + "}$ (uncalibrated)",
|
||||||
|
)
|
||||||
|
plt.plot(
|
||||||
|
s2.frequency,
|
||||||
|
np.angle(s2.sel(m=m, n=n), deg=True),
|
||||||
|
label="$S_{" + str(m) + str(n) + "}$ (calibrated)",
|
||||||
|
)
|
||||||
plt.grid(True)
|
plt.grid(True)
|
||||||
plt.legend()
|
plt.legend()
|
||||||
plt.ylabel("Phase [deg]")
|
plt.ylabel("Phase [deg]")
|
||||||
|
Reference in New Issue
Block a user