From 2e8084aae2fc0b2c4f3b250542b5a40df38951cd Mon Sep 17 00:00:00 2001 From: jupfi Date: Wed, 3 Jan 2024 15:25:31 +0100 Subject: [PATCH] Added basic apodization. --- pyproject.toml | 3 + src/nqrduck_measurement/controller.py | 55 ++++++++++++++++++- src/nqrduck_measurement/model.py | 1 + .../resources/measurement_widget.ui | 8 +-- .../signalprocessing_options.py | 34 ++++++++++++ src/nqrduck_measurement/view.py | 3 +- src/nqrduck_measurement/widget.py | 32 +++++------ 7 files changed, 114 insertions(+), 22 deletions(-) create mode 100644 src/nqrduck_measurement/signalprocessing_options.py diff --git a/pyproject.toml b/pyproject.toml index e5e4c64..66ac57f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,10 @@ classifiers = [ dependencies = [ "matplotlib", "pyqt6", + "sympy", "NQRduck", + "nqrduck-pulseprogrammer", + "nqrduck-spectrometer", ] [project.entry-points."nqrduck"] diff --git a/src/nqrduck_measurement/controller.py b/src/nqrduck_measurement/controller.py index 4fc128b..6a3e1a5 100644 --- a/src/nqrduck_measurement/controller.py +++ b/src/nqrduck_measurement/controller.py @@ -1,7 +1,12 @@ import logging import json +import numpy as np +from decimal import Decimal from PyQt6.QtCore import pyqtSlot, pyqtSignal -from PyQt6.QtWidgets import QWidget +import nqrduck.helpers.signalprocessing as sp +from nqrduck_pulseprogrammer.view import OptionsDialog +from nqrduck_spectrometer.pulsesequence import PulseSequence +from .signalprocessing_options import Apodization from nqrduck.module.module_controller import ModuleController from nqrduck_spectrometer.measurement import Measurement @@ -144,3 +149,51 @@ class MeasurementController(ModuleController): self.module.nqrduck_signal.emit( "notification", ["Error", "File is not a valid measurement file."] ) + + def show_apodization_dialog(self) -> None: + """Show apodization dialog.""" + logger.debug("Showing apodization dialog.") + # First we check if there is a measurement. + if not self.module.model.displayed_measurement: + logger.debug("No measurement to apodize.") + self.module.nqrduck_signal.emit( + "notification", ["Error", "No measurement to apodize."] + ) + return + + # We need to create a event which corresponds to the measurement. + event_duration = self.module.model.displayed_measurement.tdx[-1] * 1e-6 + + event = PulseSequence.Event(name="Apodization", duration=str(event_duration)) + parameter = Apodization() + parameter.start_x = 0 + parameter.end_x = event_duration + dialog = OptionsDialog(event, parameter, self.module.view) + result = dialog.exec() + + if result: + for option, function in dialog.return_functions.items(): + logger.debug("Setting option %s of parameter %s in event %s to %s", option, parameter, event, function()) + option.set_value(function()) + + # Get the function from the Apodization function + function = parameter.get_option_by_name(Apodization.APODIZATION_FUNCTIONS).value + logger.debug("Apodization function: %s", function) + + # Get the y data weights from the function + resolution = (self.module.model.displayed_measurement.tdx[1] - self.module.model.displayed_measurement.tdx[0]) * 1e-6 + y_weight = function.get_pulse_amplitude(event.duration, Decimal(resolution)) + #Append the last point to the end of the array + y_weight = np.append(y_weight, y_weight[-1]) + + tdy_measurement = self.module.model.displayed_measurement.tdy * y_weight + + measurement = Measurement( + self.module.model.displayed_measurement.tdx, + tdy_measurement, + target_frequency=self.module.model.displayed_measurement.target_frequency, + IF_frequency=self.module.model.displayed_measurement.IF_frequency, + ) + + self.module.model.displayed_measurement = measurement + self.module.model.add_measurement(measurement) \ No newline at end of file diff --git a/src/nqrduck_measurement/model.py b/src/nqrduck_measurement/model.py index e03d021..f3da639 100644 --- a/src/nqrduck_measurement/model.py +++ b/src/nqrduck_measurement/model.py @@ -20,6 +20,7 @@ class MeasurementModel(ModuleModel): super().__init__(module) self.view_mode = self.TIME_VIEW self.measurements = [] + self._displayed_measurement = None @property def view_mode(self): diff --git a/src/nqrduck_measurement/resources/measurement_widget.ui b/src/nqrduck_measurement/resources/measurement_widget.ui index 2534895..9cb8402 100644 --- a/src/nqrduck_measurement/resources/measurement_widget.ui +++ b/src/nqrduck_measurement/resources/measurement_widget.ui @@ -116,28 +116,28 @@ - + Apodization - + Baseline Correction - + Peak-Picking - + Fitting diff --git a/src/nqrduck_measurement/signalprocessing_options.py b/src/nqrduck_measurement/signalprocessing_options.py new file mode 100644 index 0000000..950d4a5 --- /dev/null +++ b/src/nqrduck_measurement/signalprocessing_options.py @@ -0,0 +1,34 @@ +import sympy +from nqrduck_spectrometer.base_spectrometer_model import BaseSpectrometerModel +from nqrduck_spectrometer.pulseparameters import FunctionOption, NumericOption, GaussianFunction, CustomFunction, Function + +# We implement the signal processing options as PulseParamterOptions because we can then easily use the automatic UI generation + +class FIDFunction(Function): + """The exponetial FID function.""" + + name = "FID" + + def __init__(self) -> None: + expr = sympy.sympify("exp( -x / T2star )") + super().__init__(expr) + self.start_x = 0 + self.end_x = 30 + + self.add_parameter(Function.Parameter("T2star (microseconds)", "T2star", 10)) + + +class Apodization(BaseSpectrometerModel.PulseParameter): + APODIZATION_FUNCTIONS = "Apodization functions" + + def __init__(self): + super().__init__("Apodization") + + self.add_option( + FunctionOption( + self.APODIZATION_FUNCTIONS, + [FIDFunction(), GaussianFunction(), CustomFunction()], + ), + ) + + diff --git a/src/nqrduck_measurement/view.py b/src/nqrduck_measurement/view.py index dbb2e47..ecd5980 100644 --- a/src/nqrduck_measurement/view.py +++ b/src/nqrduck_measurement/view.py @@ -60,6 +60,8 @@ class MeasurementView(ModuleView): self.module.controller.set_frequency_failure.connect(self.on_set_frequency_failure) self.module.controller.set_averages_failure.connect(self.on_set_averages_failure) + self._ui_form.apodizationButton.clicked.connect(self.module.controller.show_apodization_dialog) + # Call validator for buttonStart # Add logos @@ -226,4 +228,3 @@ class MeasurementView(ModuleView): self.spinner_movie.stop() super().hide() - diff --git a/src/nqrduck_measurement/widget.py b/src/nqrduck_measurement/widget.py index 444b7c8..b4bbf0d 100644 --- a/src/nqrduck_measurement/widget.py +++ b/src/nqrduck_measurement/widget.py @@ -68,18 +68,18 @@ class Ui_Form(object): self.spLabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.spLabel.setObjectName("spLabel") self.settingsLayout.addWidget(self.spLabel) - self.pushButton_2 = QtWidgets.QPushButton(parent=Form) - self.pushButton_2.setObjectName("pushButton_2") - self.settingsLayout.addWidget(self.pushButton_2) - self.pushButton = QtWidgets.QPushButton(parent=Form) - self.pushButton.setObjectName("pushButton") - self.settingsLayout.addWidget(self.pushButton) - self.pushButton_3 = QtWidgets.QPushButton(parent=Form) - self.pushButton_3.setObjectName("pushButton_3") - self.settingsLayout.addWidget(self.pushButton_3) - self.pushButton_4 = QtWidgets.QPushButton(parent=Form) - self.pushButton_4.setObjectName("pushButton_4") - self.settingsLayout.addWidget(self.pushButton_4) + self.apodizationButton = QtWidgets.QPushButton(parent=Form) + self.apodizationButton.setObjectName("apodizationButton") + self.settingsLayout.addWidget(self.apodizationButton) + self.baselineButton = QtWidgets.QPushButton(parent=Form) + self.baselineButton.setObjectName("baselineButton") + self.settingsLayout.addWidget(self.baselineButton) + self.peakButton = QtWidgets.QPushButton(parent=Form) + self.peakButton.setObjectName("peakButton") + self.settingsLayout.addWidget(self.peakButton) + self.fittingButton = QtWidgets.QPushButton(parent=Form) + self.fittingButton.setObjectName("fittingButton") + self.settingsLayout.addWidget(self.fittingButton) self.spsettingsButton = QtWidgets.QPushButton(parent=Form) self.spsettingsButton.setObjectName("spsettingsButton") self.settingsLayout.addWidget(self.spsettingsButton) @@ -137,10 +137,10 @@ class Ui_Form(object): self.frequencyunitLabel.setText(_translate("Form", "MHz")) self.buttonStart.setText(_translate("Form", "Start Measurement")) self.spLabel.setText(_translate("Form", "Signal Processing")) - self.pushButton_2.setText(_translate("Form", "Apodization")) - self.pushButton.setText(_translate("Form", "Baseline Correction")) - self.pushButton_3.setText(_translate("Form", "Peak-Picking")) - self.pushButton_4.setText(_translate("Form", "Fitting")) + self.apodizationButton.setText(_translate("Form", "Apodization")) + self.baselineButton.setText(_translate("Form", "Baseline Correction")) + self.peakButton.setText(_translate("Form", "Peak-Picking")) + self.fittingButton.setText(_translate("Form", "Fitting")) self.spsettingsButton.setText(_translate("Form", "Settings")) self.exportButton.setText(_translate("Form", "Export Measurement")) self.importButton.setText(_translate("Form", "Import Measurement"))