Added information on calibration.

This commit is contained in:
jupfi 2023-08-10 09:07:51 +02:00
parent ae3a42f869
commit 6a36c9db5f
3 changed files with 79 additions and 30 deletions

View file

@ -121,6 +121,14 @@ class AutoTMController(ModuleController):
def calculate_calibration(self) -> None:
"""This method is called when the calculate calibration button is pressed.
It calculates the calibration from the short, open and calibration data points.
@TODO: Make calibration useful. Right now the calibration does not work for the probe coils. It completly messes up the S11 data.
For 50 Ohm reference loads the calibration makes the S11 data usable - one then gets a flat line at -50 dB.
The problem is probably two things:
1. The ideal values for open, short and load should be measured with a VNA and then be loaded for the calibration.
The ideal values are probably not -1, 1 and 0 but will also show frequency dependent behaviour.
2 The AD8302 chip only returns the absolute value of the phase. One would probably need to calculate the phase with various algorithms found in the literature.
Though Im not sure if these proposed algorithms would work for the AD8302 chip.
"""
logger.debug("Calculating calibration")
# First we check if the short and open calibration data points are available
@ -143,26 +151,40 @@ class AutoTMController(ModuleController):
measured_gamma_open = self.module.model.open_calibration.gamma
measured_gamma_load = self.module.model.load_calibration.gamma
e_00s = []
e11s = []
delta_es = []
E_Ds = []
E_Ss = []
E_ts = []
for gamma_s, gamma_o, gamma_l in zip(measured_gamma_short, measured_gamma_open, measured_gamma_load):
A = np.array([
[1, ideal_gamma_short * gamma_s, -ideal_gamma_short],
[1, ideal_gamma_open * gamma_o, -ideal_gamma_open],
[1, ideal_gamma_load * gamma_l, -ideal_gamma_load]
])
# This is the solution from
# A = np.array([
# [1, ideal_gamma_short * gamma_s, -ideal_gamma_short],
# [1, ideal_gamma_open * gamma_o, -ideal_gamma_open],
# [1, ideal_gamma_load * gamma_l, -ideal_gamma_load]
# ])
B = np.array([gamma_s, gamma_o, gamma_l])
# B = np.array([gamma_s, gamma_o, gamma_l])
# Solve the system
e_00, e11, delta_e = np.linalg.lstsq(A, B, rcond=None)[0]
# e_00, e11, delta_e = np.linalg.lstsq(A, B, rcond=None)[0]
e_00s.append(e_00)
e11s.append(e11)
delta_es.append(delta_e)
E_D = gamma_l
E_ = (2 * gamma_l - (gamma_s + gamma_o)) / (gamma_s - gamma_o)
E_S = (2 * (gamma_o + gamma_l) * (gamma_s + gamma_l)) / (gamma_s - gamma_o)
self.module.model.calibration = (e_00s, e11s, delta_es)
E_Ds.append(E_D)
E_Ss.append(E_S)
E_ts.append(E_)
# e_00 = gamma_l # Because here the reflection coefficient should be 0
# e11 = (gamma_o + gamma_o - 2 * e_00) / (gamma_o - gamma_s)
# delta_e = -gamma_o + gamma_o* e11 + e_00
# e_00s.append(e_00)
# e11s.append(e11)
# delta_es.append(delta_e)
self.module.model.calibration = (E_Ds, E_Ss, E_ts)
def export_calibration(self, filename: str) -> None:
"""This method is called when the export calibration button is pressed.

View file

@ -40,7 +40,12 @@ class S11Data:
@property
def gamma(self):
"""Complex reflection coefficient"""
return map(cmath.rect, (10 ** (-self.return_loss_db / 20), self.phase_rad))
if len(self.return_loss_db) != len(self.phase_rad):
raise ValueError("return_loss_db and phase_rad must be the same length")
return [cmath.rect(10 ** (-loss_db / 20), phase_rad) for loss_db, phase_rad in zip(self.return_loss_db, self.phase_rad)]
def to_json(self):
return {
@ -72,6 +77,7 @@ class AutoTMModel(ModuleModel):
super().__init__(module)
self.data_points = []
self.active_calibration = None
self.calibration = None
@property
def available_devices(self):

View file

@ -126,27 +126,48 @@ class AutoTMView(ModuleView):
Args:
data_points (list): List of data points to plot.
@TODO: implement proper calibration. See the controller class for more information.
"""
frequency = data.frequency
return_loss_db = data.return_loss_db
phase = data.phase_deg
gamma = data.gamma
# Calibration test:
#calibration = self.module.model.calibration
#e_00 = calibration[0]
#e11 = calibration[1]
#delta_e = calibration[2]
# Plot complex reflection coefficient
""" import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([g.real for g in gamma], [g.imag for g in gamma])
ax.set_aspect('equal')
ax.grid(True)
ax.set_title("Complex reflection coefficient")
ax.set_xlabel("Real")
ax.set_ylabel("Imaginary")
plt.show()
"""
#y_corr = [(data_point - e_00[i]) / (data_point * e11[i] - delta_e[i]) for i, data_point in enumerate(y)]
#import numpy as np
#y = [data_point[1] for data_point in self.module.model.data_points]
#open_calibration = [data_point[1] for data_point in self.module.model.open_calibration]
#load_calibration = [data_point[1] for data_point in self.module.model.load_calibration]
#short_calibration = [data_point[1] for data_point in self.module.model.short_calibration]
magnitude_ax = self._ui_form.S11Plot.canvas.ax
# @ TODO: implement proper calibration
if self.module.model.calibration is not None:
# Calibration test:
import cmath
calibration = self.module.model.calibration
E_D = calibration[0]
E_S = calibration[1]
E_t = calibration[2]
#y_corr = np.array(y) - np.array(load_calibration)
#y_corr = y_corr - np.mean(y_corr)
# gamma_corr = [(data_point - e_00[i]) / (data_point * e11[i] - delta_e[i]) for i, data_point in enumerate(gamma)]
gamma_corr = [(data_point - E_D[i]) / (E_S[i] * (data_point - E_D[i]) + E_t[i]) for i, data_point in enumerate(gamma)]
""" fig, ax = plt.subplots()
ax.plot([g.real for g in gamma_corr], [g.imag for g in gamma_corr])
ax.set_aspect('equal')
ax.grid(True)
ax.set_title("Complex reflection coefficient")
ax.set_xlabel("Real")
ax.set_ylabel("Imaginary")
plt.show() """
return_loss_db_corr = [-20 * cmath.log10(abs(g + 1e-12)) for g in gamma_corr]
magnitude_ax.plot(frequency, return_loss_db_corr, color="red")
phase_ax = self._ui_form.S11Plot.canvas.ax.twinx()
phase_ax.set_ylabel("Phase (deg)")
@ -154,7 +175,7 @@ class AutoTMView(ModuleView):
phase_ax.set_ylim(-180, 180)
phase_ax.invert_yaxis()
magnitude_ax = self._ui_form.S11Plot.canvas.ax
magnitude_ax.clear()
magnitude_ax.set_xlabel("Frequency (MHz)")
magnitude_ax.set_ylabel("S11 (dB)")