diff --git a/src/nqrduck_spectrometer_limenqr/__init__.py b/src/nqrduck_spectrometer_limenqr/__init__.py index e69de29..1e93582 100644 --- a/src/nqrduck_spectrometer_limenqr/__init__.py +++ b/src/nqrduck_spectrometer_limenqr/__init__.py @@ -0,0 +1 @@ +"""Init file for the nqrduck_spectrometer_limenqr package.""" \ No newline at end of file diff --git a/src/nqrduck_spectrometer_limenqr/controller.py b/src/nqrduck_spectrometer_limenqr/controller.py index b7b624e..b4db5e2 100644 --- a/src/nqrduck_spectrometer_limenqr/controller.py +++ b/src/nqrduck_spectrometer_limenqr/controller.py @@ -1,3 +1,5 @@ +"""Controller module for the Lime NQR spectrometer.""" + import logging import tempfile from pathlib import Path @@ -10,24 +12,33 @@ from nqrduck_spectrometer.base_spectrometer_controller import BaseSpectrometerCo from nqrduck_spectrometer.measurement import Measurement from nqrduck_spectrometer.pulseparameters import TXPulse, RXReadout + logger = logging.getLogger(__name__) class LimeNQRController(BaseSpectrometerController): + """Controller class for the Lime NQR spectrometer.""" + def __init__(self, module): + """Initializes the LimeNQRController.""" super().__init__(module) def start_measurement(self): + """Starts the measurement procedure.""" self.log_start_message() lime = self.initialize_lime() if lime is None: # Emit error message - self.emit_measurement_error("Error with Lime driver. Is the Lime driver installed?") + self.emit_measurement_error( + "Error with Lime driver. Is the Lime driver installed?" + ) return -1 elif lime.Npulses == 0: # Emit error message - self.emit_measurement_error("Error with pulse sequence. Is the pulse sequence empty?") + self.emit_measurement_error( + "Error with pulse sequence. Is the pulse sequence empty?" + ) return -1 self.setup_lime_parameters(lime) @@ -37,7 +48,9 @@ class LimeNQRController(BaseSpectrometerController): if not self.perform_measurement(lime): self.emit_status_message("Measurement failed") - self.emit_measurement_error("Error with measurement data. Did you set an RX event?") + self.emit_measurement_error( + "Error with measurement data. Did you set an RX event?" + ) return -1 measurement_data = self.process_measurement_results(lime) @@ -48,13 +61,15 @@ class LimeNQRController(BaseSpectrometerController): else: self.emit_measurement_error("Measurement failed. Unable to retrieve data.") - def log_start_message(self): - """This method logs a message when the measurement is started.""" - logger.debug("Starting measurement with spectrometer: %s", self.module.model.name) + def log_start_message(self) -> None: + """Logs a message when the measurement is started.""" + logger.debug( + "Starting measurement with spectrometer: %s", self.module.model.name + ) + + def initialize_lime(self) -> PyLimeConfig: + """Initializes the limr object that is used to communicate with the pulseN driver. - def initialize_lime(self): - """This method initializes the limr object that is used to communicate with the pulseN driver. - Returns: PyLimeConfig: The PyLimeConfig object that is used to communicate with the pulseN driver """ @@ -67,19 +82,20 @@ class LimeNQRController(BaseSpectrometerController): except Exception as e: logger.error("Error while initializing Lime driver: %s", e) import traceback + traceback.print_exc() return None - def setup_lime_parameters(self, lime): - """This method sets the parameters of the lime config according to the settings set in the spectrometer module. - + def setup_lime_parameters(self, lime: PyLimeConfig) -> None: + """Sets the parameters of the lime config according to the settings set in the spectrometer module. + Args: lime (PyLimeConfig): The PyLimeConfig object that is used to communicate with the pulseN driver """ - #lime.noi = -1 + # lime.noi = -1 lime.override_init = -1 - # + # # lime.nrp = 1 lime.repetitions = 1 lime = self.update_settings(lime) @@ -87,23 +103,23 @@ class LimeNQRController(BaseSpectrometerController): lime.averages = self.module.model.averages self.log_lime_parameters(lime) - def setup_temporary_storage(self, lime): - """This method sets up the temporary storage for the measurement data. - + def setup_temporary_storage(self, lime: PyLimeConfig) -> None: + """Sets up the temporary storage for the measurement data. + Args: lime (PyLimeConfig): The PyLimeConfig object that is used to communicate with the pulseN driver """ temp_dir = tempfile.TemporaryDirectory() logger.debug("Created temporary directory at: %s", temp_dir.name) - lime.save_path = str(Path(temp_dir.name)) + "/" # Temporary storage path - lime.file_pattern = "temp" # Temporary filename prefix or related config - - def perform_measurement(self, lime): - """This method executes the measurement procedure. - + lime.save_path = str(Path(temp_dir.name)) + "/" # Temporary storage path + lime.file_pattern = "temp" # Temporary filename prefix or related config + + def perform_measurement(self, lime: PyLimeConfig) -> bool: + """Executes the measurement procedure. + Args: lime (PyLimeConfig): The PyLimeConfig object that is used to communicate with the pulseN driver - + Returns: bool: True if the measurement was successful, False otherwise """ @@ -115,9 +131,9 @@ class LimeNQRController(BaseSpectrometerController): logger.error("Failed to execute the measurement: %s", e) return False - def process_measurement_results(self, lime): - """This method processes the measurement results and returns a Measurement object. - + def process_measurement_results(self, lime: PyLimeConfig) -> Measurement: + """Processes the measurement results and returns a Measurement object. + Args: lime (PyLimeConfig): The PyLimeConfig object that is used to communicate with the pulseN driver @@ -133,9 +149,11 @@ class LimeNQRController(BaseSpectrometerController): logger.debug("RX event begins at: %sµs and ends at: %sµs", rx_begin, rx_stop) return self.calculate_measurement_data(lime, rx_begin, rx_stop) - def calculate_measurement_data(self, lime, rx_begin, rx_stop): - """This method calculates the measurement data from the limr object. - + def calculate_measurement_data( + self, lime: PyLimeConfig, rx_begin: float, rx_stop: float + ) -> Measurement: + """Calculates the measurement data from the limr object. + Args: lime (PyLimeConfig): The PyLimeConfig object that is used to communicate with the pulseN driver rx_begin (float): The start time of the RX event in µs @@ -146,34 +164,44 @@ class LimeNQRController(BaseSpectrometerController): """ try: path = lime.get_path() - hdf = HDF(path) + hdf = HDF(path) evidx = self.find_evaluation_range_indices(hdf, rx_begin, rx_stop) tdx, tdy = self.extract_measurement_data(lime, hdf, evidx) fft_shift = self.get_fft_shift() - return Measurement(tdx, tdy, self.module.model.target_frequency, frequency_shift=fft_shift, IF_frequency=self.module.model.if_frequency) + return Measurement( + tdx, + tdy, + self.module.model.target_frequency, + frequency_shift=fft_shift, + IF_frequency=self.module.model.if_frequency, + ) except Exception as e: logger.error("Error processing measurement result: %s", e) return None - def find_evaluation_range_indices(self, hdf, rx_begin, rx_stop): - """This method finds the indices of the evaluation range in the measurement data. - + def find_evaluation_range_indices( + self, hdf: HDF, rx_begin: float, rx_stop: float + ) -> list: + """Finds the indices of the evaluation range in the measurement data. + Args: - HDF (HDF): The HDF object that is used to read the measurement data + hdf (HDF): The HDF object that is used to read the measurement data rx_begin (float): The start time of the RX event in µs rx_stop (float): The stop time of the RX event in µs - + Returns: - list: The indices of the evaluation range in the measurement data + list: The indices of the evaluation range in the measurement data """ return np.where((hdf.tdx > rx_begin) & (hdf.tdx < rx_stop))[0] - def extract_measurement_data(self, lime, hdf, indices): - """This method extracts the measurement data from the limr object. - + def extract_measurement_data( + self, lime: PyLimeConfig, hdf: HDF, indices: list + ) -> tuple: + """Extracts the measurement data from the PyLimeConfig object. + Args: lime (PyLimeConfig): The PyLimeConfig object that is used to communicate with the pulseN driver - HDF (HDF): The HDF object that is used to read the measurement data + hdf (HDF): The HDF object that is used to read the measurement data indices (list): The indices of the evaluation range in the measurement data Returns: @@ -185,59 +213,61 @@ class LimeNQRController(BaseSpectrometerController): tdy = tdy.flatten() return tdx, tdy - def get_fft_shift(self): - """This method returns the FFT shift value from the settings. - + def get_fft_shift(self) -> int: + """Rreturns the FFT shift value from the settings. + Returns: - int: The FFT shift value + int: The FFT shift value """ - fft_shift_enabled = self.module.model.get_setting_by_name(self.module.model.FFT_SHIFT).value + fft_shift_enabled = self.module.model.get_setting_by_name( + self.module.model.FFT_SHIFT + ).value return self.module.model.if_frequency if fft_shift_enabled else 0 - def emit_measurement_data(self, measurement_data): - """This method emits the measurement data to the GUI. - + def emit_measurement_data(self, measurement_data: Measurement) -> None: + """Emits the measurement data to the GUI. + Args: measurement_data (Measurement): The measurement data """ logger.debug("Emitting measurement data") self.module.nqrduck_signal.emit("measurement_data", measurement_data) - def emit_status_message(self, message): - """This method emits a status message to the GUI. - + def emit_status_message(self, message: str) -> None: + """Emits a status message to the GUI. + Args: message (str): The status message """ self.module.nqrduck_signal.emit("statusbar_message", message) - def emit_measurement_error(self, error_message): - """This method emits a measurement error to the GUI. - + def emit_measurement_error(self, error_message: str) -> None: + """Emits a measurement error to the GUI. + Args: error_message (str): The error message """ logger.error(error_message) self.module.nqrduck_signal.emit("measurement_error", error_message) - def log_lime_parameters(self, lime): - """This method logs the parameters of the limr object. - + def log_lime_parameters(self, lime: PyLimeConfig) -> None: + """Logs the parameters of the limr object. + Args: lime (PyLimeConfig): The PyLimeConfig object that is used to communicate with the pulseN driver """ # for key, value in lime.__dict__.items(): - # logger.debug("Lime parameter %s has value %s", key, value) + # logger.debug("Lime parameter %s has value %s", key, value) logger.debug("Lime parameter %s has value %s", "srate", lime.srate) - def update_settings(self, lime): - """This method sets the parameters of the limr object according to the settings set in the spectrometer module. + def update_settings(self, lime: PyLimeConfig) -> PyLimeConfig: + """Sets the parameters of the limr object according to the settings set in the spectrometer module. Args: lime (PyLimeConfig): The PyLimeConfig object that is used to communicate with the pulseN driver Returns: - lime: The updated limr object + lime: The updated limr object """ logger.debug( "Updating settings for spectrometer: %s for measurement", @@ -259,7 +289,9 @@ class LimeNQRController(BaseSpectrometerController): lime.RX_matching = setting.get_setting() # Careful this doesn't only set the IF frequency but the local oscillator frequency elif setting.name == self.module.model.IF_FREQUENCY: - lime.frq = self.module.model.target_frequency - setting.get_setting() + lime.frq = ( + self.module.model.target_frequency - setting.get_setting() + ) self.module.model.if_frequency = setting.get_setting() elif setting.name == self.module.model.ACQUISITION_TIME: lime.rectime_secs = setting.get_setting() @@ -309,9 +341,9 @@ class LimeNQRController(BaseSpectrometerController): lime.c3_tim = c3_tim return lime - def translate_pulse_sequence(self, lime): - """This method translates the pulse sequence to the limr object. - + def translate_pulse_sequence(self, lime: PyLimeConfig) -> PyLimeConfig: + """Ttranslates the pulse sequence to the limr object. + Args: lime (PyLimeConfig): The PyLimeConfig object that is used to communicate with the pulseN driver """ @@ -325,15 +357,25 @@ class LimeNQRController(BaseSpectrometerController): self.log_parameter_details(parameter) if self.is_translatable_tx_parameter(parameter): - pulse_shape, pulse_amplitude = self.prepare_pulse_amplitude(event, parameter) - pulse_amplitude, modulated_phase = self.modulate_pulse_amplitude(pulse_amplitude, event, lime) + pulse_shape, pulse_amplitude = self.prepare_pulse_amplitude( + event, parameter + ) + pulse_amplitude, modulated_phase = self.modulate_pulse_amplitude( + pulse_amplitude, event, lime + ) if first_pulse: # If the pulse frequency list is empty - pfr, pdr, pam, pof, pph = self.initialize_pulse_lists(lime, pulse_amplitude, pulse_shape, modulated_phase) + pfr, pdr, pam, pof, pph = self.initialize_pulse_lists( + lime, pulse_amplitude, pulse_shape, modulated_phase + ) first_pulse = False else: - pfr_ext, pdr_ext, pam_ext, pph_ext = self.extend_pulse_lists(lime, pulse_amplitude, pulse_shape, modulated_phase) - pof_ext = self.calculate_and_set_offsets(lime, pulse_shape, events, event, pulse_amplitude) + pfr_ext, pdr_ext, pam_ext, pph_ext = self.extend_pulse_lists( + pulse_amplitude, pulse_shape, modulated_phase + ) + pof_ext = self.calculate_and_set_offsets( + lime, pulse_shape, events, event, pulse_amplitude + ) pfr.extend(pfr_ext) pdr.extend(pdr_ext) @@ -350,11 +392,12 @@ class LimeNQRController(BaseSpectrometerController): lime.reptime_secs = float(event.duration) lime.Npulses = len(lime.p_frq) return lime - - def get_number_of_pulses(self): - """This method calculates the number of pulses in the pulse sequence before the LimeDriverBinding is initialized. + + def get_number_of_pulses(self) -> int: + """Calculates the number of pulses in the pulse sequence before the LimeDriverBinding is initialized. + This makes sure it"s initialized with the correct size of the pulse lists. - + Returns: int: The number of pulses in the pulse sequence """ @@ -371,53 +414,60 @@ class LimeNQRController(BaseSpectrometerController): # Helper functions below: - def fetch_pulse_sequence_events(self): - """This method fetches the pulse sequence events from the pulse programmer module. - + def fetch_pulse_sequence_events(self) -> list: + """Fetches the pulse sequence events from the pulse programmer module. + Returns: list: The pulse sequence events """ return self.module.model.pulse_programmer.model.pulse_sequence.events - def log_event_details(self, event): + def log_event_details(self, event) -> None: + """Logs the details of an event.""" logger.debug("Event %s has parameters: %s", event.name, event.parameters) - def log_parameter_details(self, parameter): + def log_parameter_details(self, parameter) -> None: + """Logs the details of a parameter.""" logger.debug("Parameter %s has options: %s", parameter.name, parameter.options) def is_translatable_tx_parameter(self, parameter): - """This method checks if a parameter a pulse with a transmit pulse shape (amplitude nonzero) - + """Checks if a parameter a pulse with a transmit pulse shape (amplitude nonzero). + Args: - parameter (Parameter): The parameter to check + parameter (Parameter): The parameter to check """ - return (parameter.name == self.module.model.TX and - parameter.get_option_by_name(TXPulse.RELATIVE_AMPLITUDE).value > 0) + return ( + parameter.name == self.module.model.TX + and parameter.get_option_by_name(TXPulse.RELATIVE_AMPLITUDE).value > 0 + ) def prepare_pulse_amplitude(self, event, parameter): - """This method prepares the pulse amplitude for the limr object. - + """Prepares the pulse amplitude for the limr object. + Args: event (Event): The event that contains the parameter parameter (Parameter): The parameter that contains the pulse shape and amplitude - + Returns: tuple: A tuple containing the pulse shape and the pulse amplitude """ pulse_shape = parameter.get_option_by_name(TXPulse.TX_PULSE_SHAPE).value - pulse_amplitude = abs(pulse_shape.get_pulse_amplitude(event.duration)) * \ - (parameter.get_option_by_name(TXPulse.RELATIVE_AMPLITUDE).value / 100) + pulse_amplitude = abs(pulse_shape.get_pulse_amplitude(event.duration)) * ( + parameter.get_option_by_name(TXPulse.RELATIVE_AMPLITUDE).value / 100 + ) pulse_amplitude = np.clip(pulse_amplitude, -0.99, 0.99) return pulse_shape, pulse_amplitude - def modulate_pulse_amplitude(self, pulse_amplitude, event, lime): - """This method modulates the pulse amplitude for the limr object. We need to do this to have the pulse at IF frequency instead of LO frequency. - + def modulate_pulse_amplitude( + self, pulse_amplitude: float, event, lime: PyLimeConfig + ) -> tuple: + """Modulates the pulse amplitude for the limr object. We need to do this to have the pulse at IF frequency instead of LO frequency. + Args: pulse_amplitude (float): The pulse amplitude event (Event): The event that contains the parameter lime (PyLimeConfig) : The PyLimeConfig object that is used to communicate with the pulseN driver - + Returns: tuple: A tuple containing the modulated pulse amplitude and the modulated phase """ @@ -430,58 +480,72 @@ class LimeNQRController(BaseSpectrometerController): modulated_phase = self.unwrap_phase(np.angle(pulse_complex)) return modulated_amplitude, modulated_phase - def unwrap_phase(self, phase): + def unwrap_phase(self, phase: float) -> float: """This method unwraps the phase of the pulse. - + Args: - phase (float): The phase of the pulse + phase (float): The phase of the pulse """ return (np.unwrap(phase) + 2 * np.pi) % (2 * np.pi) - def initialize_pulse_lists(self, lime, pulse_amplitude, pulse_shape, modulated_phase): + def initialize_pulse_lists( + self, + lime: PyLimeConfig, + pulse_amplitude: np.array, + pulse_shape, + modulated_phase: np.array, + ) -> tuple: """This method initializes the pulse lists of the limr object. - + Args: lime (PyLimeConfig): The PyLimeConfig object that is used to communicate with the pulseN driver - pulse_amplitude (float): The pulse amplitude - pulse_shape (PulseShape): The pulse shape - modulated_phase (float): The modulated phase + pulse_amplitude (np.array): The pulse amplitude + pulse_shape (Function): The pulse shape + modulated_phase (np.array): The modulated phase """ pfr = [float(self.module.model.if_frequency)] * len(pulse_amplitude) - # We set the first len(pulse_amplitude) of the p_dur + # We set the first len(pulse_amplitude) of the p_dur pdr = [float(pulse_shape.resolution)] * len(pulse_amplitude) pam = list(pulse_amplitude) - pof = ([self.module.model.OFFSET_FIRST_PULSE] + - [int(pulse_shape.resolution * lime.srate)] * (len(pulse_amplitude) - 1)) + pof = [self.module.model.OFFSET_FIRST_PULSE] + [ + int(pulse_shape.resolution * lime.srate) + ] * (len(pulse_amplitude) - 1) pph = list(modulated_phase) - return pfr, pdr, pam, pof, pph + return pfr, pdr, pam, pof, pph - def extend_pulse_lists(self, lime, pulse_amplitude, pulse_shape, modulated_phase): + def extend_pulse_lists(self, pulse_amplitude, pulse_shape, modulated_phase): """This method extends the pulse lists of the limr object. - + Args: - lime (PyLimeConfig): The PyLimeConfig object that is used to communicate with the pulseN driver pulse_amplitude (float): The pulse amplitude pulse_shape (PulseShape): The pulse shape modulated_phase (float): The modulated phase + + Returns: + tuple: A tuple containing the extended pulse lists (frequency, duration, amplitude, phase) """ - pfr = ([float(self.module.model.if_frequency)] * len(pulse_amplitude)) - pdr = ([float(pulse_shape.resolution)] * len(pulse_amplitude)) - pam = (list(pulse_amplitude)) - pph = (list(modulated_phase)) + pfr = [float(self.module.model.if_frequency)] * len(pulse_amplitude) + pdr = [float(pulse_shape.resolution)] * len(pulse_amplitude) + pam = list(pulse_amplitude) + pph = list(modulated_phase) return pfr, pdr, pam, pph - def calculate_and_set_offsets(self, lime, pulse_shape, events, current_event, pulse_amplitude): + def calculate_and_set_offsets( + self, lime: PyLimeConfig, pulse_shape, events, current_event, pulse_amplitude + ) -> list: """This method calculates and sets the offsets for the limr object. - + Args: lime (PyLimeConfig): The PyLimeConfig object that is used to communicate with the pulseN driver - pulse_shape (PulseShape): The pulse shape + pulse_shape (Function): The pulse shape events (list): The pulse sequence events current_event (Event): The current event pulse_amplitude (float): The pulse amplitude + + Returns: + list: The offsets for the limr object """ blank_durations = self.get_blank_durations_before_event(events, current_event) @@ -500,29 +564,33 @@ class LimeNQRController(BaseSpectrometerController): pof.extend([offset_per_sample] * (len(pulse_amplitude) - 1)) return pof - def get_blank_durations_before_event(self, events, current_event): + # This method could be refactored in a potential pulse sequence module + def get_blank_durations_before_event(self, events, current_event) -> list: """This method returns the blank durations before the current event. - + Args: events (list): The pulse sequence events current_event (Event): The current event - + Returns: list: The blank durations before the current event """ blank_durations = [] - previous_events_without_tx_pulse = self.get_previous_events_without_tx_pulse(events, current_event) + previous_events_without_tx_pulse = self.get_previous_events_without_tx_pulse( + events, current_event + ) for event in previous_events_without_tx_pulse: blank_durations.append(float(event.duration)) return blank_durations - def get_previous_events_without_tx_pulse(self, events, current_event): + # This method could be refactored in a potential pulse sequence module + def get_previous_events_without_tx_pulse(self, events, current_event) -> list: """This method returns the previous events without a transmit pulse. - + Args: events (list): The pulse sequence events current_event (Event): The current event - + Returns: list: The previous events without a transmit pulse """ @@ -530,15 +598,19 @@ class LimeNQRController(BaseSpectrometerController): previous_events = events[:index] result = [] for event in reversed(previous_events): - translatable = any(self.is_translatable_tx_parameter(param) for param in event.parameters.values()) + translatable = any( + self.is_translatable_tx_parameter(param) + for param in event.parameters.values() + ) if not translatable: result.append(event) else: break - return reversed(result) # Reversed to maintain the original order if needed elsewhere + return reversed( + result + ) # Reversed to maintain the original order if needed elsewhere - - def translate_rx_event(self, lime): + def translate_rx_event(self, lime : PyLimeConfig) -> tuple: """This method translates the RX event of the pulse sequence to the limr object. Args: @@ -547,28 +619,34 @@ class LimeNQRController(BaseSpectrometerController): Returns: tuple: A tuple containing the start and stop time of the RX event in µs """ - CORRECTION_FACTOR = self.module.model.get_setting_by_name(self.module.model.RX_OFFSET).value + CORRECTION_FACTOR = self.module.model.get_setting_by_name( + self.module.model.RX_OFFSET + ).value events = self.module.model.pulse_programmer.model.pulse_sequence.events rx_event = self.find_rx_event(events) if not rx_event: return None, None - previous_events_duration = self.calculate_previous_events_duration(events, rx_event) + previous_events_duration = self.calculate_previous_events_duration( + events, rx_event + ) rx_duration = float(rx_event.duration) offset = self.calculate_offset(lime) - rx_begin = float(previous_events_duration) + float(offset) + float(CORRECTION_FACTOR) + rx_begin = ( + float(previous_events_duration) + float(offset) + float(CORRECTION_FACTOR) + ) rx_stop = rx_begin + rx_duration return rx_begin * 1e6, rx_stop * 1e6 def find_rx_event(self, events): """This method finds the RX event in the pulse sequence. - + Args: events (list): The pulse sequence events - + Returns: Event: The RX event """ @@ -582,30 +660,29 @@ class LimeNQRController(BaseSpectrometerController): def calculate_previous_events_duration(self, events, rx_event): """This method calculates the duration of the previous events. - + Args: events (list): The pulse sequence events rx_event (Event): The RX event - + Returns: float: The duration of the previous events """ previous_events = events[: events.index(rx_event)] return sum(event.duration for event in previous_events) - def calculate_offset(self, lime): + def calculate_offset(self, lime :PyLimeConfig) -> float: """This method calculates the offset for the RX event. Args: - lime (limr): The limr object that is used to communicate with the pulseN driver - + lime (limr): The limr object that is used to communicate with the pulseN driver + Returns: float: The offset for the RX event """ return self.module.model.OFFSET_FIRST_PULSE * (1 / lime.srate) - - def set_frequency(self, value: float): + def set_frequency(self, value: float) -> None: """This method sets the target frequency of the spectrometer. Args: @@ -622,7 +699,7 @@ class LimeNQRController(BaseSpectrometerController): ) self.module.nqrduck_signal.emit("failure_set_frequency", value) - def set_averages(self, value: int): + def set_averages(self, value: int) -> None: """This method sets the number of averages for the spectrometer. Args: diff --git a/src/nqrduck_spectrometer_limenqr/limenqr.py b/src/nqrduck_spectrometer_limenqr/limenqr.py index 2861114..5643d59 100644 --- a/src/nqrduck_spectrometer_limenqr/limenqr.py +++ b/src/nqrduck_spectrometer_limenqr/limenqr.py @@ -1,6 +1,8 @@ +"""BaseSpectrometer for Lime NQR spectrometer.""" + from nqrduck_spectrometer.base_spectrometer import BaseSpectrometer from .model import LimeNQRModel from .view import LimeNQRView from .controller import LimeNQRController -LimeNQR = BaseSpectrometer(LimeNQRModel, LimeNQRView, LimeNQRController) \ No newline at end of file +LimeNQR = BaseSpectrometer(LimeNQRModel, LimeNQRView, LimeNQRController) diff --git a/src/nqrduck_spectrometer_limenqr/model.py b/src/nqrduck_spectrometer_limenqr/model.py index 127de54..30dc5c5 100644 --- a/src/nqrduck_spectrometer_limenqr/model.py +++ b/src/nqrduck_spectrometer_limenqr/model.py @@ -1,12 +1,20 @@ +"""Model for the Lime NQR spectrometer.""" + import logging from nqrduck_spectrometer.base_spectrometer_model import BaseSpectrometerModel from nqrduck_spectrometer.pulseparameters import TXPulse, RXReadout -from nqrduck_spectrometer.settings import FloatSetting, IntSetting, BooleanSetting, SelectionSetting +from nqrduck_spectrometer.settings import ( + FloatSetting, + IntSetting, + BooleanSetting, + SelectionSetting, +) logger = logging.getLogger(__name__) -class LimeNQRModel(BaseSpectrometerModel): +class LimeNQRModel(BaseSpectrometerModel): + """Model for the Lime NQR spectrometer.""" # Setting constants for the names of the spectrometer settings CHANNEL = "TX/RX Channel" TX_MATCHING = "TX Matching" @@ -48,92 +56,229 @@ class LimeNQRModel(BaseSpectrometerModel): # Settings that are not changed by the user OFFSET_FIRST_PULSE = 300 - def __init__(self, module) -> None: + """Initializes the Lime NQR model.""" super().__init__(module) # Acquisition settings channel_options = ["0", "1"] - channel_setting = SelectionSetting(self.CHANNEL, channel_options, "0", "TX/RX Channel") + channel_setting = SelectionSetting( + self.CHANNEL, channel_options, "0", "TX/RX Channel" + ) self.add_setting(channel_setting, self.ACQUISITION) tx_matching_options = ["0", "1"] - tx_matching_setting = SelectionSetting(self.TX_MATCHING, tx_matching_options, "0", "TX Matching") + tx_matching_setting = SelectionSetting( + self.TX_MATCHING, tx_matching_options, "0", "TX Matching" + ) self.add_setting(tx_matching_setting, self.ACQUISITION) rx_matching_options = ["0", "1"] - rx_matching_setting = SelectionSetting(self.RX_MATCHING, rx_matching_options, "0", "RX Matching") + rx_matching_setting = SelectionSetting( + self.RX_MATCHING, rx_matching_options, "0", "RX Matching" + ) self.add_setting(rx_matching_setting, self.ACQUISITION) - sampling_frequency_setting = FloatSetting(self.SAMPLING_FREQUENCY, 30.72e6, "The rate at which the spectrometer samples the input signal.", min_value=0, max_value=30.72e6) + sampling_frequency_setting = FloatSetting( + self.SAMPLING_FREQUENCY, + 30.72e6, + "The rate at which the spectrometer samples the input signal.", + min_value=0, + max_value=30.72e6, + ) self.add_setting(sampling_frequency_setting, self.ACQUISITION) - if_frequency_setting = FloatSetting(self.IF_FREQUENCY, 5e6, "The intermediate frequency to which the input signal is down converted during analog-to-digital conversion.", min_value=0) + if_frequency_setting = FloatSetting( + self.IF_FREQUENCY, + 5e6, + "The intermediate frequency to which the input signal is down converted during analog-to-digital conversion.", + min_value=0, + ) self.add_setting(if_frequency_setting, self.ACQUISITION) self.if_frequency = 5e6 - acquisition_time_setting = FloatSetting(self.ACQUISITION_TIME, 82e-6, "Acquisition time - this is from the beginning of the pulse sequence", min_value=0) + acquisition_time_setting = FloatSetting( + self.ACQUISITION_TIME, + 82e-6, + "Acquisition time - this is from the beginning of the pulse sequence", + min_value=0, + ) self.add_setting(acquisition_time_setting, self.ACQUISITION) # Gate Settings - gate_enable_setting = BooleanSetting(self.GATE_ENABLE, True, "Setting that controls whether gate is on during transmitting.") + gate_enable_setting = BooleanSetting( + self.GATE_ENABLE, + True, + "Setting that controls whether gate is on during transmitting.", + ) self.add_setting(gate_enable_setting, self.GATE_SETTINGS) - gate_padding_left_setting = IntSetting(self.GATE_PADDING_LEFT, 10, "The number of samples by which to extend the gate window to the left.", min_value=0) + gate_padding_left_setting = IntSetting( + self.GATE_PADDING_LEFT, + 10, + "The number of samples by which to extend the gate window to the left.", + min_value=0, + ) self.add_setting(gate_padding_left_setting, self.GATE_SETTINGS) - gate_padding_right_setting = IntSetting(self.GATE_PADDING_RIGHT, 10, "The number of samples by which to extend the gate window to the right.", min_value=0) + gate_padding_right_setting = IntSetting( + self.GATE_PADDING_RIGHT, + 10, + "The number of samples by which to extend the gate window to the right.", + min_value=0, + ) self.add_setting(gate_padding_right_setting, self.GATE_SETTINGS) - gate_shift_setting = IntSetting(self.GATE_SHIFT, 53, "The delay, in number of samples, by which the gate window is shifted.", min_value=0) + gate_shift_setting = IntSetting( + self.GATE_SHIFT, + 53, + "The delay, in number of samples, by which the gate window is shifted.", + min_value=0, + ) self.add_setting(gate_shift_setting, self.GATE_SETTINGS) # RX/TX settings - rx_gain_setting = IntSetting(self.RX_GAIN, 55, "The gain level of the receiver’s amplifier.", min_value=0, max_value=55, spin_box=(True, True)) + rx_gain_setting = IntSetting( + self.RX_GAIN, + 55, + "The gain level of the receiver’s amplifier.", + min_value=0, + max_value=55, + spin_box=(True, True), + ) self.add_setting(rx_gain_setting, self.RX_TX_SETTINGS) - tx_gain_setting = IntSetting(self.TX_GAIN, 30, "The gain level of the transmitter’s amplifier.", min_value=0, max_value=55, spin_box=(True, True)) + tx_gain_setting = IntSetting( + self.TX_GAIN, + 30, + "The gain level of the transmitter’s amplifier.", + min_value=0, + max_value=55, + spin_box=(True, True), + ) self.add_setting(tx_gain_setting, self.RX_TX_SETTINGS) - rx_lpf_bw_setting = FloatSetting(self.RX_LPF_BW, 30.72e6/2, "The bandwidth of the receiver’s low-pass filter which attenuates frequencies below a certain threshold.") + rx_lpf_bw_setting = FloatSetting( + self.RX_LPF_BW, + 30.72e6 / 2, + "The bandwidth of the receiver’s low-pass filter which attenuates frequencies below a certain threshold.", + ) self.add_setting(rx_lpf_bw_setting, self.RX_TX_SETTINGS) - tx_lpf_bw_setting = FloatSetting(self.TX_LPF_BW, 130.0e6, "The bandwidth of the transmitter’s low-pass filter which limits the frequency range of the transmitted signa") + tx_lpf_bw_setting = FloatSetting( + self.TX_LPF_BW, + 130.0e6, + "The bandwidth of the transmitter’s low-pass filter which limits the frequency range of the transmitted signa", + ) self.add_setting(tx_lpf_bw_setting, self.RX_TX_SETTINGS) # Calibration settings - tx_i_dc_correction_setting = IntSetting(self.TX_I_DC_CORRECTION, -45, "Adjusts the direct current offset errors in the in-phase (I) component of the transmit (TX) path.", min_value=-128, max_value=127, spin_box=(True, True)) + tx_i_dc_correction_setting = IntSetting( + self.TX_I_DC_CORRECTION, + -45, + "Adjusts the direct current offset errors in the in-phase (I) component of the transmit (TX) path.", + min_value=-128, + max_value=127, + spin_box=(True, True), + ) self.add_setting(tx_i_dc_correction_setting, self.CALIBRATION) - tx_q_dc_correction_setting = IntSetting(self.TX_Q_DC_CORRECTION, 0, "Adjusts the direct current offset errors in the quadrature (Q) component of the transmit (TX) path.", min_value=-128, max_value=127, spin_box=(True, True)) + tx_q_dc_correction_setting = IntSetting( + self.TX_Q_DC_CORRECTION, + 0, + "Adjusts the direct current offset errors in the quadrature (Q) component of the transmit (TX) path.", + min_value=-128, + max_value=127, + spin_box=(True, True), + ) self.add_setting(tx_q_dc_correction_setting, self.CALIBRATION) - tx_i_gain_correction_setting = IntSetting(self.TX_I_GAIN_CORRECTION, 2047, "Modifies the gain settings for the I channel of the TX path, adjusting for imbalances.", min_value=0, max_value=2047, spin_box=(True, True)) + tx_i_gain_correction_setting = IntSetting( + self.TX_I_GAIN_CORRECTION, + 2047, + "Modifies the gain settings for the I channel of the TX path, adjusting for imbalances.", + min_value=0, + max_value=2047, + spin_box=(True, True), + ) self.add_setting(tx_i_gain_correction_setting, self.CALIBRATION) - - tx_q_gain_correction_setting = IntSetting(self.TX_Q_GAIN_CORRECTION, 2039, "Modifies the gain settings for the Q channel of the TX path, adjusting for imbalances.", min_value=0, max_value=2047, spin_box=(True, True)) + + tx_q_gain_correction_setting = IntSetting( + self.TX_Q_GAIN_CORRECTION, + 2039, + "Modifies the gain settings for the Q channel of the TX path, adjusting for imbalances.", + min_value=0, + max_value=2047, + spin_box=(True, True), + ) self.add_setting(tx_q_gain_correction_setting, self.CALIBRATION) - tx_phase_adjustment_setting = IntSetting(self.TX_PHASE_ADJUSTMENT, 3, "Corrects the Phase of I Q signals in the TX path.", min_value=-2048, max_value=2047, spin_box=(True, True)) + tx_phase_adjustment_setting = IntSetting( + self.TX_PHASE_ADJUSTMENT, + 3, + "Corrects the Phase of I Q signals in the TX path.", + min_value=-2048, + max_value=2047, + spin_box=(True, True), + ) self.add_setting(tx_phase_adjustment_setting, self.CALIBRATION) - rx_i_dc_correction_setting = IntSetting(self.RX_I_DC_CORRECTION, 0, "Adjusts the direct current offset errors in the in-phase (I) component of the receive (RX) path.", min_value=-63, max_value=63, spin_box=(True, True)) + rx_i_dc_correction_setting = IntSetting( + self.RX_I_DC_CORRECTION, + 0, + "Adjusts the direct current offset errors in the in-phase (I) component of the receive (RX) path.", + min_value=-63, + max_value=63, + spin_box=(True, True), + ) self.add_setting(rx_i_dc_correction_setting, self.CALIBRATION) - rx_q_dc_correction_setting = IntSetting(self.RX_Q_DC_CORRECTION, 0, "Adjusts the direct current offset errors in the quadrature (Q) component of the receive (RX) path.", min_value=-63, max_value=63, spin_box=(True, True)) + rx_q_dc_correction_setting = IntSetting( + self.RX_Q_DC_CORRECTION, + 0, + "Adjusts the direct current offset errors in the quadrature (Q) component of the receive (RX) path.", + min_value=-63, + max_value=63, + spin_box=(True, True), + ) self.add_setting(rx_q_dc_correction_setting, self.CALIBRATION) - rx_i_gain_correction_setting = IntSetting(self.RX_I_GAIN_CORRECTION, 2047, "Modifies the gain settings for the I channel of the RX path, adjusting for imbalances.", min_value=0, max_value=2047, spin_box=(True, True)) + rx_i_gain_correction_setting = IntSetting( + self.RX_I_GAIN_CORRECTION, + 2047, + "Modifies the gain settings for the I channel of the RX path, adjusting for imbalances.", + min_value=0, + max_value=2047, + spin_box=(True, True), + ) self.add_setting(rx_i_gain_correction_setting, self.CALIBRATION) - rx_q_gain_correction_setting = IntSetting(self.RX_Q_GAIN_CORRECTION, 2047, "Modifies the gain settings for the Q channel of the RX path, adjusting for imbalances.", min_value=0, max_value=2047, spin_box=(True, True)) + rx_q_gain_correction_setting = IntSetting( + self.RX_Q_GAIN_CORRECTION, + 2047, + "Modifies the gain settings for the Q channel of the RX path, adjusting for imbalances.", + min_value=0, + max_value=2047, + spin_box=(True, True), + ) self.add_setting(rx_q_gain_correction_setting, self.CALIBRATION) - rx_phase_adjustment_setting = IntSetting(self.RX_PHASE_ADJUSTMENT, 0, "Corrects the Phase of I Q signals in the RX path.", min_value=-2048, max_value=2047, spin_box=(True, True)) + rx_phase_adjustment_setting = IntSetting( + self.RX_PHASE_ADJUSTMENT, + 0, + "Corrects the Phase of I Q signals in the RX path.", + min_value=-2048, + max_value=2047, + spin_box=(True, True), + ) self.add_setting(rx_phase_adjustment_setting, self.CALIBRATION) - + # Signal Processing settings - rx_offset_setting = FloatSetting(self.RX_OFFSET, 2.4e-6, "The offset of the RX event, this changes all the time") + rx_offset_setting = FloatSetting( + self.RX_OFFSET, + 2.4e-6, + "The offset of the RX event, this changes all the time", + ) self.add_setting(rx_offset_setting, self.SIGNAL_PROCESSING) fft_shift_setting = BooleanSetting(self.FFT_SHIFT, False, "FFT shift") @@ -147,6 +292,7 @@ class LimeNQRModel(BaseSpectrometerModel): # Try to load the pulse programmer module try: from nqrduck_pulseprogrammer.pulseprogrammer import pulse_programmer + self.pulse_programmer = pulse_programmer logger.debug("Pulse programmer found.") self.pulse_programmer.controller.on_loading(self.pulse_parameter_options) @@ -157,26 +303,27 @@ class LimeNQRModel(BaseSpectrometerModel): @property def target_frequency(self): + """The target frequency of the spectrometer.""" return self._target_frequency - + @target_frequency.setter def target_frequency(self, value): self._target_frequency = value @property def averages(self): + """The number of averages to be taken.""" return self._averages - + @averages.setter def averages(self, value): self._averages = value @property def if_frequency(self): + """The intermediate frequency to which the input signal is down converted during analog-to-digital conversion.""" return self._if_frequency - + @if_frequency.setter def if_frequency(self, value): self._if_frequency = value - - diff --git a/src/nqrduck_spectrometer_limenqr/view.py b/src/nqrduck_spectrometer_limenqr/view.py index 984ea82..23bbb2c 100644 --- a/src/nqrduck_spectrometer_limenqr/view.py +++ b/src/nqrduck_spectrometer_limenqr/view.py @@ -1,9 +1,13 @@ +"""View for LimeNQR spectrometer.""" from nqrduck_spectrometer.base_spectrometer_view import BaseSpectrometerView class LimeNQRView(BaseSpectrometerView): + """View class for LimeNQR spectrometer.""" + def __init__(self, module): + """Initialize the LimeNQRView object.""" super().__init__(module) + # Setting UI is automatically generated based on the settings specified in the model self.widget = self.load_settings_ui() -