Allow Touchstone files for calibration (#727)

* Update Calibration.py
* Update Touchstone.py
* Update CalibrationSettings.py
pull/730/head
EnPassant123 2024-11-28 11:16:37 -08:00 zatwierdzone przez GitHub
rodzic c8952a26fa
commit ee167e56e1
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
3 zmienionych plików z 156 dodań i 25 usunięć

Wyświetl plik

@ -368,8 +368,12 @@ class Calibration:
logger.debug("Calibration correctly calculated.")
def gamma_short(self, freq: int) -> complex:
if self.cal_element.short_is_ideal:
if self.cal_element.short_state == "IDEAL":
return IDEAL_SHORT
if self.cal_element.short_state == "FILE":
self.cal_element.short_touchstone.gen_interpolation_s11()
dp = self.cal_element.short_touchstone.s_freq("11", freq)
return complex(dp.re, dp.im)
logger.debug("Using short calibration set values.")
cal_element = self.cal_element
Zsp = complex(
@ -394,8 +398,12 @@ class Calibration:
)
def gamma_open(self, freq: int) -> complex:
if self.cal_element.open_is_ideal:
if self.cal_element.open_state == "IDEAL":
return IDEAL_OPEN
if self.cal_element.open_state == "FILE":
self.cal_element.open_touchstone.gen_interpolation_s11()
dp = self.cal_element.open_touchstone.s_freq("11", freq)
return complex(dp.re, dp.im)
logger.debug("Using open calibration set values.")
cal_element = self.cal_element
Zop = complex(
@ -415,8 +423,12 @@ class Calibration:
)
def gamma_load(self, freq: int) -> complex:
if self.cal_element.load_is_ideal:
if self.cal_element.load_state == "IDEAL":
return IDEAL_LOAD
if self.cal_element.load_state == "FILE":
self.cal_element.load_touchstone.gen_interpolation_s11()
dp = self.cal_element.load_touchstone.s_freq("11", freq)
return complex(dp.re, dp.im)
logger.debug("Using load calibration set values.")
cal_element = self.cal_element
Zl = complex(cal_element.load_r, 0.0)
@ -442,7 +454,7 @@ class Calibration:
cal_element = self.cal_element
return cmath.exp(
complex(0.0, -2.0 * math.pi * cal_element.through_length * freq)
)
)
def gen_interpolation(self):
(freq, e00, e11, delta_e, e10e01, e30, e22, e10e32) = zip(

Wyświetl plik

@ -187,6 +187,34 @@ class Touchstone:
fill_value=(imag[0], imag[-1]),
),
}
def gen_interpolation_s11(self):
freq = []
real = []
imag = []
for dp in self.s("11"):
freq.append(dp.freq)
real.append(dp.re)
imag.append(dp.im)
self._interp["11"] = {
"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:

Wyświetl plik

@ -25,6 +25,8 @@ from PyQt6 import QtWidgets, QtCore, QtGui
from NanoVNASaver.Calibration import Calibration
from NanoVNASaver.Settings.Sweep import SweepMode
from NanoVNASaver.Windows.Defaults import make_scrollable
from NanoVNASaver.Touchstone import Touchstone
logger = logging.getLogger(__name__)
@ -165,11 +167,40 @@ class CalibrationWindow(QtWidgets.QWidget):
cal_standard_box = QtWidgets.QGroupBox("Calibration standards")
cal_standard_layout = QtWidgets.QFormLayout(cal_standard_box)
self.use_ideal_values = QtWidgets.QCheckBox("Use ideal values")
self.use_ideal_values.setChecked(True)
self.use_ideal_values.stateChanged.connect(self.idealCheckboxChanged)
cal_standard_layout.addRow(self.use_ideal_values)
self.use_ideal_values = QtWidgets.QRadioButton("Use ideal values")
self.use_s1p_files = QtWidgets.QRadioButton("Use s1p files")
self.use_coefficients = QtWidgets.QRadioButton("Use coefficients")
self.use_ideal_values.setChecked(True)
self.radio_group = QtWidgets.QButtonGroup(self)
self.radio_group.addButton(self.use_ideal_values)
self.radio_group.addButton(self.use_s1p_files)
self.radio_group.addButton(self.use_coefficients)
self.radio_group.buttonClicked.connect(self.calStandardChanged)
self.radio_layout = QtWidgets.QHBoxLayout()
self.radio_layout.addWidget(self.use_ideal_values)
self.radio_layout.addWidget(self.use_s1p_files)
self.radio_layout.addWidget(self.use_coefficients)
cal_standard_layout.addRow(self.radio_layout)
self.file_button_short = QtWidgets.QPushButton("Short S1P file")
self.file_button_open = QtWidgets.QPushButton("Open S1P file")
self.file_button_load = QtWidgets.QPushButton("Load S1P file")
self.file_button_short.setEnabled(False)
self.file_button_open.setEnabled(False)
self.file_button_load.setEnabled(False)
cal_standard_layout.addRow(self.file_button_short)
cal_standard_layout.addRow(self.file_button_open)
cal_standard_layout.addRow(self.file_button_load)
self.file_button_open.clicked.connect(self.select_file_open)
self.file_button_short.clicked.connect(self.select_file_short)
self.file_button_load.clicked.connect(self.select_file_load)
self.cal_short_box = QtWidgets.QGroupBox("Short")
cal_short_form = QtWidgets.QFormLayout(self.cal_short_box)
self.cal_short_box.setDisabled(True)
@ -188,7 +219,8 @@ class CalibrationWindow(QtWidgets.QWidget):
cal_short_form.addRow("L2 (H(e-33))", self.short_l2_input)
cal_short_form.addRow("L3 (H(e-42))", self.short_l3_input)
cal_short_form.addRow("Offset Delay (ps)", self.short_length)
self.cal_open_box = QtWidgets.QGroupBox("Open")
cal_open_form = QtWidgets.QFormLayout(self.cal_open_box)
self.cal_open_box.setDisabled(True)
@ -208,6 +240,7 @@ class CalibrationWindow(QtWidgets.QWidget):
cal_open_form.addRow("C3 (F(e-45))", self.open_c3_input)
cal_open_form.addRow("Offset Delay (ps)", self.open_length)
self.cal_load_box = QtWidgets.QGroupBox("Load")
cal_load_form = QtWidgets.QFormLayout(self.cal_load_box)
self.cal_load_box.setDisabled(True)
@ -225,6 +258,8 @@ class CalibrationWindow(QtWidgets.QWidget):
cal_load_form.addRow("Capacitance (F(e-15))", self.load_capacitance)
cal_load_form.addRow("Offset Delay (ps)", self.load_length)
self.cal_through_box = QtWidgets.QGroupBox("Through")
cal_through_form = QtWidgets.QFormLayout(self.cal_through_box)
self.cal_through_box.setDisabled(True)
@ -264,6 +299,9 @@ class CalibrationWindow(QtWidgets.QWidget):
cal_standard_layout.addWidget(self.cal_standard_save_box)
right_layout.addWidget(cal_standard_box)
self.open_touchstone = None
self.short_touchstone = None
self.load_touchstone = None
def checkExpertUser(self):
if not self.app.settings.value("ExpertCalibrationUser", False, bool):
@ -499,6 +537,9 @@ class CalibrationWindow(QtWidgets.QWidget):
self.calibration_status_label.setText("Device calibration")
self.calibration_source_label.setText("Device")
self.notes_textedit.clear()
self.short_touchstone = None
self.open_touchstone = None
self.load_touchstone = None
if len(self.app.worker.rawData11) > 0:
# There's raw data, so we can get corrected data
@ -546,16 +587,16 @@ class CalibrationWindow(QtWidgets.QWidget):
)
return
cal_element.short_is_ideal = True
cal_element.open_is_ideal = True
cal_element.load_is_ideal = True
cal_element.short_state = "IDEAL"
cal_element.open_state = "IDEAL"
cal_element.load_state = "IDEAL"
cal_element.through_is_ideal = True
# TODO: all ideal or not?
if not self.use_ideal_values.isChecked():
cal_element.short_is_ideal = False
cal_element.open_is_ideal = False
cal_element.load_is_ideal = False
if self.radio_group.checkedButton() == self.use_coefficients:
cal_element.short_state = "COEFF"
cal_element.open_state = "COEFF"
cal_element.load_state = "COEFF"
cal_element.through_is_ideal = False
# We are using custom calibration standards
@ -606,6 +647,20 @@ class CalibrationWindow(QtWidgets.QWidget):
cal_element.through_length = (
getFloatValue(self.through_length.text()) / 1.0e12
)
elif self.radio_group.checkedButton() == self.use_s1p_files:
if (self.short_touchstone is not None):
cal_element.short_state = "FILE"
cal_element.short_touchstone = self.short_touchstone
if (self.open_touchstone is not None):
cal_element.open_state = "FILE"
cal_element.open_touchstone = self.open_touchstone
if (self.load_touchstone is not None):
cal_element.load_state = "FILE"
cal_element.load_touchstone = self.load_touchstone
cal_element.through_is_ideal = False
cal_element.through_length = (
getFloatValue(self.through_length.text()) / 1.0e12
)
logger.debug("Attempting calibration calculation.")
try:
@ -704,15 +759,34 @@ class CalibrationWindow(QtWidgets.QWidget):
logger.error("Calibration save failed!")
self.app.showError("Calibration save failed.")
def idealCheckboxChanged(self):
self.cal_short_box.setDisabled(self.use_ideal_values.isChecked())
self.cal_open_box.setDisabled(self.use_ideal_values.isChecked())
self.cal_load_box.setDisabled(self.use_ideal_values.isChecked())
self.cal_through_box.setDisabled(self.use_ideal_values.isChecked())
self.cal_standard_save_box.setDisabled(
self.use_ideal_values.isChecked()
)
def calStandardChanged(self, button):
if button == self.use_ideal_values:
self.cal_short_box.setDisabled(True)
self.cal_open_box.setDisabled(True)
self.cal_load_box.setDisabled(True)
self.cal_through_box.setDisabled(True)
self.cal_standard_save_box.setDisabled(True)
self.file_button_short.setDisabled(True)
self.file_button_open.setDisabled(True)
self.file_button_load.setDisabled(True)
elif button == self.use_s1p_files:
self.cal_short_box.setDisabled(True)
self.cal_open_box.setDisabled(True)
self.cal_load_box.setDisabled(True)
self.cal_through_box.setDisabled(False)
self.cal_standard_save_box.setDisabled(True)
self.file_button_short.setDisabled(False)
self.file_button_open.setDisabled(False)
self.file_button_load.setDisabled(False)
elif button == self.use_coefficients:
self.cal_short_box.setDisabled(False)
self.cal_open_box.setDisabled(False)
self.cal_load_box.setDisabled(False)
self.cal_through_box.setDisabled(False)
self.cal_standard_save_box.setDisabled(False)
self.file_button_short.setDisabled(True)
self.file_button_open.setDisabled(True)
self.file_button_load.setDisabled(True)
def automaticCalibration(self):
self.btn_automatic.setDisabled(True)
introduction = QtWidgets.QMessageBox(
@ -970,3 +1044,20 @@ class CalibrationWindow(QtWidgets.QWidget):
self.automaticCalibrationStep
)
return
def select_file_open(self):
filename, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Select Open S1P", "", "Touchstone Files (*.s1p)")
if filename != "":
self.open_touchstone = Touchstone(filename)
self.open_touchstone.load()
def select_file_short(self):
filename, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Select Short S1P", "", "Touchstone Files (*.s1p)")
if filename != "":
self.short_touchstone = Touchstone(filename)
self.short_touchstone.load()
def select_file_load(self):
filename, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Select Load S1P", "", "Touchstone Files (*.s1p)")
if filename != "":
self.load_touchstone = Touchstone(filename)
self.load_touchstone.load()