diff --git a/src/nqrduck_autotm/controller.py b/src/nqrduck_autotm/controller.py index 9736f31..a65fd2f 100644 --- a/src/nqrduck_autotm/controller.py +++ b/src/nqrduck_autotm/controller.py @@ -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, LookupTable +from .model import S11Data, ElectricalLookupTable, MechanicalLookupTable logger = logging.getLogger(__name__) @@ -16,6 +16,25 @@ logger = logging.getLogger(__name__) class AutoTMController(ModuleController): BAUDRATE = 115200 + @pyqtSlot(str, object) + def process_signals(self, key: str, value: object) -> None: + logger.debug("Received signal: %s", key) + if key == "set_tune_and_match": + self.tune_and_match(value) + + def tune_and_match(self, frequency: float) -> None: + """ This method is called when this module already has a LUT table. It should then tune and match the probe coil to the specified frequency. + """ + if self.module.model.LUT is None: + logger.error("Could not tune and match. No LUT available.") + return + elif self.module.model.LUT.TYPE == "Electrical": + tunning_voltage, matching_voltage = self.module.model.LUT.get_voltages(frequency) + self.set_voltages(str(tunning_voltage), str(matching_voltage)) + + elif self.module.model.LUT.TYPE == "Mechanical": + pass + def find_devices(self) -> None: """Scan for available serial devices and add them to the model as available devices.""" logger.debug("Scanning for available serial devices") @@ -436,7 +455,11 @@ class AutoTMController(ModuleController): ) command = "v%sv%s" % (matching_voltage, tuning_voltage) - self.send_command(command) + confirmation = self.send_command(command) + if confirmation: + logger.debug("Voltages set successfully") + # Emit nqrduck signal that T&M was successful + self.module.nqrduck_signal.emit("confirm_tune_and_match", None) def generate_lut( self, @@ -482,7 +505,8 @@ class AutoTMController(ModuleController): self.module.view.add_info_text(error) return - if frequency_step > (stop_frequency - start_frequency): + # - 0.1 is to prevent float errors + if frequency_step - 0.1 > (stop_frequency - start_frequency): error = "Could not generate LUT. Frequency step must be smaller than the frequency range" logger.error(error) self.module.view.add_info_text(error) @@ -496,7 +520,7 @@ class AutoTMController(ModuleController): ) # We create the lookup table - LUT = LookupTable( + LUT = ElectricalLookupTable( start_frequency, stop_frequency, frequency_step ) diff --git a/src/nqrduck_autotm/model.py b/src/nqrduck_autotm/model.py index 9ce0fb1..1e407ca 100644 --- a/src/nqrduck_autotm/model.py +++ b/src/nqrduck_autotm/model.py @@ -178,16 +178,6 @@ class LookupTable: # This is the frequency at which the tuning and matching process was started self.started_frequency = None - 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.started_frequency = frequency - self.add_voltages(None, None) - 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. @@ -214,6 +204,33 @@ class LookupTable: return frequency return None + + def get_entry_number(self, frequency: float) -> int: + """This method returns the entry number of the given frequency. + + Args: + frequency (float): The frequency for which the entry number should be returned. + + Returns: + int: The entry number of the given frequency. + """ + # Round to closest integer + return int(round((frequency - self.start_frequency) / self.frequency_step)) + +class ElectricalLookupTable(LookupTable): + TYPE = "Electrical" + + def __init__(self, start_frequency: float, stop_frequency: float, frequency_step: float) -> None: + 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.started_frequency = frequency + self.add_voltages(None, None) def add_voltages(self, tuning_voltage: float, matching_voltage: float) -> None: """Add a tuning and matching voltage for the last started frequency to the lookup table. @@ -223,7 +240,22 @@ class LookupTable: matching_voltage (float): The matching voltage for the given frequency.""" self.data[self.started_frequency] = (tuning_voltage, matching_voltage) + def get_voltages(self, frequency: float) -> tuple: + """Get the tuning and matching voltage for the given frequency. + Args: + frequency (float): The frequency for which the tuning and matching voltage should be returned. + + Returns: + tuple: The tuning and matching voltage for the given frequency. + """ + entry_number = self.get_entry_number(frequency) + key = list(self.data.keys())[entry_number] + return self.data[key] + +class MechanicalLookupTable(LookupTable): + TYPE = "Mechanical" + pass class AutoTMModel(ModuleModel): available_devices_changed = pyqtSignal(list) serial_changed = pyqtSignal(QSerialPort)