mirror of
https://github.com/nqrduck/nqrduck-autotm.git
synced 2024-11-05 01:40:01 +00:00
Linting.
This commit is contained in:
parent
3a23b126fe
commit
bfbc20c2e9
5 changed files with 556 additions and 212 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue