kopia lustrzana https://github.com/NanoVNA-Saver/nanovna-saver
Add CalibrationSettings script and rewrite it to be used headless.
rodzic
0ecbad1833
commit
9bbf4180fd
|
@ -0,0 +1,340 @@
|
|||
# NanoVNASaver
|
||||
#
|
||||
# A python program to view and export Touchstone data from a NanoVNA
|
||||
# Copyright (C) 2019, 2020 Rune B. Broberg
|
||||
# Copyright (C) 2020,2021 NanoVNA-Saver Authors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
from .Calibration import Calibration
|
||||
from .Settings.Sweep import SweepMode
|
||||
|
||||
|
||||
class CalibrationHelpers():# renamed from CalibrationWindow since it is no longer a window.
|
||||
nextStep = -1
|
||||
|
||||
def __init__(self, calibration, settings, touchstone):
|
||||
self.calibration = calibration
|
||||
self.settings = settings
|
||||
self.data = touchstone
|
||||
|
||||
self.listCalibrationStandards() # The only thing in the init that was worth saving.
|
||||
|
||||
|
||||
def checkExpertUser(self):
|
||||
if not self.settings.value("ExpertCalibrationUser", False, bool):
|
||||
response = input(
|
||||
"""Are you sure? \n
|
||||
|
||||
Use of the manual calibration buttons is non-intuitive
|
||||
and primarily suited for users with very specialized
|
||||
needs. The buttons do not sweep for you nor do\n
|
||||
they interact with the NanoVNA calibration.\n\n
|
||||
If you are trying to do a calibration of the NanoVNA,\n
|
||||
do so on the device itself instead. If you are trying to\n
|
||||
do a calibration with NanoVNA-Saver, use the Calibration assistant\n
|
||||
if possible.\n\n
|
||||
Are you certain you know what you are doing? (Y/n)
|
||||
""")
|
||||
if response.upper() == "Y" or response.upper() == "YES":
|
||||
self.settings.setValue("ExpertCalibrationUser", True)
|
||||
return True
|
||||
return False
|
||||
return True
|
||||
|
||||
def cal_save(self, name: str):
|
||||
if name in {"through", "isolation"}:
|
||||
self.calibration.insert(name, self.data.s21) ######## FIX THIS
|
||||
else:
|
||||
self.calibration.insert(name, self.data.s11)
|
||||
|
||||
def manual_save(self, name: str):
|
||||
if self.checkExpertUser():
|
||||
self.cal_save(name)
|
||||
|
||||
def listCalibrationStandards(self):
|
||||
num_standards = self.settings.beginReadArray("CalibrationStandards")
|
||||
for i in range(num_standards):
|
||||
self.settings.setArrayIndex(i)
|
||||
name = self.settings.value("Name", defaultValue="INVALID NAME")
|
||||
print(name)
|
||||
self.settings.endArray()
|
||||
|
||||
def reset(self):
|
||||
self.calibration = Calibration()
|
||||
|
||||
if len(self.worker.rawData11) > 0:
|
||||
# There's raw data, so we can get corrected data
|
||||
if self.verbose:
|
||||
print("Saving and displaying raw data.")
|
||||
self.saveData(
|
||||
self.worker.rawData11,
|
||||
self.worker.rawData21,
|
||||
self.sweepSource,
|
||||
)
|
||||
self.worker.signals.updated.emit()
|
||||
|
||||
def setOffsetDelay(self, value: float):
|
||||
if self.verbose:
|
||||
print("New offset delay value: %f ps", value)
|
||||
self.worker.offsetDelay = value / 1e12
|
||||
if len(self.worker.rawData11) > 0:
|
||||
# There's raw data, so we can get corrected data
|
||||
if self.verbose:
|
||||
print("Applying new offset to existing sweep data.")
|
||||
(
|
||||
self.worker.data11,
|
||||
self.worker.data21,
|
||||
) = self.worker.applyCalibration(
|
||||
self.worker.rawData11, self.worker.rawData21
|
||||
)
|
||||
if self.verbose:
|
||||
print("Saving and displaying corrected data.")
|
||||
self.saveData(
|
||||
self.worker.data11,
|
||||
self.worker.data21,
|
||||
self.sweepSource,
|
||||
)
|
||||
self.worker.signals.updated.emit()
|
||||
|
||||
def calculate(self):
|
||||
cal_element = self.calibration.cal_element
|
||||
if False: #TODO ensure sweep is not currently running.
|
||||
print("Unable to apply calibration while a sweep is running. Please stop the sweep and try again.")
|
||||
return
|
||||
|
||||
cal_element.short_is_ideal = True
|
||||
cal_element.open_is_ideal = True
|
||||
cal_element.load_is_ideal = True
|
||||
cal_element.through_is_ideal = True
|
||||
|
||||
try:
|
||||
self.calibration.calc_corrections()
|
||||
|
||||
if self.use_ideal_values:
|
||||
self.calibration_source_label.setText(
|
||||
self.calibration.source
|
||||
)
|
||||
|
||||
if self.worker.rawData11:
|
||||
# There's raw data, so we can get corrected data
|
||||
if self.verbose:
|
||||
print("Applying calibration to existing sweep data.")
|
||||
(
|
||||
self.worker.data11,
|
||||
self.worker.data21,
|
||||
) = self.worker.applyCalibration(
|
||||
self.worker.rawData11, self.worker.rawData21
|
||||
)
|
||||
if self.verbose:
|
||||
print("Saving and displaying corrected data.")
|
||||
self.saveData(
|
||||
self.worker.data11,
|
||||
self.worker.data21,
|
||||
self.sweepSource,
|
||||
)
|
||||
self.worker.signals.updated.emit()
|
||||
except ValueError as e:
|
||||
raise Exception(f"Error applying calibration: {str(e)}\nApplying calibration failed.")
|
||||
|
||||
|
||||
def loadCalibration(self, filename):
|
||||
if filename:
|
||||
self.calibration.load(filename)
|
||||
if not self.calibration.isValid1Port():
|
||||
raise Exception("Not a valid port.")
|
||||
|
||||
for i, name in enumerate(
|
||||
("short", "open", "load", "through", "isolation", "thrurefl")
|
||||
):
|
||||
if i == 2 and not self.calibration.isValid2Port():
|
||||
break
|
||||
self.calculate()
|
||||
self.settings.setValue("CalibrationFile", filename)
|
||||
|
||||
def saveCalibration(self, filename):
|
||||
if not self.calibration.isCalculated:
|
||||
raise Exception("Cannot save an unapplied calibration state.")
|
||||
|
||||
try:
|
||||
self.calibration.save(filename)
|
||||
self.settings.setValue("CalibrationFile", filename)
|
||||
return True
|
||||
except Exception as e:
|
||||
print("Save failed: ", e)
|
||||
return False
|
||||
|
||||
def automaticCalibration(self):
|
||||
response = input(
|
||||
"""Calibration assistant,
|
||||
|
||||
This calibration assistant will help you create a calibration\n
|
||||
in the NanoVNASaver application. It will sweep the\n
|
||||
standards for you and guide you through the process.<br><br>\n
|
||||
Before starting, ensure you have Open, Short and Load\n
|
||||
standards available and the cables you wish to have\n
|
||||
calibrated connected to the device.<br><br>\n
|
||||
If you want a 2-port calibration, also have a through\n
|
||||
connector on hand.<br><br>\n
|
||||
<b>The best results are achieved by having the NanoVNA\n
|
||||
calibrated on-device for the full span of interest and stored\n
|
||||
in save slot 0 before starting.</b><br><br>\n\n
|
||||
Once you are ready to proceed, press enter. (q to quit).""")
|
||||
|
||||
if response.lower() == 'q':
|
||||
return
|
||||
print("Starting automatic calibration assistant.")
|
||||
if not self.vna.connected():
|
||||
print("NanoVNA not connected.\n\nPlease ensure the NanoVNA is connected before attempting calibration.")
|
||||
return
|
||||
|
||||
if self.sweep.properties.mode == SweepMode.CONTINOUS:
|
||||
print("Continuous sweep enabled.\n\nPlease disable continuous sweeping before attempting calibration.")
|
||||
return
|
||||
|
||||
response = input("Calibrate short.\n\nPlease connect the short standard to port 0 of the NanoVNA.\n\n Press enter when you are ready to continue. (q to quit).")
|
||||
|
||||
if response.lower() == 'q':
|
||||
return
|
||||
|
||||
self.reset()
|
||||
self.calibration.source = "Calibration assistant"
|
||||
self.nextStep = 0
|
||||
self.worker.signals.finished.connect(self.automaticCalibrationStep)
|
||||
self.sweep_start()
|
||||
return
|
||||
|
||||
def automaticCalibrationStep(self):
|
||||
if self.nextStep == -1:
|
||||
self.worker.signals.finished.disconnect(
|
||||
self.automaticCalibrationStep
|
||||
)
|
||||
return
|
||||
|
||||
if self.nextStep == 0:
|
||||
# Short
|
||||
self.cal_save("short")
|
||||
self.nextStep = 1
|
||||
|
||||
response = input("""Calibrate open.\n\nPlease connect the open
|
||||
standard to port 0 of the NanoVNA.\n\n
|
||||
Either use a supplied open, or leave the end of the
|
||||
cable unconnected if desired.\n\n
|
||||
Press enter when you are ready to continue. (q to quit).""")
|
||||
|
||||
if response.lower() == 'q':
|
||||
self.nextStep = -1
|
||||
self.worker.signals.finished.disconnect(
|
||||
self.automaticCalibrationStep
|
||||
)
|
||||
return
|
||||
self.sweep_start()
|
||||
return
|
||||
|
||||
if self.nextStep == 1:
|
||||
# Open
|
||||
self.cal_save("open")
|
||||
self.nextStep = 2
|
||||
response = input("""Calibrate load
|
||||
Please connect the "load" standard to port 0 of the NanoVNA.\n\n
|
||||
Press Ok when you are ready to continue. (q to quit).""")
|
||||
|
||||
if response.lower() == 'q':
|
||||
self.nextStep = -1
|
||||
self.worker.signals.finished.disconnect(
|
||||
self.automaticCalibrationStep
|
||||
)
|
||||
return
|
||||
self.sweep_start()
|
||||
return
|
||||
|
||||
if self.nextStep == 2:
|
||||
# Load
|
||||
self.cal_save("load")
|
||||
self.nextStep = 3
|
||||
response = input("""1-port calibration complete,\n
|
||||
The required steps for a 1-port calibration are now complete.\n\n
|
||||
Do you wish to continue and perform a 2-port calibration,
|
||||
enter Y.\nTo apply the 1-port calibration and stop, press q""")
|
||||
|
||||
if response.lower() == 'q':
|
||||
self.calculate()
|
||||
self.nextStep = -1
|
||||
self.worker.signals.finished.disconnect(
|
||||
self.automaticCalibrationStep
|
||||
)
|
||||
return
|
||||
if response.lower() == 'y' or response.lower() == "yes":
|
||||
self.nextStep = -1
|
||||
self.worker.signals.finished.disconnect(
|
||||
self.automaticCalibrationStep
|
||||
)
|
||||
return
|
||||
|
||||
response = input("""Calibrate isolation\n
|
||||
Please connect the load standard to port 1 of the
|
||||
NanoVNA.\n\n If available, also connect a load standard to
|
||||
port 0.\n\n Press enter when you are ready to continue. (q to quit).""")
|
||||
|
||||
if response.lower() == 'q':
|
||||
self.nextStep = -1
|
||||
self.worker.signals.finished.disconnect(
|
||||
self.automaticCalibrationStep
|
||||
)
|
||||
return
|
||||
self.sweep_start()
|
||||
return
|
||||
|
||||
if self.nextStep == 3:
|
||||
# Isolation
|
||||
self.cal_save("isolation")
|
||||
self.nextStep = 4
|
||||
response = input("""Calibrate through.\n
|
||||
Please connect the "through" standard between
|
||||
port 0 and port 1 of the NanoVNA.\n\n
|
||||
Press Ok when you are ready to continue. (q to quit).""")
|
||||
|
||||
if response.lower() == 'q':
|
||||
self.btn_automatic.setDisabled(False)
|
||||
self.nextStep = -1
|
||||
self.worker.signals.finished.disconnect(
|
||||
self.automaticCalibrationStep
|
||||
)
|
||||
return
|
||||
self.sweep_start()
|
||||
return
|
||||
|
||||
if self.nextStep == 4:
|
||||
# Done
|
||||
self.cal_save("thrurefl")
|
||||
self.cal_save("through")
|
||||
response = input("""Calibrate complete.\n
|
||||
The calibration process is now complete. Press
|
||||
enter to apply the calibration parameters. (q to quit).""")
|
||||
|
||||
if response.lower() == 'q':
|
||||
self.btn_automatic.setDisabled(False)
|
||||
self.nextStep = -1
|
||||
self.worker.signals.finished.disconnect(
|
||||
self.automaticCalibrationStep
|
||||
)
|
||||
return
|
||||
|
||||
self.calculate()
|
||||
self.btn_automatic.setDisabled(False)
|
||||
self.nextStep = -1
|
||||
self.worker.signals.finished.disconnect(
|
||||
self.automaticCalibrationStep
|
||||
)
|
||||
return
|
Ładowanie…
Reference in New Issue