mirror of
https://github.com/nqrduck/nqrduck-autotm.git
synced 2024-11-09 11:40:02 +00:00
Added information on calibration.
This commit is contained in:
parent
ae3a42f869
commit
6a36c9db5f
3 changed files with 79 additions and 30 deletions
|
@ -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.
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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()
|
||||
"""
|
||||
|
||||
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)]
|
||||
#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]
|
||||
|
||||
#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)")
|
||||
|
|
Loading…
Reference in a new issue