mirror of
https://github.com/nqrduck/nqrduck-autotm.git
synced 2024-11-08 11:20:00 +00:00
Added loading saving of positions.
This commit is contained in:
parent
857ff6eb08
commit
6920b7545a
3 changed files with 246 additions and 1 deletions
|
@ -8,7 +8,7 @@ from PyQt6 import QtSerialPort
|
|||
from PyQt6.QtCore import QThread, pyqtSignal, pyqtSlot, Qt
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
from nqrduck.module.module_controller import ModuleController
|
||||
from .model import S11Data, ElectricalLookupTable, MechanicalLookupTable
|
||||
from .model import S11Data, ElectricalLookupTable, MechanicalLookupTable, SavedPosition
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -671,5 +671,43 @@ class AutoTMController(ModuleController):
|
|||
actual_steps = self.calculate_steps_for_absolute_move(future_position)
|
||||
self.send_stepper_command(actual_steps)
|
||||
|
||||
def load_positions(self, path : str) -> None:
|
||||
"""Load the saved positions from a json file.
|
||||
|
||||
Args:
|
||||
path (str): The path to the json file.
|
||||
"""
|
||||
# First clear the old positions
|
||||
self.module.model.saved_positions = []
|
||||
|
||||
with open(path, "r") as f:
|
||||
positions = json.load(f)
|
||||
for position in positions:
|
||||
logger.debug("Loading position: %s", position)
|
||||
self.add_position(position["frequency"], position["tuning_position"], position["matching_position"])
|
||||
|
||||
|
||||
def save_positions(self, path: str) -> None:
|
||||
"""Save the current positions to a json file.
|
||||
|
||||
Args:
|
||||
path (str): The path to the json file.
|
||||
"""
|
||||
positions = self.module.model.saved_positions
|
||||
with open(path, "w") as f:
|
||||
json_position = [position.to_json() for position in positions]
|
||||
json.dump(json_position, f)
|
||||
|
||||
def add_position(self, frequency: str, tuning_position: str, matching_position: str) -> None:
|
||||
"""Add a position to the lookup table.
|
||||
|
||||
Args:
|
||||
frequency (str): The frequency of the position.
|
||||
tuning_position (str): The tuning position.
|
||||
matching_position (str): The matching position.
|
||||
"""
|
||||
logger.debug("Adding new position at %s MHz", frequency)
|
||||
self.module.model.add_saved_position(frequency, tuning_position, matching_position)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -223,6 +223,25 @@ class Stepper:
|
|||
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:
|
||||
self.frequency = frequency
|
||||
self.tuning_position = tuning_position
|
||||
self.matching_position = matching_position
|
||||
|
||||
def to_json(self):
|
||||
return {
|
||||
"frequency": self.frequency,
|
||||
"tuning_position": self.tuning_position,
|
||||
"matching_position": self.matching_position,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
logger.debug(json)
|
||||
return cls(json[0], json[1], json[2])
|
||||
|
||||
class TuningStepper(Stepper):
|
||||
TYPE = "Tuning"
|
||||
MAX_STEPS = 1e6
|
||||
|
@ -275,6 +294,7 @@ class AutoTMModel(ModuleModel):
|
|||
serial_changed = pyqtSignal(QSerialPort)
|
||||
data_points_changed = pyqtSignal(list)
|
||||
active_stepper_changed = pyqtSignal(Stepper)
|
||||
saved_positions_changed = pyqtSignal(list)
|
||||
|
||||
short_calibration_finished = pyqtSignal(S11Data)
|
||||
open_calibration_finished = pyqtSignal(S11Data)
|
||||
|
@ -292,6 +312,8 @@ class AutoTMModel(ModuleModel):
|
|||
self.matching_stepper = MatchingStepper()
|
||||
self.active_stepper = self.tuning_stepper
|
||||
|
||||
self.saved_positions = []
|
||||
|
||||
@property
|
||||
def available_devices(self):
|
||||
return self._available_devices
|
||||
|
@ -325,6 +347,20 @@ class AutoTMModel(ModuleModel):
|
|||
self.data_points.clear()
|
||||
self.data_points_changed.emit(self.data_points)
|
||||
|
||||
@property
|
||||
def saved_positions(self):
|
||||
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:
|
||||
"""Add a saved position to the model."""
|
||||
self.saved_positions.append(SavedPosition(frequency, tuning_position, matching_position))
|
||||
self.saved_positions_changed.emit(self.saved_positions)
|
||||
|
||||
@property
|
||||
def measurement(self):
|
||||
"""The measurement property is used to store the current measurement.
|
||||
|
|
|
@ -124,6 +124,9 @@ class AutoTMView(ModuleView):
|
|||
# Active stepper changed
|
||||
self.module.model.active_stepper_changed.connect(self.on_active_stepper_changed)
|
||||
|
||||
# Position Button
|
||||
self._ui_form.positionButton.clicked.connect(self.on_position_button_clicked)
|
||||
|
||||
self.init_plot()
|
||||
self.init_labels()
|
||||
|
||||
|
@ -218,6 +221,15 @@ class AutoTMView(ModuleView):
|
|||
self._ui_form.increaseButton.setEnabled(False)
|
||||
self._ui_form.absoluteGoButton.setEnabled(False)
|
||||
|
||||
@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")
|
||||
self.position_window = self.StepperSavedPositionsWindow(self.module, self)
|
||||
self.position_window.show()
|
||||
|
||||
def plot_measurement(self, data: "S11Data") -> None:
|
||||
"""Update the S11 plot with the current data points.
|
||||
|
||||
|
@ -336,6 +348,165 @@ class AutoTMView(ModuleView):
|
|||
self.lut_window = self.LutWindow(self.module)
|
||||
self.lut_window.show()
|
||||
|
||||
class StepperSavedPositionsWindow(QDialog):
|
||||
def __init__(self, module, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setParent(parent)
|
||||
self.module = module
|
||||
self.setWindowTitle("Saved positions")
|
||||
# make window larger
|
||||
self.resize(800, 800)
|
||||
|
||||
# Add vertical main layout
|
||||
main_layout = QVBoxLayout()
|
||||
|
||||
# Create table widget
|
||||
self.table_widget = QTableWidget()
|
||||
self.table_widget.setColumnCount(4)
|
||||
self.table_widget.setHorizontalHeaderLabels(
|
||||
["Frequency", "Tuning Position", "Matching Position", "Button"]
|
||||
)
|
||||
|
||||
self.table_widget.setColumnWidth(0, 100)
|
||||
self.table_widget.setColumnWidth(1, 200)
|
||||
self.table_widget.setColumnWidth(2, 200)
|
||||
self.table_widget.setColumnWidth(3, 100)
|
||||
self.on_saved_positions_changed()
|
||||
|
||||
# Add a 'Load Position' button (File selector)
|
||||
load_position_button = QPushButton("Load Positions File")
|
||||
load_position_button.clicked.connect(self.on_load_position_button_clicked)
|
||||
main_layout.addWidget(load_position_button)
|
||||
|
||||
# Add a 'Save Position' button (File selector)
|
||||
save_position_button = QPushButton("Save Positions File")
|
||||
save_position_button.clicked.connect(self.on_save_position_button_clicked)
|
||||
main_layout.addWidget(save_position_button)
|
||||
|
||||
# Add a 'New Position' button
|
||||
new_position_button = QPushButton("New Position")
|
||||
new_position_button.clicked.connect(self.on_new_position_button_clicked)
|
||||
main_layout.addWidget(new_position_button)
|
||||
|
||||
# Add table widget to main layout
|
||||
main_layout.addWidget(self.table_widget)
|
||||
|
||||
# On saved positions changed
|
||||
self.module.model.saved_positions_changed.connect(self.on_saved_positions_changed)
|
||||
|
||||
|
||||
self.setLayout(main_layout)
|
||||
|
||||
def file_selector(self, mode) -> str:
|
||||
"""Opens a file selector and returns the selected file."""
|
||||
filedialog = QFileDialog()
|
||||
if mode == "load":
|
||||
filedialog.setAcceptMode(QFileDialog.AcceptMode.AcceptOpen)
|
||||
elif mode == "save":
|
||||
filedialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
|
||||
filedialog.setNameFilter("position files (*.pos)")
|
||||
filedialog.setDefaultSuffix("pos")
|
||||
filedialog.exec()
|
||||
filename = filedialog.selectedFiles()[0]
|
||||
return filename
|
||||
|
||||
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)
|
||||
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)
|
||||
self.module.controller.save_positions(filename)
|
||||
|
||||
def on_new_position_button_clicked(self) -> None:
|
||||
"""Opens a new position dialog."""
|
||||
logger.debug("New position button clicked")
|
||||
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")
|
||||
self.table_widget.clearContents()
|
||||
self.table_widget.setRowCount(0)
|
||||
|
||||
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, 1, QTableWidgetItem(position.tuning_position)
|
||||
)
|
||||
self.table_widget.setItem(
|
||||
row, 2, QTableWidgetItem(position.matching_position)
|
||||
)
|
||||
button = QPushButton("Go")
|
||||
self.table_widget.setCellWidget(row, 3, button)
|
||||
logger.debug("Updated saved positions table")
|
||||
|
||||
class NewPositionWindow(QDialog):
|
||||
def __init__(self, module, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setParent(parent)
|
||||
self.module = module
|
||||
self.setWindowTitle("New Position")
|
||||
|
||||
# Add vertical main layout
|
||||
main_layout = QVBoxLayout()
|
||||
|
||||
# Add horizontal layout for the frequency range
|
||||
frequency_layout = QHBoxLayout()
|
||||
main_layout.addLayout(frequency_layout)
|
||||
frequency_label = QLabel("Frequency")
|
||||
frequency_layout.addWidget(frequency_label)
|
||||
frequency_edit = QLineEdit()
|
||||
frequency_layout.addWidget(frequency_edit)
|
||||
unit_label = QLabel("MHz")
|
||||
frequency_layout.addWidget(unit_label)
|
||||
frequency_layout.addStretch()
|
||||
|
||||
# Add horizontal layout for the calibration type
|
||||
type_layout = QHBoxLayout()
|
||||
main_layout.addLayout(type_layout)
|
||||
|
||||
# Add vertical layout for short calibration
|
||||
tuning_layout = QVBoxLayout()
|
||||
tuning_label = QLabel("Tuning Position")
|
||||
tuning_layout.addWidget(tuning_label)
|
||||
tuning_edit = QLineEdit()
|
||||
tuning_layout.addWidget(tuning_edit)
|
||||
type_layout.addLayout(tuning_layout)
|
||||
|
||||
# Add vertical layout for open calibration
|
||||
matching_layout = QVBoxLayout()
|
||||
matching_label = QLabel("Matching Position")
|
||||
matching_layout.addWidget(matching_label)
|
||||
matching_edit = QLineEdit()
|
||||
matching_layout.addWidget(matching_edit)
|
||||
type_layout.addLayout(matching_layout)
|
||||
|
||||
# Add vertical layout for save calibration
|
||||
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()))
|
||||
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:
|
||||
"""This method is called when the apply button is clicked."""
|
||||
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."""
|
||||
|
||||
|
|
Loading…
Reference in a new issue