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: def calculate_calibration(self) -> None:
"""This method is called when the calculate calibration button is pressed. """This method is called when the calculate calibration button is pressed.
It calculates the calibration from the short, open and calibration data points. 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") logger.debug("Calculating calibration")
# First we check if the short and open calibration data points are available # 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_open = self.module.model.open_calibration.gamma
measured_gamma_load = self.module.model.load_calibration.gamma measured_gamma_load = self.module.model.load_calibration.gamma
e_00s = [] E_Ds = []
e11s = [] E_Ss = []
delta_es = [] E_ts = []
for gamma_s, gamma_o, gamma_l in zip(measured_gamma_short, measured_gamma_open, measured_gamma_load): for gamma_s, gamma_o, gamma_l in zip(measured_gamma_short, measured_gamma_open, measured_gamma_load):
A = np.array([ # This is the solution from
[1, ideal_gamma_short * gamma_s, -ideal_gamma_short], # A = np.array([
[1, ideal_gamma_open * gamma_o, -ideal_gamma_open], # [1, ideal_gamma_short * gamma_s, -ideal_gamma_short],
[1, ideal_gamma_load * gamma_l, -ideal_gamma_load] # [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 # 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) E_D = gamma_l
e11s.append(e11) E_ = (2 * gamma_l - (gamma_s + gamma_o)) / (gamma_s - gamma_o)
delta_es.append(delta_e) 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: def export_calibration(self, filename: str) -> None:
"""This method is called when the export calibration button is pressed. """This method is called when the export calibration button is pressed.

View file

@ -40,7 +40,12 @@ class S11Data:
@property @property
def gamma(self): def gamma(self):
"""Complex reflection coefficient""" """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): def to_json(self):
return { return {
@ -72,6 +77,7 @@ class AutoTMModel(ModuleModel):
super().__init__(module) super().__init__(module)
self.data_points = [] self.data_points = []
self.active_calibration = None self.active_calibration = None
self.calibration = None
@property @property
def available_devices(self): def available_devices(self):

View file

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