Skip to content

Commit

Permalink
Allow Touchstone files for calibration (NanoVNA-Saver#727)
Browse files Browse the repository at this point in the history
* Update Calibration.py
* Update Touchstone.py
* Update CalibrationSettings.py
  • Loading branch information
EnPassant123 authored and zarath committed Nov 30, 2024
1 parent 79d953c commit 3786631
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 25 deletions.
20 changes: 16 additions & 4 deletions src/NanoVNASaver/Calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,8 +368,12 @@ def calc_corrections(self):
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(
Expand All @@ -394,8 +398,12 @@ def gamma_short(self, freq: int) -> complex:
)

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(
Expand All @@ -415,8 +423,12 @@ def gamma_open(self, freq: int) -> complex:
)

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)
Expand All @@ -442,7 +454,7 @@ def gamma_through(self, freq: int) -> complex:
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(
Expand Down
28 changes: 28 additions & 0 deletions src/NanoVNASaver/Touchstone.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,34 @@ def gen_interpolation(self):
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:
Expand Down
133 changes: 112 additions & 21 deletions src/NanoVNASaver/Windows/CalibrationSettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
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__)

Expand Down Expand Up @@ -165,11 +167,40 @@ def __init__(self, app: 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)
Expand All @@ -188,7 +219,8 @@ def __init__(self, app: 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)
Expand All @@ -208,6 +240,7 @@ def __init__(self, app: 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)
Expand All @@ -225,6 +258,8 @@ def __init__(self, app: 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)
Expand Down Expand Up @@ -264,6 +299,9 @@ def __init__(self, app: 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):
Expand Down Expand Up @@ -499,6 +537,9 @@ def reset(self):
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
Expand Down Expand Up @@ -546,16 +587,16 @@ def calculate(self):
)
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
Expand Down Expand Up @@ -606,6 +647,20 @@ def calculate(self):
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:
Expand Down Expand Up @@ -704,15 +759,34 @@ def saveCalibration(self):
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(
Expand Down Expand Up @@ -970,3 +1044,20 @@ def automaticCalibrationStep(self):
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()

0 comments on commit 3786631

Please sign in to comment.