From ecd779ed1ff62e2664ed487d81d9a0cf5fe81a7d Mon Sep 17 00:00:00 2001 From: jupfi Date: Thu, 13 Jul 2023 15:58:12 +0200 Subject: [PATCH] Started pulse sequence translation. --- .gitignore | 6 +- .../contrib/__init__.py | 0 .../controller.py | 89 +++++++++++++++++++ src/nqrduck_spectrometer_limenqr/model.py | 87 ++++++++---------- 4 files changed, 129 insertions(+), 53 deletions(-) create mode 100644 src/nqrduck_spectrometer_limenqr/contrib/__init__.py diff --git a/.gitignore b/.gitignore index c1a36ca..98def11 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,8 @@ build/ venv/ # Other -*.DS_Store \ No newline at end of file +*.DS_Store + +# Contrib files without permission +src/nqrduck_spectrometer_limenqr/contrib/limr.py +src/nqrduck_spectrometer_limenqr/contrib/pulseN_test_USB.cpp \ No newline at end of file diff --git a/src/nqrduck_spectrometer_limenqr/contrib/__init__.py b/src/nqrduck_spectrometer_limenqr/contrib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/nqrduck_spectrometer_limenqr/controller.py b/src/nqrduck_spectrometer_limenqr/controller.py index b82f8c4..53db04b 100644 --- a/src/nqrduck_spectrometer_limenqr/controller.py +++ b/src/nqrduck_spectrometer_limenqr/controller.py @@ -1,4 +1,6 @@ import logging +from pathlib import Path +import numpy as np from nqrduck.module.module_controller import ModuleController from nqrduck_spectrometer.base_spectrometer_controller import BaseSpectrometerController @@ -10,3 +12,90 @@ class LimeNQRController(BaseSpectrometerController): def start_measurement(self): logger.debug("Starting measurement with spectrometer: %s", self.module.model.name) + # Now we request the pulse sequence set in the pulse programmer module + pulse_sequence = self.module.model.pulse_programmer.model.pulse_sequence + logger.debug("Pulse sequence is: %s", pulse_sequence.dump_sequence_data()) + + try: + from .contrib.limr import limr + self_path = Path(__file__).parent + driver_path = str(self_path / "contrib/pulseN_test_USB.cpp") + lime = limr(driver_path) + except ImportError as e: + logger.error("Error while importing limr. %s", e) + except Exception as e: + logger.error("Error while loading pulseN_test_USB.cpp: %s", e) + + lime = self.update_settings(lime) + lime = self.translate_pulse_sequence(lime) + + def update_settings(self, lime): + logger.debug("Updating settings for spectrometer: %s for measurement", self.module.model.name) + l.t3d = [0, 0, 0, 0] + for category in self.module.model.settings.keys(): + for setting in self.module.model.settings[category]: + logger.debug("Setting %s has value %s", setting.name, setting.value) + if setting.name == "RX Gain": + lime.rgn = setting.value + elif setting.name == "TX Gain": + lime.tgn = setting.value + elif setting.name == "Averages": + lime.nav = setting.value + elif setting.name == "Sampling Frequency": + lime.sra = setting.value + elif setting.name == "RX LPF BW": + lime.rlp = setting.value + elif setting.name == "TX LPF BW": + lime.tlp = setting.value + elif setting.name == "IF frequency": + lime.lof = self.target_frequency - setting.value + elif setting.name == "Acquisition time": + lime.tac = 82e-6 + elif setting.name == "Enable": + lime.t3d[0] = int(setting.value) + elif setting.name == "Gate padding left": + lime.t3d[1] = setting.value + elif setting.name == "Gate shift": + lime.t3d[2] = setting.value + elif setting.name == "Gate padding right": + lime.t3d[3] = setting.value + + return lime + + def translate_pulse_sequence(self, lime): + """This method translates the pulse sequence into the format required by the lime spectrometer. + """ + + for event in self.module.model.pulse_programmer.model.pulse_sequence.events.values(): + logger.debug("Event %s has parameters: %s", event.name, event.parameters) + for parameter in event.parameters.values(): + logger.debug("Parameter %s has options: %s", parameter.name, parameter.options) + + if parameter.name == "TX": + + if len(lime.pfr) == 0: + # Add the TX pulse to the pulse frequency list (lime.pfr) + lime.pfr = [self.module.model.if_frequency] + # Add the duration of the TX pulse to the pulse duration list (lime.pdr) + lime.pdr = [event.duration] + # Add the TX pulse amplitude to the pulse amplitude list (lime.pam) + lime.pam = [parameter.options["TX Amplitude"].value] + # Add the pulse offset to the pulse offset list (lime.pof) + # This leads to a default offset of 300 samples for the first pulse + lime.pof = [300] + # Add the TX pulse phase to the pulse phase list (lime.pph) -> not yet implemented + else: + lime.pfr.append(self.module.model.if_frequency) + lime.pdr.append(event.duration) + lime.pam.append(parameter.options["TX Amplitude"].value) + lime.pof.append(np.ceil(lime.pfr[-2] * lime.sra)) + + # The acquisition time can be calculated from the buffer length of 4096 samples and the sampling frequency + # 82µs is the shortest possible acquisition time + + # The last event is the repetition time event + lime.trp = event.duration + + lime.npu = len(lime.pfr) + return lime + \ No newline at end of file diff --git a/src/nqrduck_spectrometer_limenqr/model.py b/src/nqrduck_spectrometer_limenqr/model.py index 3d97357..4dd3a52 100644 --- a/src/nqrduck_spectrometer_limenqr/model.py +++ b/src/nqrduck_spectrometer_limenqr/model.py @@ -10,18 +10,42 @@ class LimeNQRModel(BaseSpectrometerModel): def __init__(self, module) -> None: super().__init__(module) - self.add_setting("Frequency", 100, "Experiment frequency", "Acquisition") + # Acquisition settings + self.add_setting("Frequency", 100e6, "Experiment frequency", "Acquisition") + self.target_frequency = 100e6 self.add_setting("Averages", 100, "Number of averages", "Acquisition") self.add_setting("Sampling Frequency", 30.72e6 , "Sampling frequency", "Acquisition") self.add_setting("IF Frequency", 1.2e6, "IF frequency", "Acquisition") + self.if_frequency = 1.2e6 + self.add_setting("Acquisition time", 82e-6, "Acquisition time - this is from the beginning of the pulse sequence", "Acquisition") + # Gate Settings + self.add_setting("Enable", True, "Enable", "Gate Settings") + self.add_setting("Gate padding left", 10, "Gate padding left", "Gate Settings") + self.add_setting("Gate padding right", 10, "Gate padding right", "Gate Settings") + self.add_setting("Gate shift", 53, "Gate shift", "Gate Settings") + # RX/TX settings self.add_setting("RX Gain", 55, "RX Gain", "RX/TX Settings") self.add_setting("TX Gain", 40, "TX Gain", "RX/TX Settings") self.add_setting("RX LPF BW", 30.72e6/2, "RX LPF BW", "RX/TX Settings") self.add_setting("TX LPF BW", 130.0e6, "TX LPF BW", "RX/TX Settings") + # Calibration settings + self.add_setting("TX I DC correction", -45, "TX I DC correction", "Calibration") + self.add_setting("TX Q DC correction", 0, "TX Q DC correction", "Calibration") + self.add_setting("TX I Gain correction", 2047, "TX I Gain correction", "Calibration") + self.add_setting("TX Q Gain correction", 2039, "TX Q Gain correction", "Calibration") + self.add_setting("TX phase adjustment", 3, "TX phase adjustment", "Calibration") + self.add_setting("RX I DC correction", 0, "TX I DC correction", "Calibration") + self.add_setting("RX Q DC correction", 0, "TX Q DC correction", "Calibration") + self.add_setting("RX I Gain correction", 2047, "TX I Gain correction", "Calibration") + self.add_setting("RX Q Gain correction", 2047, "TX Q Gain correction", "Calibration") + self.add_setting("RX phase adjustment", 0, "TX phase adjustment", "Calibration") + + # Pulse parameter options self.add_pulse_parameter_option("TX", TXPulse) self.add_pulse_parameter_option("Gate", Gate) self.add_pulse_parameter_option("RX", RXReadout) + # Try to load the pulse programmer module try: from nqrduck_pulseprogrammer.pulseprogrammer import pulse_programmer self.pulse_programmer = pulse_programmer @@ -31,60 +55,19 @@ class LimeNQRModel(BaseSpectrometerModel): logger.warning("No pulse programmer found.") @property - def rx_antenna(self): - return self._rx_antenna + def target_frequency(self): + return self._target_frequency - @rx_antenna.setter - def rx_antenna(self, value): - self._rx_antenna = value + @target_frequency.setter + def target_frequency(self, value): + self._target_frequency = value @property - def tx_antenna(self): - return self._tx_antenna + def if_frequency(self): + return self._if_frequency - @tx_antenna.setter - def tx_antenna(self, value): - self._tx_antenna = value + @if_frequency.setter + def if_frequency(self, value): + self._if_frequency = value - @property - def rx_gain(self): - return self._rx_gain - - @rx_gain.setter - def rx_gain(self, value): - self._rx_gain = value - - @property - def tx_gain(self): - return self._tx_gain - - @tx_gain.setter - def tx_gain(self, value): - self._tx_gain = value - - @property - def rx_lpfbw(self): - return self._rx_lpfbw - - @rx_lpfbw.setter - def rx_lpfbw(self, value): - self._rx_lpfbw = value - - @property - def tx_lpfbw(self): - return self._tx_lpfbw - - @tx_lpfbw.setter - def tx_lpfbw(self, value): - self._tx_lpfbw = value - - # Pulse params - - @property - def tx_freq(self): - return self._tx_freq - - @tx_freq.setter - def tx_freq(self, value): - self._tx_freq = value