kopia lustrzana https://github.com/NanoVNA-Saver/nanovna-saver
Allow Touchstone files for calibration (#727)
* Update Calibration.py * Update Touchstone.py * Update CalibrationSettings.pypull/730/head
rodzic
c8952a26fa
commit
ee167e56e1
|
@ -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(
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
|
Ładowanie…
Reference in New Issue