Implemented LUT creation for mechanical probe coils.

This commit is contained in:
jupfi 2023-12-12 16:05:30 +01:00
parent 3046b6c7d5
commit bdfe4e3e9c
5 changed files with 86 additions and 34 deletions

View file

@ -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.print_info)
self.module.model.serial_data_received.connect(self.read_position_data) 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_reflection_data)
self.module.model.serial_data_received.connect(self.process_position_sweep_result)
@pyqtSlot(str, object) @pyqtSlot(str, object)
def process_signals(self, key: str, value: object) -> None: def process_signals(self, key: str, value: object) -> None:
@ -216,7 +217,7 @@ class AutoTMController(ModuleController):
if text.startswith("v"): if text.startswith("v"):
text = text[1:].split("t") text = text[1:].split("t")
matching_voltage, tuning_voltage = map(float, text) 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) logger.debug("Received voltage sweep result: %s %s", matching_voltage, tuning_voltage)
LUT.add_voltages(matching_voltage, tuning_voltage) LUT.add_voltages(matching_voltage, tuning_voltage)
self.continue_or_finish_voltage_sweep(LUT) self.continue_or_finish_voltage_sweep(LUT)
@ -265,6 +266,7 @@ class AutoTMController(ModuleController):
LUT (LookupTable): The lookup table that is being generated.""" LUT (LookupTable): The lookup table that is being generated."""
logger.debug("Voltage sweep finished") logger.debug("Voltage sweep finished")
self.module.view.el_LUT_spinner.hide() self.module.view.el_LUT_spinner.hide()
self.module.model.LUT = LUT
self.module.model.voltage_sweep_stop = time.time() self.module.model.voltage_sweep_stop = time.time()
duration = self.module.model.voltage_sweep_stop - self.module.model.voltage_sweep_start 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") 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) confirmation = self.send_command(command)
# If the command was send successfully, we set the LUT # If the command was send successfully, we set the LUT
if confirmation: if confirmation:
self.module.model.LUT = LUT self.module.model.el_lut = LUT
self.module.view.create_el_LUT_spinner_dialog() self.module.view.create_el_LUT_spinner_dialog()
def switch_to_preamp(self) -> None: def switch_to_preamp(self) -> None:
@ -674,6 +676,7 @@ class AutoTMController(ModuleController):
logger.debug("Homing") logger.debug("Homing")
self.send_command("h") self.send_command("h")
self.module.model.tuning_stepper.last_direction = 1 self.module.model.tuning_stepper.last_direction = 1
self.module.model.matching_stepper.last_direction = 1
@pyqtSlot(str) @pyqtSlot(str)
def on_stepper_changed(self, stepper: str) -> None: 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.""" """Send a command to the stepper motor based on the number of steps."""
# Here we handle backlash of the tuner # Here we handle backlash of the tuner
# Determine the direction of the current steps # Determine the direction of the current steps
backlash = 0
current_direction = np.sign(steps) # This will be -1,or 1 current_direction = np.sign(steps) # This will be -1,or 1
if stepper.TYPE == "Tuning": if stepper.TYPE == "Tuning":
logger.debug("Stepper last direction: %s", stepper.last_direction) logger.debug("Stepper last direction: %s", stepper.last_direction)
logger.debug("Current direction: %s", current_direction) logger.debug("Current direction: %s", current_direction)
if stepper.last_direction != 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 stepper.last_direction = current_direction
logger.debug("Stepper last direction: %s", stepper.last_direction) logger.debug("Stepper last direction: %s", stepper.last_direction)
motor_identifier = stepper.TYPE.lower()[0] motor_identifier = stepper.TYPE.lower()[0]
command = f"m{motor_identifier}{steps}" command = f"m{motor_identifier}{steps},{backlash}"
confirmation = self.send_command(command) confirmation = self.send_command(command)
return confirmation return confirmation
@ -893,6 +897,8 @@ class AutoTMController(ModuleController):
# Lock GUI # Lock GUI
# self.module.view.create_mech_LUT_spinner_dialog() # self.module.view.create_mech_LUT_spinner_dialog()
self.module.model.mech_lut = LUT
self.start_next_mechTM(LUT) self.start_next_mechTM(LUT)
@ -903,41 +909,62 @@ class AutoTMController(ModuleController):
LUT.started_frequency = next_frequency LUT.started_frequency = next_frequency
logger.debug("Starting next mechanical tuning and matching:") 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 # Now we vary the tuning capacitor position and matching capacitor position
# Step size tuner: # Step size tuner:
TUNER_STEP_SIZE = 20 TUNER_STEP_SIZE = 20
# Step size matcher: # Step size matcher:
MATCHER_STEP_SIZE = 5 MATCHER_STEP_SIZE = 50
# We read the first reflection TUNING_RANGE = 60
last_reflection = self.read_reflection(next_frequency) MATCHING_RANGE = 500
last_magnitude = get_magnitude(last_reflection)
# Now we tune and match our probe coil for every frequency tuning_backlash = 45
stepper = self.module.model.tuning_stepper # I'm not sure about this value ...
new_magnitude = 1e6 # Big matching_backlash = 0
# Probably also start a frequency sweep (?) # Command for the position sweep: p<frequency in MHz>t<range>,<step size>,<backlash>,<last_direction>m<range>,<step size>,<backlash>,<last_direction>"
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: confirmation = self.send_command(command)
# We move the stepper
last_magnitude = new_magnitude
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<tuning_position>,<tuning_last_direction>m<matching_position>,<matching_last_direction>
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 # Keep backlash compensation consistent
new_magnitude = get_magnitude(self.read_reflection(next_frequency)) 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 logger.debug("Tuning position: %s, Matching position: %s", tuning_position, matching_position)
self.on_relative_move(-TUNER_STEP_SIZE, stepper)
logger.debug("Tuning capacitor position: %s", stepper.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: def read_reflection(self, frequency) -> tuple:
"""Starts a reflection measurement and reads the reflection at the specified frequency.""" """Starts a reflection measurement and reads the reflection at the specified frequency."""
# We send the command to the atm system # We send the command to the atm system

View file

@ -224,6 +224,10 @@ class MatchingStepper(Stepper):
TYPE = "Matching" TYPE = "Matching"
MAX_STEPS = 1e6 MAX_STEPS = 1e6
def __init__(self) -> None:
super().__init__()
self.last_direction = None
class ElectricalLookupTable(LookupTable): class ElectricalLookupTable(LookupTable):
TYPE = "Electrical" TYPE = "Electrical"
@ -380,6 +384,8 @@ class AutoTMModel(ModuleModel):
self.el_lut = None self.el_lut = None
self.mech_lut = None self.mech_lut = None
self.LUT = None
self.last_reflection = None self.last_reflection = None
@property @property

View file

@ -236,7 +236,7 @@
<number>0</number> <number>0</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>50000</number> <number>1000000</number>
</property> </property>
<property name="value"> <property name="value">
<number>500</number> <number>500</number>

View file

@ -85,6 +85,8 @@ class AutoTMView(ModuleView):
# On clicking of the viewLUTButton call the view_lut method # On clicking of the viewLUTButton call the view_lut method
self._ui_form.viewelLUTButton.clicked.connect(self.view_el_lut) 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 # On clicking of the setvoltagesButton call the set_voltages method
self._ui_form.setvoltagesButton.clicked.connect( self._ui_form.setvoltagesButton.clicked.connect(
lambda: self.module.controller.set_voltages( lambda: self.module.controller.set_voltages(
@ -372,6 +374,16 @@ class AutoTMView(ModuleView):
self.lut_window = self.LutWindow(self.module) self.lut_window = self.LutWindow(self.module)
self.lut_window.show() 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): class StepperSavedPositionsWindow(QDialog):
def __init__(self, module, parent=None): def __init__(self, module, parent=None):
super().__init__(parent) super().__init__(parent)
@ -576,13 +588,20 @@ class AutoTMView(ModuleView):
# Add vertical main layout # Add vertical main layout
main_layout = QVBoxLayout() main_layout = QVBoxLayout()
LUT = self.module.model.LUT
# Create table widget # Create table widget
self.table_widget = QTableWidget() self.table_widget = QTableWidget()
self.table_widget.setColumnCount(3) self.table_widget.setColumnCount(3)
if LUT.TYPE == "Mechanical":
self.table_widget.setHorizontalHeaderLabels(
["Frequency (MHz)", "Tuning Position", "Matching Position"]
)
elif LUT.TYPE == "Electrical":
self.table_widget.setHorizontalHeaderLabels( self.table_widget.setHorizontalHeaderLabels(
["Frequency (MHz)", "Matching Voltage", "Tuning Voltage"] ["Frequency (MHz)", "Matching Voltage", "Tuning Voltage"]
) )
LUT = self.module.model.LUT
for row, frequency in enumerate(LUT.data.keys()): for row, frequency in enumerate(LUT.data.keys()):
self.table_widget.insertRow(row) self.table_widget.insertRow(row)
self.table_widget.setItem(row, 0, QTableWidgetItem(str(frequency))) self.table_widget.setItem(row, 0, QTableWidgetItem(str(frequency)))

View file

@ -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 # 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.gridLayout_4.addWidget(self.label_20, 6, 0, 1, 1)
self.stepsizeBox = QtWidgets.QSpinBox(parent=self.mechTab) self.stepsizeBox = QtWidgets.QSpinBox(parent=self.mechTab)
self.stepsizeBox.setMinimum(0) self.stepsizeBox.setMinimum(0)
self.stepsizeBox.setMaximum(50000) self.stepsizeBox.setMaximum(1000000)
self.stepsizeBox.setProperty("value", 500) self.stepsizeBox.setProperty("value", 500)
self.stepsizeBox.setObjectName("stepsizeBox") self.stepsizeBox.setObjectName("stepsizeBox")
self.gridLayout_4.addWidget(self.stepsizeBox, 3, 1, 1, 1) self.gridLayout_4.addWidget(self.stepsizeBox, 3, 1, 1, 1)