This commit is contained in:
jupfi 2024-05-08 16:26:57 +02:00
parent 3a23b126fe
commit bfbc20c2e9
5 changed files with 556 additions and 212 deletions

View file

@ -1 +1,3 @@
from .autotm import AutoTM as Module
"""The NQRduck AutoTM module. It is used to automatically tune and match magnetic resonance probe coils."""
from .autotm import AutoTM as Module

View file

@ -1,6 +1,8 @@
"""The Module creation snippet for the NQRduck AutoTM module."""
from nqrduck.module.module import Module
from .model import AutoTMModel
from .view import AutoTMView
from .controller import AutoTMController
AutoTM = Module(AutoTMModel, AutoTMView, AutoTMController)
AutoTM = Module(AutoTMModel, AutoTMView, AutoTMController)

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,10 @@
"""The module model for the NQRduck AutoTM module. It is used to store the data and state of the AutoTM module.
Additionally it includes the LookupTable class which is used to store tuning and matching voltages for different frequencies.
The S11Data class is used to store the S11 data that is read in via the serial connection.
"""
import cmath
import numpy as np
import logging
@ -10,6 +17,7 @@ logger = logging.getLogger(__name__)
class S11Data:
"""This class is used to store the S11 data that is read in via the serial connection."""
FILE_EXTENSION = "s11"
# Conversion factors - the data is generally sent and received in mV
# These values are used to convert the data to dB and degrees
@ -19,26 +27,32 @@ class S11Data:
PHASE_SLOPE = 10 # deg/mV
def __init__(self, data_points: list) -> None:
"""Initialize the S11 data."""
self.frequency = np.array([data_point[0] for data_point in data_points])
self.return_loss_mv = np.array([data_point[1] for data_point in data_points])
self.phase_mv = np.array([data_point[2] for data_point in data_points])
@property
def millivolts(self):
"""The reflection data in millivolts. This is the raw data that is read in via the serial connection."""
return self.frequency, self.return_loss_mv, self.phase_mv
@property
def return_loss_db(self):
"""Returns the return loss in dB calculated from the return loss in mV."""
return (
self.return_loss_mv - self.CENTER_POINT_MAGNITUDE
) / self.MAGNITUDE_SLOPE
@property
def phase_deg(self, phase_correction=True):
"""Returns the absolute value of the phase in degrees
def phase_deg(self, phase_correction=True) -> np.array:
"""Returns the absolute value of the phase in degrees.
Keyword Arguments:
phase_correction {bool} -- If True, the phase correction is applied. (default: {False})
Args:
phase_correction (bool, optional): If True, the phase correction is applied. Defaults to True.
Returns:
np.array: The absolute value of the phase in degrees.
"""
phase_deg = (self.phase_mv - self.CENTER_POINT_PHASE) / self.PHASE_SLOPE
if phase_correction:
@ -48,11 +62,12 @@ class S11Data:
@property
def phase_rad(self):
"""Returns the phase in radians."""
return self.phase_deg * cmath.pi / 180
@property
def gamma(self):
"""Complex reflection coefficient"""
"""Complex reflection coefficient."""
if len(self.return_loss_db) != len(self.phase_rad):
raise ValueError("return_loss_db and phase_rad must be the same length")
@ -65,6 +80,7 @@ class S11Data:
self, frequency_data: np.array, phase_data: np.array
) -> np.array:
"""This method fixes the phase sign of the phase data.
The AD8302 can only measure the absolute value of the phase.
Therefore we need to correct the phase sign. This can be done via the slope of the phase.
If the slope is negative, the phase is positive and vice versa.
@ -145,6 +161,7 @@ class S11Data:
return phase_data_corrected
def to_json(self):
"""Convert the S11 data to a JSON serializable format."""
return {
"frequency": self.frequency.tolist(),
"return_loss_mv": self.return_loss_mv.tolist(),
@ -153,6 +170,7 @@ class S11Data:
@classmethod
def from_json(cls, json):
"""Create an S11Data object from a JSON serializable format."""
f = json["frequency"]
rl = json["return_loss_mv"]
p = json["phase_mv"]
@ -171,13 +189,14 @@ class LookupTable:
stop_frequency: float,
frequency_step: float,
) -> None:
"""Initialize the lookup table."""
self.start_frequency = start_frequency
self.stop_frequency = stop_frequency
self.frequency_step = frequency_step
# This is the frequency at which the tuning and matching process was started
self.started_frequency = None
def get_entry_number(self, frequency: float) -> int:
"""This method returns the entry number of the given frequency.
@ -190,57 +209,78 @@ class LookupTable:
# Round to closest integer
return int(round((frequency - self.start_frequency) / self.frequency_step))
class Stepper:
class Stepper:
"""This class is used to store the state of a stepper motor."""
def __init__(self) -> None:
"""Initialize the stepper motor."""
self.homed = False
self.position = 0
class SavedPosition:
"""This class is used to store a saved position for tuning and matching of electrical probeheads."""
def __init__(self, frequency: float, tuning_position : int, matching_position : int) -> None:
def __init__(
self, frequency: float, tuning_position: int, matching_position: int
) -> None:
"""Initialize the saved position."""
self.frequency = frequency
self.tuning_position = tuning_position
self.matching_position = matching_position
def to_json(self):
"""Convert the saved position to a JSON serializable format."""
return {
"frequency": self.frequency,
"tuning_position": self.tuning_position,
"matching_position": self.matching_position,
}
class TuningStepper(Stepper):
"""This class is used to store the state of the tuning stepper motor."""
TYPE = "Tuning"
MAX_STEPS = 1e6
BACKLASH_STEPS = 60
def __init__(self) -> None:
"""Initialize the tuning stepper motor."""
super().__init__()
# Backlash stepper
# Backlash stepper
self.last_direction = None
class MatchingStepper(Stepper):
"""This class is used to store the state of the matching stepper motor."""
TYPE = "Matching"
MAX_STEPS = 1e6
BACKLASH_STEPS = 0
def __init__(self) -> None:
"""Initialize the matching stepper motor."""
super().__init__()
self.last_direction = None
class ElectricalLookupTable(LookupTable):
"""This class is used to store a lookup table for tuning and matching of electrical probeheads."""
TYPE = "Electrical"
def __init__(self, start_frequency: float, stop_frequency: float, frequency_step: float) -> None:
def __init__(
self, start_frequency: float, stop_frequency: float, frequency_step: float
) -> None:
"""Initialize the lookup table."""
super().__init__(start_frequency, stop_frequency, frequency_step)
self.init_voltages()
def init_voltages(self) -> None:
"""Initialize the lookup table with default values."""
for frequency in np.arange(
self.start_frequency, self.stop_frequency + self.frequency_step, self.frequency_step
self.start_frequency,
self.stop_frequency + self.frequency_step,
self.frequency_step,
):
self.started_frequency = frequency
self.add_voltages(None, None)
@ -250,7 +290,7 @@ class ElectricalLookupTable(LookupTable):
Args:
tuning_voltage (float): The tuning voltage for the given frequency.
matching_voltage (float): The matching voltage for the given frequency.
matching_voltage (float): The matching voltage for the given frequency.
"""
self.data[self.started_frequency] = (tuning_voltage, matching_voltage)
@ -266,10 +306,11 @@ class ElectricalLookupTable(LookupTable):
entry_number = self.get_entry_number(frequency)
key = list(self.data.keys())[entry_number]
return self.data[key]
def is_incomplete(self) -> bool:
"""This method returns True if the lookup table is incomplete,
i.e. if there are frequencies for which no the tuning or matching voltage is none.
"""This method returns True if the lookup table is incomplete.
I.e. if there are frequencies for which no the tuning or matching voltage is none.
Returns:
bool: True if the lookup table is incomplete, False otherwise.
@ -293,19 +334,25 @@ class ElectricalLookupTable(LookupTable):
return None
class MechanicalLookupTable(LookupTable):
"""This class is used to store a lookup table for tuning and matching of mechanical probeheads."""
# Hmm duplicate code
TYPE = "Mechanical"
def __init__(self, start_frequency: float, stop_frequency: float, frequency_step: float) -> None:
def __init__(
self, start_frequency: float, stop_frequency: float, frequency_step: float
) -> None:
"""Initialize the lookup table."""
super().__init__(start_frequency, stop_frequency, frequency_step)
self.init_positions()
def init_positions(self) -> None:
"""Initialize the lookup table with default values."""
for frequency in np.arange(
self.start_frequency, self.stop_frequency + self.frequency_step, self.frequency_step
self.start_frequency,
self.stop_frequency + self.frequency_step,
self.frequency_step,
):
self.started_frequency = frequency
self.add_positions(None, None)
@ -315,7 +362,7 @@ class MechanicalLookupTable(LookupTable):
Args:
tuning_position (int): The tuning position for the given frequency.
matching_position (int): The matching position for the given frequency.
matching_position (int): The matching position for the given frequency.
"""
self.data[self.started_frequency] = (tuning_position, matching_position)
@ -331,10 +378,11 @@ class MechanicalLookupTable(LookupTable):
entry_number = self.get_entry_number(frequency)
key = list(self.data.keys())[entry_number]
return self.data[key]
def is_incomplete(self) -> bool:
"""This method returns True if the lookup table is incomplete,
i.e. if there are frequencies for which no the tuning or matching position is none.
"""This method returns True if the lookup table is incomplete.
I.e. if there are frequencies for which no the tuning or matching position is none.
Returns:
bool: True if the lookup table is incomplete, False otherwise.
@ -345,7 +393,7 @@ class MechanicalLookupTable(LookupTable):
for tuning_position, matching_position in self.data.values()
]
)
def get_next_frequency(self) -> float:
"""This method returns the next frequency for which the tuning and matching position is not yet set.
@ -357,8 +405,10 @@ class MechanicalLookupTable(LookupTable):
return frequency
return None
class AutoTMModel(ModuleModel):
class AutoTMModel(ModuleModel):
"""The module model for the NQRduck AutoTM module. It is used to store the data and state of the AutoTM module."""
available_devices_changed = pyqtSignal(list)
serial_changed = pyqtSignal(QSerialPort)
data_points_changed = pyqtSignal(list)
@ -372,6 +422,7 @@ class AutoTMModel(ModuleModel):
measurement_finished = pyqtSignal(S11Data)
def __init__(self, module) -> None:
"""Initialize the AutoTM model."""
super().__init__(module)
self.data_points = []
self.active_calibration = None
@ -398,6 +449,7 @@ class AutoTMModel(ModuleModel):
@property
def available_devices(self):
"""The available_devices property is used to store the available serial devices."""
return self._available_devices
@available_devices.setter
@ -419,6 +471,7 @@ class AutoTMModel(ModuleModel):
self, frequency: float, return_loss: float, phase: float
) -> None:
"""Add a data point to the model. These data points are our intermediate data points read in via the serial connection.
They will be saved in the according properties later on.
"""
self.data_points.append((frequency, return_loss, phase))
@ -431,16 +484,21 @@ class AutoTMModel(ModuleModel):
@property
def saved_positions(self):
"""The saved_positions property is used to store the saved positions for tuning and matching of the probeheads."""
return self._saved_positions
@saved_positions.setter
def saved_positions(self, value):
self._saved_positions = value
self.saved_positions_changed.emit(value)
def add_saved_position(self, frequency: float, tuning_position: int, matching_position: int) -> None:
def add_saved_position(
self, frequency: float, tuning_position: int, matching_position: int
) -> None:
"""Add a saved position to the model."""
self.saved_positions.append(SavedPosition(frequency, tuning_position, matching_position))
self.saved_positions.append(
SavedPosition(frequency, tuning_position, matching_position)
)
self.saved_positions_changed.emit(self.saved_positions)
def delete_saved_position(self, position: SavedPosition) -> None:
@ -451,6 +509,7 @@ class AutoTMModel(ModuleModel):
@property
def measurement(self):
"""The measurement property is used to store the current measurement.
This is the measurement that is shown in the main S11 plot
"""
return self._measurement
@ -463,8 +522,9 @@ class AutoTMModel(ModuleModel):
@property
def active_stepper(self):
"""The active_stepper property is used to store the active stepper motor."""
return self._active_stepper
@active_stepper.setter
def active_stepper(self, value):
self._active_stepper = value
@ -474,6 +534,7 @@ class AutoTMModel(ModuleModel):
@property
def active_calibration(self):
"""The active_calibration property is used to store the active calibration type."""
return self._active_calibration
@active_calibration.setter
@ -482,6 +543,7 @@ class AutoTMModel(ModuleModel):
@property
def short_calibration(self):
"""The short_calibration property is used to store the short calibration data."""
return self._short_calibration
@short_calibration.setter
@ -497,6 +559,7 @@ class AutoTMModel(ModuleModel):
@property
def open_calibration(self):
"""The open calibration data."""
return self._open_calibration
@open_calibration.setter
@ -512,6 +575,7 @@ class AutoTMModel(ModuleModel):
@property
def load_calibration(self):
"""The load calibration data."""
return self._load_calibration
@load_calibration.setter
@ -527,6 +591,7 @@ class AutoTMModel(ModuleModel):
@property
def calibration(self):
"""The calibration data."""
return self._calibration
@calibration.setter
@ -536,6 +601,7 @@ class AutoTMModel(ModuleModel):
@property
def LUT(self):
"""The lookup table for tuning and matching of the probeheads."""
return self._LUT
@LUT.setter

