diff --git a/NanoVNASaver/Calibration.py b/NanoVNASaver/Calibration.py index 53f47ad..d20f94a 100644 --- a/NanoVNASaver/Calibration.py +++ b/NanoVNASaver/Calibration.py @@ -1,4 +1,5 @@ -# NanoVNASaver - a python program to view and export Touchstone data from a NanoVNA +# NanoVNASaver +# A python program to view and export Touchstone data from a NanoVNA # Copyright (C) 2019. Rune B. Broberg # # This program is free software: you can redistribute it and/or modify @@ -13,94 +14,138 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - import logging import math import os +import re from typing import List import numpy as np from .RFTools import Datapoint +RXP_CAL_LINE = re.compile(r"""^\s* + (?P\d+) \s+ + (?P[-0-9Ee.]+) \s+ (?P[-0-9Ee.]+) \s+ + (?P[-0-9Ee.]+) \s+ (?P[-0-9Ee.]+) \s+ + (?P[-0-9Ee.]+) \s+ (?P[-0-9Ee.]+)(?: \s + (?P[-0-9Ee.]+) \s+ (?P[-0-9Ee.]+) \s+ + (?P[-0-9Ee.]+) \s+ (?P[-0-9Ee.]+) + )? +""", re.VERBOSE) + logger = logging.getLogger(__name__) +# TODO: make a real class of calibration class Calibration: - notes = [] - s11short: List[Datapoint] = [] - s11open: List[Datapoint] = [] - s11load: List[Datapoint] = [] - s21through: List[Datapoint] = [] - s21isolation: List[Datapoint] = [] + _CAL_NAMES = ("short", "open", "load", "through", "isolation",) - frequencies = [] + def __init__(self): - # 1-port - e00 = [] # Directivity - e11 = [] # Port match - deltaE = [] # Tracking + self.notes = [] + self.cals = {} + self._reset_cals() + self.frequencies = [] + # 1-port + self.e00 = [] # Directivity + self.e11 = [] # Port match + self.deltaE = [] # Tracking - # 2-port - e30 = [] # Port match - e10e32 = [] # Transmission + # 2-port + self.e30 = [] # Port match + self.e10e32 = [] # Transmission - shortIdeal = np.complex(-1, 0) - useIdealShort = True - shortL0 = 5.7 * 10E-12 - shortL1 = -8960 * 10E-24 - shortL2 = -1100 * 10E-33 - shortL3 = -41200 * 10E-42 - shortLength = -34.2 # Picoseconds - # These numbers look very large, considering what Keysight suggests their numbers are. + self.shortIdeal = np.complex(-1, 0) + self.useIdealShort = True + self.shortL0 = 5.7 * 10E-12 + self.shortL1 = -8960 * 10E-24 + self.shortL2 = -1100 * 10E-33 + self.shortL3 = -41200 * 10E-42 + self.shortLength = -34.2 # Picoseconds + # These numbers look very large, considering what Keysight suggests their numbers are. - useIdealOpen = True - openIdeal = np.complex(1, 0) - openC0 = 2.1 * 10E-14 # Subtract 50fF for the nanoVNA calibration if nanoVNA is calibrated? - openC1 = 5.67 * 10E-23 - openC2 = -2.39 * 10E-31 - openC3 = 2.0 * 10E-40 - openLength = 0 + self.useIdealOpen = True + self.openIdeal = np.complex(1, 0) + self.openC0 = 2.1 * 10E-14 # Subtract 50fF for the nanoVNA calibration if nanoVNA is calibrated? + self.openC1 = 5.67 * 10E-23 + self.openC2 = -2.39 * 10E-31 + self.openC3 = 2.0 * 10E-40 + self.openLength = 0 - useIdealLoad = True - loadR = 25 - loadL = 0 - loadC = 0 - loadLength = 0 - loadIdeal = np.complex(0, 0) + self.useIdealLoad = True + self.loadR = 25 + self.loadL = 0 + self.loadC = 0 + self.loadLength = 0 + self.loadIdeal = np.complex(0, 0) - useIdealThrough = True - throughLength = 0 + self.useIdealThrough = True + self.throughLength = 0 - isCalculated = False + self.isCalculated = False - source = "Manual" + self.source = "Manual" - def isValid2Port(self): - valid = len(self.s21through) > 0 and len(self.s21isolation) > 0 and self.isValid1Port() - valid &= len(self.s21through) == len(self.s21isolation) == len(self.s11short) - return valid + def _reset_cals(self): + for name in Calibration._CAL_NAMES: + self.cals[name] = [] + + @property + def s11short(self) -> List[Datapoint]: + return self.cals["short"] + @s11short.setter + def s11short(self, values: List[Datapoint]): + self.cals["short"] = values + @property + def s11open(self) -> List[Datapoint]: + return self.cals["open"] + @s11open.setter + def s11open(self, values: List[Datapoint]): + self.cals["open"] = values + @property + def s11load(self) -> List[Datapoint]: + return self.cals["load"] + @s11load.setter + def s11load(self, values: List[Datapoint]): + self.cals["load"] = values + @property + def s21through(self) -> List[Datapoint]: + return self.cals["through"] + @s21through.setter + def s21through(self, values: List[Datapoint]): + self.cals["through"] = values + @property + def s21isolation(self) -> List[Datapoint]: + return self.cals["isolation"] + @s21isolation.setter + def s21isolation(self, values: List[Datapoint]): + self.cals["isolation"] = values def isValid1Port(self): - valid = len(self.s11short) > 0 and len(self.s11open) > 0 and len(self.s11load) > 0 - valid &= len(self.s11short) == len(self.s11open) == len(self.s11load) - return valid + lengths = [len(self.cals[x]) + for x in Calibration._CAL_NAMES[:3]] + return min(lengths) > 0 and min(lengths) == max(lengths) - def calculateCorrections(self) -> (bool, str): + def isValid2Port(self): + lengths = [len(self.cals[x]) for x in Calibration._CAL_NAMES] + return min(lengths) > 0 and min(lengths) == max(lengths) + + def calc_corrections(self): if not self.isValid1Port(): - logger.warning("Tried to calibrate from insufficient data.") - if len(self.s11short) == 0 or len(self.s11open) == 0 or len(self.s11load) == 0: - return (False, - "All of short, open and load calibration steps" - "must be completed for calibration to be applied.") - return False, "All calibration data sets must be the same size." - self.frequencies = [int] * len(self.s11short) - self.e00 = [np.complex] * len(self.s11short) - self.e11 = [np.complex] * len(self.s11short) - self.deltaE = [np.complex] * len(self.s11short) - self.e30 = [np.complex] * len(self.s11short) - self.e10e32 = [np.complex] * len(self.s11short) - logger.debug("Calculating calibration for %d points.", len(self.s11short)) + logger.warning( + "Tried to calibrate from insufficient data.") + raise ValueError( + "All of short, open and load calibration steps" + "must be completed for calibration to be applied.") + nr_points = len(self.cals["short"]) + logger.debug("Calculating calibration for %d points.", nr_points) + self.frequencies = [] + self.e00 = [np.complex] * nr_points + self.e11 = [np.complex] * nr_points + self.deltaE = [np.complex] * nr_points + self.e30 = [np.complex] * nr_points + self.e10e32 = [np.complex] * nr_points if self.useIdealShort: logger.debug("Using ideal values.") else: @@ -109,9 +154,11 @@ class Calibration: logger.debug("Calculating 2-port calibration.") else: logger.debug("Calculating 1-port calibration.") - for i in range(len(self.s11short)): - self.frequencies[i] = self.s11short[i].freq - f = self.s11short[i].freq + for i, cur_short in enumerate(self.cals["short"]): + cur_open = self.cals["open"][i] + cur_load = self.cals["load"][i] + f = cur_short.freq + self.frequencies.append(f) pi = math.pi if self.useIdealShort: @@ -151,9 +198,9 @@ class Calibration: g3 = g3 * np.exp( np.complex(0, 1) * 2 * 2 * math.pi * f * self.loadLength * -1) - gm1 = np.complex(self.s11short[i].re, self.s11short[i].im) - gm2 = np.complex(self.s11open[i].re, self.s11open[i].im) - gm3 = np.complex(self.s11load[i].re, self.s11load[i].im) + gm1 = np.complex(cur_short.re, cur_short.im) + gm2 = np.complex(cur_open.re, cur_open.im) + gm3 = np.complex(cur_load.re, cur_load.im) try: denominator = ( @@ -181,52 +228,55 @@ class Calibration: " for two of short, open and load?") logger.debug( "Division error at index %d" - " Short == Load: %s" - " Short == Open: %s" - " Open == Load: %s", - i, - self.s11short[i] == self.s11load[i], - self.s11short[i] == self.s11open[i], - self.s11open[i] == self.s11load[i]) - return (self.isCalculated, - f"Two of short, open and load returned the same" - f" values at frequency {self.s11open[i].freq}Hz.") + " Short == Load: %s Short == Open: %s" + " Open == Load: %s", i, + cur_short == cur_load, cur_short == cur_open, + cur_open == cur_load) + raise ValueError( + f"Two of short, open and load returned the same" + f" values at frequency {f}Hz.") if self.isValid2Port(): + cur_through = self.cals["through"][i] + cur_isolation = self.cals["isolation"][i] + self.e30[i] = np.complex( - self.s21isolation[i].re, self.s21isolation[i].im) - s21m = np.complex(self.s21through[i].re, self.s21through[i].im) + cur_isolation.re, cur_isolation.im) + s21m = np.complex(cur_through.re, cur_through.im) if not self.useIdealThrough: gammaThrough = np.exp( - np.complex(0, 1) * 2 * math.pi * self.throughLength * f * -1) + np.complex(0, 1) * 2 * math.pi * + self.throughLength * f * -1) s21m = s21m / gammaThrough - self.e10e32[i] = (s21m - self.e30[i]) * (1 - (self.e11[i]*self.e11[i])) + self.e10e32[i] = (s21m - self.e30[i]) * ( + 1 - (self.e11[i] * self.e11[i])) self.isCalculated = True logger.debug("Calibration correctly calculated.") - return self.isCalculated, "Calibration successful." def correct11(self, re, im, freq): s11m = np.complex(re, im) distance = 10**10 index = 0 - for i in range(len(self.s11short)): - if abs(self.s11short[i].freq - freq) < distance: + for i, cur_short in enumerate(self.cals["short"]): + if abs(cur_short.freq - freq) < distance: index = i - distance = abs(self.s11short[i].freq - freq) - # TODO: Interpolate with the adjacent data point to get better corrections? + distance = abs(cur_short.freq - freq) + # TODO: Interpolate with the adjacent data point + # to get better corrections? - s11 = (s11m - self.e00[index]) / ((s11m * self.e11[index]) - self.deltaE[index]) + s11 = (s11m - self.e00[index]) / ( + (s11m * self.e11[index]) - self.deltaE[index]) return s11.real, s11.imag def correct21(self, re, im, freq): s21m = np.complex(re, im) distance = 10**10 index = 0 - for i in range(len(self.s21through)): - if abs(self.s21through[i].freq - freq) < distance: + for i, cur_through in enumerate(self.cals["through"]): + if abs(cur_through.freq - freq) < distance: index = i - distance = abs(self.s21through[i].freq - freq) + distance = abs(cur_through.freq - freq) s21 = (s21m - self.e30[index]) / self.e10e32[index] return s21.real, s21.imag @@ -242,104 +292,74 @@ class Calibration: output = input_val * np.exp(np.complex(0, 1) * 2 * math.pi * d.freq * delay * -1) return Datapoint(d.freq, output.real, output.imag) - def saveCalibration(self, filename): + # TODO: implement tests + def save(self, filename: str): # Save the calibration data to file - if filename == "" or not self.isValid1Port(): - return False - try: - file = open(filename, "w+") - file.write("# Calibration data for NanoVNA-Saver\n") + if not self.isValid1Port(): + raise ValueError("Not a valid 1-Port calibration") + with open(filename, "w+") as calfile: + calfile.write("# Calibration data for NanoVNA-Saver\n") for note in self.notes: - file.write(f"! {note}\n") - file.write( + calfile.write(f"! {note}\n") + calfile.write( "# Hz ShortR ShortI OpenR OpenI LoadR LoadI" " ThroughR ThroughI IsolationR IsolationI\n") - for i in range(len(self.s11short)): - freq = str(self.s11short[i].freq) - shortr = str(self.s11short[i].re) - shorti = str(self.s11short[i].im) - openr = str(self.s11open[i].re) - openi = str(self.s11open[i].im) - loadr = str(self.s11load[i].re) - loadi = str(self.s11load[i].im) - file.write(" ".join((freq, shortr, shorti, openr, openi, loadr, loadi))) + for i, cur_short in enumerate(self.cals["short"]): + data = [ + cur_short.freq, + cur_short.re, cur_short.im, + self.s11open[i].re, self.s11open[i].im, + self.s11load[i].re, self.s11load[i].im, + ] if self.isValid2Port(): - throughr = str(self.s21through[i].re) - throughi = str(self.s21through[i].im) - isolationr = str(self.s21isolation[i].re) - isolationi = str(self.s21isolation[i].im) - file.write(" ".join((throughr, throughi, isolationr, isolationi))) - file.write("\n") - file.close() - return True - except Exception as e: - logger.exception("Error saving calibration data: %s", e) - return False - - def loadCalibration(self, filename): - # Load calibration data from file - if filename == "": - return + data.extend([ + self.s21through[i].re, self.s21through[i].im, + self.s21isolation[i].re, self.s21isolation[i].im + ]) + calfile.write(" ".join([str(val) for val in data])) + calfile.write("\n") + # TODO: implement tests + # TODO: Exception should be catched by caller + def load(self, filename): self.source = os.path.basename(filename) - - self.s11short = [] - self.s11open = [] - self.s11load = [] - - self.s21through = [] - self.s21isolation = [] + self._reset_cals() self.notes = [] - try: - file = open(filename, "r") - lines = file.readlines() - parsed_header = False - - for line in lines: + parsed_header = False + with open(filename) as calfile: + for i, line in enumerate(calfile): line = line.strip() if line.startswith("!"): note = line[2:] self.notes.append(note) continue - if line.startswith("#") and not parsed_header: - # Check that this is a valid header - if line == ("# Hz ShortR ShortI OpenR OpenI LoadR LoadI" + if line.startswith("#"): + if not parsed_header: + # Check that this is a valid header + if line == ( + "# Hz ShortR ShortI OpenR OpenI LoadR LoadI" " ThroughR ThroughI IsolationR IsolationI"): - parsed_header = True + parsed_header = True continue if not parsed_header: logger.warning( - "Warning: Read line without having read header: %s", line) + "Warning: Read line without having read header: %s", + line) continue - try: - if line.count(" ") == 6: - freq, shortr, shorti, openr, openi, loadr, loadi = line.split( - " ") - self.s11short.append( - Datapoint(int(freq), float(shortr), float(shorti))) - self.s11open.append( - Datapoint(int(freq), float(openr), float(openi))) - self.s11load.append( - Datapoint(int(freq), float(loadr), float(loadi))) - else: - (freq, shortr, shorti, openr, openi, loadr, loadi, - throughr, throughi, isolationr, isolationi) = line.split(" ") - self.s11short.append( - Datapoint(int(freq), float(shortr), float(shorti))) - self.s11open.append( - Datapoint(int(freq), float(openr), float(openi))) - self.s11load.append( - Datapoint(int(freq), float(loadr), float(loadi))) - self.s21through.append( - Datapoint(int(freq), float(throughr), float(throughi))) - self.s21isolation.append( - Datapoint(int(freq), float(isolationr), float(isolationi))) + m = RXP_CAL_LINE.search(line) + if not m: + logger.warning("Illegal data in cal file. Line %i", i) + cal = m.groupdict() - except ValueError as e: - logger.exception( - "Error parsing calibration data \"%s\": %s", line, e) - file.close() - except Exception as e: - logger.exception("Failed loading calibration data: %s", e) + if cal["throughr"]: + nr_cals = 5 + else: + nr_cals = 3 + + for name in Calibration._CAL_NAMES[:nr_cals]: + self.cals[name].append( + Datapoint(int(cal["freq"]), + float(cal[f"{name}r"]), + float(cal[f"{name}i"]))) diff --git a/NanoVNASaver/Windows/CalibrationSettings.py b/NanoVNASaver/Windows/CalibrationSettings.py index ecc8392..bb15c53 100644 --- a/NanoVNASaver/Windows/CalibrationSettings.py +++ b/NanoVNASaver/Windows/CalibrationSettings.py @@ -24,6 +24,10 @@ from NanoVNASaver.Calibration import Calibration logger = logging.getLogger(__name__) +def _format_cal_label(data: list, prefix: str = "Set") -> str: + return f"{prefix} ({len(data)} points)" + + class CalibrationWindow(QtWidgets.QWidget): nextStep = -1 @@ -57,27 +61,15 @@ class CalibrationWindow(QtWidgets.QWidget): calibration_control_group = QtWidgets.QGroupBox("Calibrate") calibration_control_layout = QtWidgets.QFormLayout(calibration_control_group) - btn_cal_short = QtWidgets.QPushButton("Short") - btn_cal_short.clicked.connect(self.manualSaveShort) - self.cal_short_label = QtWidgets.QLabel("Uncalibrated") - - btn_cal_open = QtWidgets.QPushButton("Open") - btn_cal_open.clicked.connect(self.manualSaveOpen) - self.cal_open_label = QtWidgets.QLabel("Uncalibrated") - - btn_cal_load = QtWidgets.QPushButton("Load") - btn_cal_load.clicked.connect(self.manualSaveLoad) - self.cal_load_label = QtWidgets.QLabel("Uncalibrated") - - btn_cal_through = QtWidgets.QPushButton("Through") - btn_cal_through.clicked.connect(self.manualSaveThrough) - # btn_cal_through.setDisabled(True) - self.cal_through_label = QtWidgets.QLabel("Uncalibrated") - - btn_cal_isolation = QtWidgets.QPushButton("Isolation") - btn_cal_isolation.clicked.connect(self.manualSaveIsolation) - # btn_cal_isolation.setDisabled(True) - self.cal_isolation_label = QtWidgets.QLabel("Uncalibrated") + cal_btn = {} + self.cal_label = {} + for label_name in Calibration._CAL_NAMES: + self.cal_label[label_name] = QtWidgets.QLabel("Uncalibrated") + cal_btn[label_name] = QtWidgets.QPushButton( + label_name.capitalize()) + cal_btn[label_name].clicked.connect(lambda: self.manual_save(label_name)) + calibration_control_layout.addRow( + cal_btn[label_name], self.cal_label[label_name]) self.input_offset_delay = QtWidgets.QDoubleSpinBox() self.input_offset_delay.setValue(0) @@ -86,12 +78,6 @@ class CalibrationWindow(QtWidgets.QWidget): self.input_offset_delay.valueChanged.connect(self.setOffsetDelay) self.input_offset_delay.setRange(-10e6, 10e6) - calibration_control_layout.addRow(btn_cal_short, self.cal_short_label) - calibration_control_layout.addRow(btn_cal_open, self.cal_open_label) - calibration_control_layout.addRow(btn_cal_load, self.cal_load_label) - calibration_control_layout.addRow(btn_cal_isolation, self.cal_isolation_label) - calibration_control_layout.addRow(btn_cal_through, self.cal_through_label) - calibration_control_layout.addRow(QtWidgets.QLabel("")) calibration_control_layout.addRow("Offset delay", self.input_offset_delay) @@ -239,50 +225,14 @@ class CalibrationWindow(QtWidgets.QWidget): return False return True - def manualSaveShort(self): + def cal_save(self, name): + self.app.calibration.cals[name] = self.app.data + self.cal_label[name].setText( + _format_cal_label(self.app.data)) + + def manual_save(self, name): if self.checkExpertUser(): - self.saveShort() - - def saveShort(self): - self.app.calibration.s11short = self.app.data - self.cal_short_label.setText( - f"Data set ({self.app.calibration.s11short} points)") - - def manualSaveOpen(self): - if self.checkExpertUser(): - self.saveOpen() - - def saveOpen(self): - self.app.calibration.s11open = self.app.data - self.cal_open_label.setText( - f"Data set ({self.app.calibration.s11open} points)") - - def manualSaveLoad(self): - if self.checkExpertUser(): - self.saveLoad() - - def saveLoad(self): - self.app.calibration.s11load = self.app.data - self.cal_load_label.setText( - f"Data set ({self.app.calibration.s11load} points)") - - def manualSaveIsolation(self): - if self.checkExpertUser(): - self.saveIsolation() - - def saveIsolation(self): - self.app.calibration.s21isolation = self.app.data21 - self.cal_isolation_label.setText( - f"Data set ({self.app.calibration.s21isolation} points)") - - def manualSaveThrough(self): - if self.checkExpertUser(): - self.saveThrough() - - def saveThrough(self): - self.app.calibration.s21through = self.app.data21 - self.cal_through_label.setText( - f"Data set ({self.app.calibration.s21through} points)") + self.cal_save(name) def listCalibrationStandards(self): self.cal_standard_save_selector.clear() @@ -469,11 +419,8 @@ class CalibrationWindow(QtWidgets.QWidget): def reset(self): self.app.calibration = Calibration() - self.cal_short_label.setText("Uncalibrated") - self.cal_open_label.setText("Uncalibrated") - self.cal_load_label.setText("Uncalibrated") - self.cal_through_label.setText("Uncalibrated") - self.cal_isolation_label.setText("Uncalibrated") + for label in self.cal_label.values(): + label.setText("Uncalibrated") self.calibration_status_label.setText("Device calibration") self.calibration_source_label.setText("Device") self.notes_textedit.clear() @@ -572,8 +519,8 @@ class CalibrationWindow(QtWidgets.QWidget): 'Invalid data for "through" calibration standard. Using ideal values.') logger.debug("Attempting calibration calculation.") - valid, error = self.app.calibration.calculateCorrections() - if valid: + try: + self.app.calibration.calc_corrections() self.calibration_status_label.setText( f"Application calibration ({len(self.app.calibration.s11short)} points)") if self.use_ideal_values.isChecked(): @@ -591,9 +538,9 @@ class CalibrationWindow(QtWidgets.QWidget): self.app.saveData(self.app.worker.data11, self.app.worker.data21, self.app.sweepSource) self.app.worker.signals.updated.emit() - else: + except ValueError as e: # showError here hides the calibration window, so we need to pop up our own - QtWidgets.QMessageBox.warning(self, "Error applying calibration", error) + QtWidgets.QMessageBox.warning(self, "Error applying calibration", str(e)) self.calibration_status_label.setText("Applying calibration failed.") self.calibration_source_label.setText(self.app.calibration.source) @@ -608,24 +555,26 @@ class CalibrationWindow(QtWidgets.QWidget): filename, _ = QtWidgets.QFileDialog.getOpenFileName( filter="Calibration Files (*.cal);;All files (*.*)") if filename: - self.app.calibration.loadCalibration(filename) - if self.app.calibration.isValid1Port(): - self.cal_short_label.setText( - f"Loaded ({len(self.app.calibration.s11short)})") - self.cal_open_label.setText( - f"Loaded ({len(self.app.calibration.s11open)})") - self.cal_load_label.setText( - f"Loaded ({len(self.app.calibration.s11load)})") - if self.app.calibration.isValid2Port(): - self.cal_through_label.setText( - f"Loaded ({len(self.app.calibration.s21through)})") - self.cal_isolation_label.setText( - f"Loaded ({len(self.app.calibration.s21isolation)})") - self.calculate() - self.notes_textedit.clear() - for note in self.app.calibration.notes: - self.notes_textedit.appendPlainText(note) - self.app.settings.setValue("CalibrationFile", filename) + self.app.calibration.load(filename) + if not self.app.calibration.isValid1Port(): + return + cals = ( + ("short", self.app.calibration.s11short), + ("open", self.app.calibration.s11open), + ("load", self.app.calibration.s11load), + ("through", self.app.calibration.s21through), + ("isolation", self.app.calibration.s21isolation), + ) + for i, cal in enumerate(cals): + self.cal_label[cal[0]].setText( + _format_cal_label(cal[1], "Loaded")) + if i == 2 and not self.app.calibration.isValid2Port(): + break + self.calculate() + self.notes_textedit.clear() + for note in self.app.calibration.notes: + self.notes_textedit.appendPlainText(note) + self.app.settings.setValue("CalibrationFile", filename) def saveCalibration(self): if not self.app.calibration.isCalculated: @@ -645,9 +594,10 @@ class CalibrationWindow(QtWidgets.QWidget): logger.debug("No file name selected.") return self.app.calibration.notes = self.notes_textedit.toPlainText().splitlines() - if filename and self.app.calibration.saveCalibration(filename): + try: + self.app.calibration.save(filename) self.app.settings.setValue("CalibrationFile", filename) - else: + except IOError: logger.error("Calibration save failed!") self.app.showError("Calibration save failed.") @@ -724,7 +674,7 @@ class CalibrationWindow(QtWidgets.QWidget): if self.nextStep == 0: # Short - self.saveShort() + self.save("short") self.nextStep = 1 open_step = QtWidgets.QMessageBox( @@ -748,7 +698,7 @@ class CalibrationWindow(QtWidgets.QWidget): if self.nextStep == 1: # Open - self.saveOpen() + self.save("open") self.nextStep = 2 load_step = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Information, @@ -769,7 +719,7 @@ class CalibrationWindow(QtWidgets.QWidget): if self.nextStep == 2: # Load - self.saveLoad() + self.save("load") self.nextStep = 3 continue_step = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Information, @@ -813,7 +763,7 @@ class CalibrationWindow(QtWidgets.QWidget): if self.nextStep == 3: # Isolation - self.saveIsolation() + self.save("isolation") self.nextStep = 4 through_step = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Information, @@ -835,7 +785,7 @@ class CalibrationWindow(QtWidgets.QWidget): if self.nextStep == 4: # Done - self.saveThrough() + self.save("through") apply_step = QtWidgets.QMessageBox( QtWidgets.QMessageBox.Information, "Calibrate complete",