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.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: 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:
# 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<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
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

View file

@ -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

View file

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

View file

@ -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)))

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
#
@ -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)