View file

@ -1,3 +1,5 @@
"""This module contains the view class for the AutoTM module."""
import logging
from datetime import datetime
import cmath
@ -27,7 +29,9 @@ logger = logging.getLogger(__name__)
class AutoTMView(ModuleView):
"""The view class for the AutoTM module."""
def __init__(self, module):
"""Initializes the AutoTM view."""
super().__init__(module)
widget = QWidget()
@ -127,11 +131,27 @@ class AutoTMView(ModuleView):
self._ui_form.startButton.setIconSize(self._ui_form.startButton.size())
# Stepper selection
self._ui_form.stepperselectBox.currentIndexChanged.connect(lambda: self.module.controller.on_stepper_changed(self._ui_form.stepperselectBox.currentText()))
self._ui_form.increaseButton.clicked.connect(lambda: self.module.controller.on_relative_move(self._ui_form.stepsizeBox.text()))
self._ui_form.decreaseButton.clicked.connect(lambda: self.module.controller.on_relative_move("-" + self._ui_form.stepsizeBox.text()))
self._ui_form.stepperselectBox.currentIndexChanged.connect(
lambda: self.module.controller.on_stepper_changed(
self._ui_form.stepperselectBox.currentText()
)
)
self._ui_form.increaseButton.clicked.connect(
lambda: self.module.controller.on_relative_move(
self._ui_form.stepsizeBox.text()
)
)
self._ui_form.decreaseButton.clicked.connect(
lambda: self.module.controller.on_relative_move(
"-" + self._ui_form.stepsizeBox.text()
)
)
self._ui_form.absoluteGoButton.clicked.connect(lambda: self.module.controller.on_absolute_move(self._ui_form.absoluteposBox.text()))
self._ui_form.absoluteGoButton.clicked.connect(
lambda: self.module.controller.on_absolute_move(
self._ui_form.absoluteposBox.text()
)
)
# Active stepper changed
self.module.model.active_stepper_changed.connect(self.on_active_stepper_changed)
@ -175,6 +195,7 @@ class AutoTMView(ModuleView):
def on_calibration_button_clicked(self) -> None:
"""This method is called when the calibration button is clicked.
It opens the calibration window.
"""
logger.debug("Calibration button clicked")
@ -183,7 +204,11 @@ class AutoTMView(ModuleView):
@pyqtSlot(list)
def on_available_devices_changed(self, available_devices: list) -> None:
"""Update the available devices list in the view."""
"""Update the available devices list in the view.
Args:
available_devices (list): List of available devices.
"""
logger.debug("Updating available devices list")
self._ui_form.portBox.clear()
self._ui_form.portBox.addItems(available_devices)
@ -201,6 +226,7 @@ class AutoTMView(ModuleView):
@pyqtSlot()
def on_connect_button_clicked(self) -> None:
"""This method is called when the connect button is clicked.
It calls the connect method of the controller with the currently selected device.
"""
logger.debug("Connect button clicked")
@ -217,7 +243,7 @@ class AutoTMView(ModuleView):
logger.debug("Updating serial connection label")
if serial.isOpen():
self._ui_form.connectionLabel.setText(serial.portName())
self.add_info_text("Connected to device %s" % serial.portName())
self.add_info_text(f"Connected to device {serial.portName()}")
# Change the connectButton to a disconnectButton
self._ui_form.connectButton.setText("Disconnect")
else:
@ -231,7 +257,9 @@ class AutoTMView(ModuleView):
def on_active_stepper_changed(self) -> None:
"""Update the stepper position label according to the current stepper position."""
logger.debug("Updating stepper position label")
self._ui_form.stepperposLabel.setText(str(self.module.model.active_stepper.position))
self._ui_form.stepperposLabel.setText(
str(self.module.model.active_stepper.position)
)
logger.debug("Updated stepper position label")
# Only allow position change when stepper is homed
@ -253,6 +281,7 @@ class AutoTMView(ModuleView):
@pyqtSlot()
def on_position_button_clicked(self) -> None:
"""This method is called when the position button is clicked.
It opens the position window.
"""
logger.debug("Position button clicked")
@ -263,7 +292,7 @@ class AutoTMView(ModuleView):
"""Update the S11 plot with the current data points.
Args:
data_points (list): List of data points to plot.
data (S11Data): The current S11 data points.
@TODO: implement proper calibration. See the controller class for more information.
"""
@ -328,7 +357,7 @@ class AutoTMView(ModuleView):
"""
# Add a timestamp to the text
timestamp = datetime.now().strftime("%H:%M:%S")
text = "[%s] %s" % (timestamp, text)
text = f"[{timestamp}] {text}"
text_label = QLabel(text)
text_label.setStyleSheet("font-size: 25px;")
self._ui_form.scrollAreaWidgetContents.layout().addWidget(text_label)
@ -351,7 +380,7 @@ class AutoTMView(ModuleView):
)
# Add a timestamp to the text
timestamp = datetime.now().strftime("%H:%M:%S")
text = "[%s] %s" % (timestamp, text)
text = f"[{timestamp}] {text}"
text_label = QLabel(text)
text_label.setStyleSheet("font-size: 25px; color: red;")
@ -365,7 +394,9 @@ class AutoTMView(ModuleView):
def create_frequency_sweep_spinner_dialog(self) -> None:
"""Creates a frequency sweep spinner dialog."""
self.frequency_sweep_spinner = self.LoadingSpinner("Performing frequency sweep ...", self)
self.frequency_sweep_spinner = self.LoadingSpinner(
"Performing frequency sweep ...", self
)
self.frequency_sweep_spinner.show()
def create_el_LUT_spinner_dialog(self) -> None:
@ -375,7 +406,9 @@ class AutoTMView(ModuleView):
def create_mech_LUT_spinner_dialog(self) -> None:
"""Creates a mechanical LUT spinner dialog."""
self.mech_LUT_spinner = self.LoadingSpinner("Generating mechanical LUT ...", self)
self.mech_LUT_spinner = self.LoadingSpinner(
"Generating mechanical LUT ...", self
)
self.mech_LUT_spinner.show()
def view_el_lut(self) -> None:
@ -417,7 +450,9 @@ class AutoTMView(ModuleView):
self.module.controller.load_measurement(file_name)
class StepperSavedPositionsWindow(QDialog):
"""This class implements a window that shows the saved positions of the stepper."""
def __init__(self, module, parent=None):
"""Initializes the StepperSavedPositionsWindow."""
super().__init__(parent)
self.setParent(parent)
self.module = module
@ -432,7 +467,13 @@ class AutoTMView(ModuleView):
self.table_widget = QTableWidget()
self.table_widget.setColumnCount(5)
self.table_widget.setHorizontalHeaderLabels(
["Frequency (MHz)", "Tuning Position", "Matching Position", "Button", "Delete"]
[
"Frequency (MHz)",
"Tuning Position",
"Matching Position",
"Button",
"Delete",
]
)
self.table_widget.setColumnWidth(0, 150)
@ -461,8 +502,9 @@ class AutoTMView(ModuleView):
main_layout.addWidget(self.table_widget)
# On saved positions changed
self.module.model.saved_positions_changed.connect(self.on_saved_positions_changed)
self.module.model.saved_positions_changed.connect(
self.on_saved_positions_changed
)
self.setLayout(main_layout)
@ -482,13 +524,13 @@ class AutoTMView(ModuleView):
def on_load_position_button_clicked(self) -> None:
"""File picker for loading a position from a file."""
filename = self.file_selector("load")
logger.debug("Loading position from %s" % filename)
logger.debug(f"Loading position from {filename}")
self.module.controller.load_positions(filename)
def on_save_position_button_clicked(self) -> None:
"""File picker for saving a position to a file."""
filename = self.file_selector("save")
logger.debug("Saving position to %s" % filename)
logger.debug(f"Saving position to {filename}")
self.module.controller.save_positions(filename)
def on_new_position_button_clicked(self) -> None:
@ -497,9 +539,9 @@ class AutoTMView(ModuleView):
self.new_position_window = self.NewPositionWindow(self.module, self)
self.new_position_window.show()
def on_saved_positions_changed(self) -> None:
"""This method is called when the saved positions changed.
It updates the table widget.
"""
logger.debug("Updating saved positions table")
@ -508,7 +550,9 @@ class AutoTMView(ModuleView):
for row, position in enumerate(self.module.model.saved_positions):
self.table_widget.insertRow(row)
self.table_widget.setItem(row, 0, QTableWidgetItem(str(position.frequency)))
self.table_widget.setItem(
row, 0, QTableWidgetItem(str(position.frequency))
)
self.table_widget.setItem(
row, 1, QTableWidgetItem(position.tuning_position)
)
@ -517,7 +561,8 @@ class AutoTMView(ModuleView):
)
go_button = QPushButton("Go")
go_button.clicked.connect(
lambda _, position=position: self.module.controller.on_go_to_position(
lambda _,
position=position: self.module.controller.on_go_to_position(
position
)
)
@ -525,16 +570,19 @@ class AutoTMView(ModuleView):
delete_button = QPushButton("Delete")
delete_button.clicked.connect(
lambda _, position=position: self.module.controller.on_delete_position(
lambda _,
position=position: self.module.controller.on_delete_position(
position
)
)
self.table_widget.setCellWidget(row, 4, delete_button)
logger.debug("Updated saved positions table")
class NewPositionWindow(QDialog):
"""This class implements a window for adding a new position."""
def __init__(self, module, parent=None):
"""Initializes the NewPositionWindow."""
super().__init__(parent)
self.setParent(parent)
self.module = module
@ -578,22 +626,32 @@ class AutoTMView(ModuleView):
data_layout = QVBoxLayout()
# Apply button
apply_button = QPushButton("Apply")
apply_button.clicked.connect(lambda: self.on_apply_button_clicked(frequency_edit.text(), tuning_edit.text(), matching_edit.text()))
apply_button.clicked.connect(
lambda: self.on_apply_button_clicked(
frequency_edit.text(), tuning_edit.text(), matching_edit.text()
)
)
data_layout.addWidget(apply_button)
main_layout.addLayout(data_layout)
self.setLayout(main_layout)
def on_apply_button_clicked(self, frequency: str, tuning_position: str, matching_position: str) -> None:
def on_apply_button_clicked(
self, frequency: str, tuning_position: str, matching_position: str
) -> None:
"""This method is called when the apply button is clicked."""
self.module.controller.add_position(frequency, tuning_position, matching_position)
self.module.controller.add_position(
frequency, tuning_position, matching_position
)
# Close the calibration window
self.close()
class LoadingSpinner(QDialog):
"""This class implements a spinner dialog that is shown during a frequency sweep."""
def __init__(self, text : str, parent=None):
def __init__(self, text: str, parent=None):
"""Initializes the LoadingSpinner."""
super().__init__(parent)
self.setWindowTitle("Loading")
self.setModal(True)
@ -611,7 +669,9 @@ class AutoTMView(ModuleView):
self.spinner_movie.start()
class LutWindow(QDialog):
"""This class implements a window that shows the LUT."""
def __init__(self, module, parent=None):
"""Initializes the LutWindow."""
super().__init__()
self.module = module
self.setParent(parent)
@ -641,7 +701,7 @@ class AutoTMView(ModuleView):
self.table_widget.setHorizontalHeaderLabels(
["Frequency (MHz)", "Tuning Voltage", "Matching Voltage"]
)
for row, frequency in enumerate(LUT.data.keys()):
self.table_widget.insertRow(row)
self.table_widget.setItem(row, 0, QTableWidgetItem(str(frequency)))
@ -659,7 +719,9 @@ class AutoTMView(ModuleView):
tuning_voltage = str(LUT.data[frequency][0])
matching_voltage = str(LUT.data[frequency][1])
test_button.clicked.connect(
lambda _, tuning_voltage=tuning_voltage, matching_voltage=matching_voltage: self.module.controller.set_voltages(
lambda _,
tuning_voltage=tuning_voltage,
matching_voltage=matching_voltage: self.module.controller.set_voltages(
tuning_voltage, matching_voltage
)
)
@ -668,11 +730,13 @@ class AutoTMView(ModuleView):
tuning_position = str(LUT.data[frequency][0])
matching_position = str(LUT.data[frequency][1])
test_button.clicked.connect(
lambda _, tuning_position=tuning_position, matching_position=matching_position: self.module.controller.go_to_position(
lambda _,
tuning_position=tuning_position,
matching_position=matching_position: self.module.controller.go_to_position(
tuning_position, matching_position
)
)
self.table_widget.setCellWidget(row, 3, test_button)
# Add table widget to main layout
@ -681,6 +745,7 @@ class AutoTMView(ModuleView):
def test_lut(self):
"""This method is called when the Test LUT button is clicked. It sets all of the voltages from the lut with a small delay.
One can then view the matching on a seperate VNA.
"""
# This should be in the controller
@ -690,7 +755,9 @@ class AutoTMView(ModuleView):
self.module.controller.set_voltages(tuning_voltage, matching_voltage)
class CalibrationWindow(QDialog):
"""The calibration Dialog."""
def __init__(self, module, parent=None):
"""Initializes the CalibrationWindow."""
super().__init__(parent)
self.setParent(parent)
self.module = module
@ -791,18 +858,22 @@ class AutoTMView(ModuleView):
)
def on_short_calibration_finished(self, short_calibration: "S11Data") -> None:
"""This method is called when the short calibration has finished. It plots the calibration data on the short_plot widget."""
self.on_calibration_finished("short", self.short_plot, short_calibration)
def on_open_calibration_finished(self, open_calibration: "S11Data") -> None:
"""This method is called when the open calibration has finished. It plots the calibration data on the open_plot widget."""
self.on_calibration_finished("open", self.open_plot, open_calibration)
def on_load_calibration_finished(self, load_calibration: "S11Data") -> None:
"""This method is called when the load calibration has finished. It plots the calibration data on the load_plot widget."""
self.on_calibration_finished("load", self.load_plot, load_calibration)
def on_calibration_finished(
self, type: str, widget: MplWidget, data: "S11Data"
) -> None:
"""This method is called when a calibration has finished.
It plots the calibration data on the given widget.
"""
frequency = data.frequency
@ -829,13 +900,14 @@ class AutoTMView(ModuleView):
widget.canvas.flush_events()
def on_export_button_clicked(self) -> None:
"""Called when the export button was clicked."""
filedialog = QFileDialog()
filedialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
filedialog.setNameFilter("calibration files (*.cal)")
filedialog.setDefaultSuffix("cal")
filedialog.exec()
filename = filedialog.selectedFiles()[0]
logger.debug("Exporting calibration to %s" % filename)
logger.debug(f"Exporting calibration to {filename}")
self.module.controller.export_calibration(filename)
def on_import_button_clicked(self) -> None:
@ -846,7 +918,7 @@ class AutoTMView(ModuleView):
filedialog.setDefaultSuffix("cal")
filedialog.exec()
filename = filedialog.selectedFiles()[0]
logger.debug("Importing calibration from %s" % filename)
logger.debug(f"Importing calibration from {filename}")
self.module.controller.import_calibration(filename)
def on_apply_button_clicked(self) -> None: