From bdfe4e3e9c540afa0ec059eb68d3872587e06d39 Mon Sep 17 00:00:00 2001 From: jupfi Date: Tue, 12 Dec 2023 16:05:30 +0100 Subject: [PATCH] Implemented LUT creation for mechanical probe coils. --- src/nqrduck_autotm/controller.py | 81 ++++++++++++------- src/nqrduck_autotm/model.py | 6 ++ src/nqrduck_autotm/resources/autotm_widget.ui | 2 +- src/nqrduck_autotm/view.py | 27 ++++++- src/nqrduck_autotm/widget.py | 4 +- 5 files changed, 86 insertions(+), 34 deletions(-) diff --git a/src/nqrduck_autotm/controller.py b/src/nqrduck_autotm/controller.py index d9c6457..f947f55 100644 --- a/src/nqrduck_autotm/controller.py +++ b/src/nqrduck_autotm/controller.py @@ -32,6 +32,7 @@ class AutoTMController(ModuleController): self.module.model.serial_data_received.connect(self.print_info) self.module.model.serial_data_received.connect(self.read_position_data) self.module.model.serial_data_received.connect(self.process_reflection_data) + self.module.model.serial_data_received.connect(self.process_position_sweep_result) @pyqtSlot(str, object) def process_signals(self, key: str, value: object) -> None: @@ -216,7 +217,7 @@ class AutoTMController(ModuleController): if text.startswith("v"): text = text[1:].split("t") matching_voltage, tuning_voltage = map(float, text) - LUT = self.module.model.LUT + LUT = self.module.model.el_lut logger.debug("Received voltage sweep result: %s %s", matching_voltage, tuning_voltage) LUT.add_voltages(matching_voltage, tuning_voltage) self.continue_or_finish_voltage_sweep(LUT) @@ -265,6 +266,7 @@ class AutoTMController(ModuleController): LUT (LookupTable): The lookup table that is being generated.""" logger.debug("Voltage sweep finished") self.module.view.el_LUT_spinner.hide() + self.module.model.LUT = LUT self.module.model.voltage_sweep_stop = time.time() duration = self.module.model.voltage_sweep_stop - self.module.model.voltage_sweep_start self.module.view.add_info_text(f"Voltage sweep finished in {duration:.2f} seconds") @@ -596,7 +598,7 @@ class AutoTMController(ModuleController): confirmation = self.send_command(command) # If the command was send successfully, we set the LUT if confirmation: - self.module.model.LUT = LUT + self.module.model.el_lut = LUT self.module.view.create_el_LUT_spinner_dialog() def switch_to_preamp(self) -> None: @@ -674,6 +676,7 @@ class AutoTMController(ModuleController): logger.debug("Homing") self.send_command("h") self.module.model.tuning_stepper.last_direction = 1 + self.module.model.matching_stepper.last_direction = 1 @pyqtSlot(str) def on_stepper_changed(self, stepper: str) -> None: @@ -711,18 +714,19 @@ class AutoTMController(ModuleController): """Send a command to the stepper motor based on the number of steps.""" # Here we handle backlash of the tuner # Determine the direction of the current steps + backlash = 0 current_direction = np.sign(steps) # This will be -1,or 1 if stepper.TYPE == "Tuning": logger.debug("Stepper last direction: %s", stepper.last_direction) logger.debug("Current direction: %s", current_direction) if stepper.last_direction != current_direction: - steps = (abs(steps) + stepper.BACKLASH_STEPS) * current_direction + backlash = stepper.BACKLASH_STEPS * current_direction stepper.last_direction = current_direction logger.debug("Stepper last direction: %s", stepper.last_direction) motor_identifier = stepper.TYPE.lower()[0] - command = f"m{motor_identifier}{steps}" + command = f"m{motor_identifier}{steps},{backlash}" confirmation = self.send_command(command) return confirmation @@ -893,6 +897,8 @@ class AutoTMController(ModuleController): # Lock GUI # self.module.view.create_mech_LUT_spinner_dialog() + self.module.model.mech_lut = LUT + self.start_next_mechTM(LUT) @@ -903,41 +909,62 @@ class AutoTMController(ModuleController): LUT.started_frequency = next_frequency logger.debug("Starting next mechanical tuning and matching:") - def get_magnitude(reflection): - CENTER_POINT_MAGNITUDE = 900 # mV - MAGNITUDE_SLOPE = 30 # dB/mV - return (reflection[0] - CENTER_POINT_MAGNITUDE) / MAGNITUDE_SLOPE - # Now we vary the tuning capacitor position and matching capacitor position # Step size tuner: TUNER_STEP_SIZE = 20 - # Step size matcher: - MATCHER_STEP_SIZE = 5 + MATCHER_STEP_SIZE = 50 - # We read the first reflection - last_reflection = self.read_reflection(next_frequency) - last_magnitude = get_magnitude(last_reflection) + TUNING_RANGE = 60 + MATCHING_RANGE = 500 - # Now we tune and match our probe coil for every frequency - stepper = self.module.model.tuning_stepper - new_magnitude = 1e6 # Big + tuning_backlash = 45 + # I'm not sure about this value ... + matching_backlash = 0 - # Probably also start a frequency sweep (?) + # Command for the position sweep: pt,,,m,,," + tuning_last_direction = self.module.model.tuning_stepper.last_direction + matching_last_direction = self.module.model.matching_stepper.last_direction + command = f"p{next_frequency}t{TUNING_RANGE},{TUNER_STEP_SIZE},{tuning_backlash},{tuning_last_direction}m{MATCHING_RANGE},{MATCHER_STEP_SIZE},{matching_backlash},{matching_last_direction}" - while new_magnitude > last_magnitude: - # We move the stepper - last_magnitude = new_magnitude + confirmation = self.send_command(command) - self.on_relative_move(TUNER_STEP_SIZE, stepper) + @pyqtSlot(str) + def process_position_sweep_result(self, text): + if text.startswith("z"): + text = text[1:] + # Format is z,m, + text = text.split("m") + tuning_position, tuning_last_direction = map(int, text[0].split(",")) + matching_position, matching_last_direction = map(int, text[1].split(",")) - # We read the reflection - new_magnitude = get_magnitude(self.read_reflection(next_frequency)) + # Keep backlash compensation consistent + self.module.model.tuning_stepper.last_direction = tuning_last_direction + self.module.model.matching_stepper.last_direction = matching_last_direction - # We move the stepper back - self.on_relative_move(-TUNER_STEP_SIZE, stepper) - logger.debug("Tuning capacitor position: %s", stepper.position) + logger.debug("Tuning position: %s, Matching position: %s", tuning_position, matching_position) + LUT = self.module.model.mech_lut + logger.debug("Received position sweep result: %s %s", matching_position, tuning_position) + LUT.add_positions(tuning_position, matching_position) + self.continue_or_finish_position_sweep(LUT) + + def continue_or_finish_position_sweep(self, LUT): + """Continue or finish the position sweep.""" + if LUT.is_incomplete(): + self.start_next_mechTM(LUT) + else: + self.finish_position_sweep(LUT) + + def finish_position_sweep(self, LUT): + """Finish the position sweep.""" + logger.debug("Finished position sweep") + self.module.model.mech_lut = LUT + self.module.model.LUT = LUT + # self.module.view.create_mech_LUT_spinner_dialog() + + + # This method isn't used anymore but it might be useful in the future so I'll keep it here def read_reflection(self, frequency) -> tuple: """Starts a reflection measurement and reads the reflection at the specified frequency.""" # We send the command to the atm system diff --git a/src/nqrduck_autotm/model.py b/src/nqrduck_autotm/model.py index 48502c1..49e3e2a 100644 --- a/src/nqrduck_autotm/model.py +++ b/src/nqrduck_autotm/model.py @@ -224,6 +224,10 @@ class MatchingStepper(Stepper): TYPE = "Matching" MAX_STEPS = 1e6 + def __init__(self) -> None: + super().__init__() + self.last_direction = None + class ElectricalLookupTable(LookupTable): TYPE = "Electrical" @@ -380,6 +384,8 @@ class AutoTMModel(ModuleModel): self.el_lut = None self.mech_lut = None + self.LUT = None + self.last_reflection = None @property diff --git a/src/nqrduck_autotm/resources/autotm_widget.ui b/src/nqrduck_autotm/resources/autotm_widget.ui index 753fb72..786475c 100644 --- a/src/nqrduck_autotm/resources/autotm_widget.ui +++ b/src/nqrduck_autotm/resources/autotm_widget.ui @@ -236,7 +236,7 @@ 0 - 50000 + 1000000 500 diff --git a/src/nqrduck_autotm/view.py b/src/nqrduck_autotm/view.py index c2932c8..fafae94 100644 --- a/src/nqrduck_autotm/view.py +++ b/src/nqrduck_autotm/view.py @@ -85,6 +85,8 @@ class AutoTMView(ModuleView): # On clicking of the viewLUTButton call the view_lut method self._ui_form.viewelLUTButton.clicked.connect(self.view_el_lut) + self._ui_form.viewmechLUTButton.clicked.connect(self.view_mech_lut) + # On clicking of the setvoltagesButton call the set_voltages method self._ui_form.setvoltagesButton.clicked.connect( lambda: self.module.controller.set_voltages( @@ -372,6 +374,16 @@ class AutoTMView(ModuleView): self.lut_window = self.LutWindow(self.module) self.lut_window.show() + def view_mech_lut(self) -> None: + """Creates a new Dialog that shows the currently active mechanical LUT.""" + logger.debug("View mechanical LUT") + if self.module.model.mech_lut is None: + logger.debug("No LUT available") + self.add_error_text("No LUT available") + return + self.lut_window = self.LutWindow(self.module) + self.lut_window.show() + class StepperSavedPositionsWindow(QDialog): def __init__(self, module, parent=None): super().__init__(parent) @@ -576,13 +588,20 @@ class AutoTMView(ModuleView): # Add vertical main layout main_layout = QVBoxLayout() + LUT = self.module.model.LUT + # Create table widget self.table_widget = QTableWidget() self.table_widget.setColumnCount(3) - self.table_widget.setHorizontalHeaderLabels( - ["Frequency (MHz)", "Matching Voltage", "Tuning Voltage"] - ) - LUT = self.module.model.LUT + if LUT.TYPE == "Mechanical": + self.table_widget.setHorizontalHeaderLabels( + ["Frequency (MHz)", "Tuning Position", "Matching Position"] + ) + elif LUT.TYPE == "Electrical": + self.table_widget.setHorizontalHeaderLabels( + ["Frequency (MHz)", "Matching Voltage", "Tuning Voltage"] + ) + for row, frequency in enumerate(LUT.data.keys()): self.table_widget.insertRow(row) self.table_widget.setItem(row, 0, QTableWidgetItem(str(frequency))) diff --git a/src/nqrduck_autotm/widget.py b/src/nqrduck_autotm/widget.py index a32d211..3b0fb83 100644 --- a/src/nqrduck_autotm/widget.py +++ b/src/nqrduck_autotm/widget.py @@ -1,4 +1,4 @@ -# Form implementation generated from reading ui file '../Modules/nqrduck-autotm/src/nqrduck_autotm/resources/autotm_widget.ui' +# Form implementation generated from reading ui file 'Modules/nqrduck-autotm/src/nqrduck_autotm/resources/autotm_widget.ui' # # Created by: PyQt6 UI code generator 6.5.1 # @@ -130,7 +130,7 @@ class Ui_Form(object): self.gridLayout_4.addWidget(self.label_20, 6, 0, 1, 1) self.stepsizeBox = QtWidgets.QSpinBox(parent=self.mechTab) self.stepsizeBox.setMinimum(0) - self.stepsizeBox.setMaximum(50000) + self.stepsizeBox.setMaximum(1000000) self.stepsizeBox.setProperty("value", 500) self.stepsizeBox.setObjectName("stepsizeBox") self.gridLayout_4.addWidget(self.stepsizeBox, 3, 1, 1, 1)