mirror of
https://github.com/nqrduck/nqrduck-spectrometer.git
synced 2024-12-30 12:18:09 +00:00
Linting
This commit is contained in:
parent
3a455428e5
commit
a758d20c03
15 changed files with 661 additions and 195 deletions
|
@ -32,5 +32,31 @@ dependencies = [
|
|||
"scipy",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"black",
|
||||
"pydocstyle",
|
||||
"pyupgrade",
|
||||
"ruff",
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
exclude = [
|
||||
"widget.py",
|
||||
"base_spectrometer_widget.py",
|
||||
]
|
||||
|
||||
[tool.ruff.lint]
|
||||
extend-select = [
|
||||
"UP", # pyupgrade
|
||||
"D", # pydocstyle
|
||||
]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"__init__.py" = ["F401"]
|
||||
|
||||
[tool.ruff.lint.pydocstyle]
|
||||
convention = "google"
|
||||
|
||||
[project.entry-points."nqrduck"]
|
||||
"nqrduck-spectrometer" = "nqrduck_spectrometer.spectrometer:Spectrometer"
|
|
@ -1 +1,2 @@
|
|||
"""Init for importing the module."""
|
||||
from .spectrometer import Spectrometer as Module
|
|
@ -1,27 +1,39 @@
|
|||
"""Base Spectrometer Module."""
|
||||
|
||||
from PyQt6.QtCore import pyqtSignal
|
||||
from nqrduck.module.module import Module
|
||||
|
||||
|
||||
class BaseSpectrometer(Module):
|
||||
"""Base class for all spectrometers. All spectrometers should inherit from this class."""
|
||||
"""Base class for all spectrometers. All spectrometers should inherit from this class.
|
||||
|
||||
Args:
|
||||
Model (SpectrometerModel) : The model of the spectrometer
|
||||
View (SpectrometerView) : The view of the spectrometer
|
||||
Controller (SpectrometerController) : The controller of the spectrometer
|
||||
|
||||
Signals:
|
||||
change_spectrometer (str) : Signal emitted when the spectrometer is changed
|
||||
"""
|
||||
|
||||
change_spectrometer = pyqtSignal(str)
|
||||
|
||||
def __init__(self, model, view, controller):
|
||||
"""Initializes the spectrometer."""
|
||||
super().__init__(model, None, controller)
|
||||
# This stops the view from being added to the main window.
|
||||
self.view = None
|
||||
self.settings_view = view(self)
|
||||
|
||||
|
||||
def set_active(self):
|
||||
"""Sets the spectrometer as the active spectrometer.
|
||||
"""
|
||||
"""Sets the spectrometer as the active spectrometer."""
|
||||
self.change_spectrometer.emit(self._model.name)
|
||||
|
||||
@property
|
||||
def settings_view(self):
|
||||
"""The settings view of the spectrometer.
|
||||
"""
|
||||
"""The settings view of the spectrometer."""
|
||||
return self._settings_view
|
||||
|
||||
|
||||
@settings_view.setter
|
||||
def settings_view(self, value):
|
||||
self._settings_view = value
|
||||
|
|
|
@ -1,20 +1,26 @@
|
|||
"""Base class for all spectrometer controllers."""
|
||||
|
||||
from nqrduck.module.module_controller import ModuleController
|
||||
|
||||
|
||||
class BaseSpectrometerController(ModuleController):
|
||||
"""The base class for all spectrometer controllers."""
|
||||
|
||||
def __init__(self, module):
|
||||
"""Initializes the spectrometer controller."""
|
||||
super().__init__(module)
|
||||
|
||||
def start_measurement(self):
|
||||
"""Starts the measurement.
|
||||
|
||||
This method should be called when the measurement is started.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def set_frequency(self, value):
|
||||
"""Sets the frequency of the spectrometer."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def set_averages(self, value):
|
||||
raise NotImplementedError
|
||||
"""Sets the number of averages."""
|
||||
raise NotImplementedError
|
||||
|
|
|
@ -1,110 +1,127 @@
|
|||
"""The base class for all spectrometer models."""
|
||||
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
from PyQt6.QtCore import pyqtSlot, pyqtSignal, QObject
|
||||
from PyQt6.QtGui import QPixmap
|
||||
from nqrduck.module.module_model import ModuleModel
|
||||
from.settings import Setting
|
||||
from .settings import Setting
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseSpectrometerModel(ModuleModel):
|
||||
"""The base class for all spectrometer models.
|
||||
|
||||
It contains the settings and pulse parameters of the spectrometer.
|
||||
|
||||
Arguments:
|
||||
module (Module) -- The module that the spectrometer is connected to
|
||||
|
||||
|
||||
Args:
|
||||
module (Module) : The module that the spectrometer is connected to
|
||||
|
||||
Attributes:
|
||||
settings (OrderedDict) -- The settings of the spectrometer
|
||||
pulse_parameter_options (OrderedDict) -- The pulse parameter options of the spectrometer
|
||||
settings (OrderedDict) : The settings of the spectrometer
|
||||
pulse_parameter_options (OrderedDict) : The pulse parameter options of the spectrometer
|
||||
"""
|
||||
settings : OrderedDict
|
||||
pulse_parameter_options : OrderedDict
|
||||
|
||||
|
||||
settings: OrderedDict
|
||||
pulse_parameter_options: OrderedDict
|
||||
|
||||
class PulseParameter:
|
||||
"""A pulse parameter is a value that can be different for each event in a pulse sequence.
|
||||
|
||||
E.g. the transmit pulse power or the phase of the transmit pulse.
|
||||
|
||||
Arguments:
|
||||
name (str) -- The name of the pulse parameter
|
||||
|
||||
|
||||
Args:
|
||||
name (str) : The name of the pulse parameter
|
||||
|
||||
Attributes:
|
||||
name (str) -- The name of the pulse parameter
|
||||
options (OrderedDict) -- The options of the pulse parameter
|
||||
name (str) : The name of the pulse parameter
|
||||
options (OrderedDict) : The options of the pulse parameter
|
||||
"""
|
||||
def __init__(self, name : str):
|
||||
|
||||
def __init__(self, name: str):
|
||||
"""Initializes the pulse parameter.
|
||||
|
||||
|
||||
Arguments:
|
||||
name (str) -- The name of the pulse parameter
|
||||
name (str) : The name of the pulse parameter
|
||||
"""
|
||||
self.name = name
|
||||
self.options = list()
|
||||
|
||||
def get_pixmap(self):
|
||||
|
||||
def get_pixmap(self) -> QPixmap:
|
||||
"""Gets the pixmap of the pulse parameter.
|
||||
|
||||
Implment this method in the derived class.
|
||||
|
||||
Returns:
|
||||
QPixmap : The pixmap of the pulse parameter
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def add_option(self, option : "Option") -> None:
|
||||
|
||||
def add_option(self, option: "Option") -> None:
|
||||
"""Adds an option to the pulse parameter.
|
||||
|
||||
Arguments:
|
||||
option (Option) -- The option to add
|
||||
|
||||
Args:
|
||||
option (Option) : The option to add
|
||||
"""
|
||||
self.options.append(option)
|
||||
|
||||
def get_options(self) -> list:
|
||||
""" Gets the options of the pulse parameter.
|
||||
|
||||
"""Gets the options of the pulse parameter.
|
||||
|
||||
Returns:
|
||||
list -- The options of the pulse parameter
|
||||
list : The options of the pulse parameter
|
||||
"""
|
||||
return self.options
|
||||
|
||||
def get_option_by_name(self, name : str) -> "Option":
|
||||
|
||||
def get_option_by_name(self, name: str) -> "Option":
|
||||
"""Gets an option by its name.
|
||||
|
||||
Arguments:
|
||||
name (str) -- The name of the option
|
||||
Args:
|
||||
name (str) : The name of the option
|
||||
|
||||
Returns:
|
||||
Option -- The option with the specified name
|
||||
|
||||
Option : The option with the specified name
|
||||
|
||||
Raises:
|
||||
ValueError -- If no option with the specified name is found
|
||||
ValueError : If no option with the specified name is found
|
||||
"""
|
||||
|
||||
for option in self.options:
|
||||
if option.name == name:
|
||||
return option
|
||||
raise ValueError("Option with name %s not found" % name)
|
||||
|
||||
|
||||
def __init__(self, module):
|
||||
"""Initializes the spectrometer model.
|
||||
|
||||
Arguments:
|
||||
module (Module) -- The module that the spectrometer is connected to
|
||||
|
||||
Args:
|
||||
module (Module) : The module that the spectrometer is connected to
|
||||
"""
|
||||
super().__init__(module)
|
||||
self.settings = OrderedDict()
|
||||
self.pulse_parameter_options = OrderedDict()
|
||||
|
||||
def add_setting(self, setting : Setting, category : str) -> None:
|
||||
def add_setting(self, setting: Setting, category: str) -> None:
|
||||
"""Adds a setting to the spectrometer.
|
||||
|
||||
Args:
|
||||
setting (Setting) : The setting to add
|
||||
category (str) : The category of the setting
|
||||
"""
|
||||
if category not in self.settings.keys():
|
||||
self.settings[category] = []
|
||||
self.settings[category].append(setting)
|
||||
|
||||
def get_setting_by_name(self, name : str) -> Setting:
|
||||
def get_setting_by_name(self, name: str) -> Setting:
|
||||
"""Gets a setting by its name.
|
||||
|
||||
Arguments:
|
||||
name (str) -- The name of the setting
|
||||
|
||||
|
||||
Args:
|
||||
name (str) : The name of the setting
|
||||
|
||||
Returns:
|
||||
Setting -- The setting with the specified name
|
||||
|
||||
Setting : The setting with the specified name
|
||||
|
||||
Raises:
|
||||
ValueError -- If no setting with the specified name is found
|
||||
ValueError : If no setting with the specified name is found
|
||||
"""
|
||||
for category in self.settings.keys():
|
||||
for setting in self.settings[category]:
|
||||
|
@ -112,28 +129,31 @@ class BaseSpectrometerModel(ModuleModel):
|
|||
return setting
|
||||
raise ValueError("Setting with name %s not found" % name)
|
||||
|
||||
def add_pulse_parameter_option(self, name : str, pulse_parameter_class : PulseParameter) -> None:
|
||||
""" Adds a pulse parameter option to the spectrometer.
|
||||
|
||||
Arguments:
|
||||
name (str) -- The name of the pulse parameter
|
||||
pulse_parameter_class (PulseParameter) -- The pulse parameter class"""
|
||||
def add_pulse_parameter_option(
|
||||
self, name: str, pulse_parameter_class: PulseParameter
|
||||
) -> None:
|
||||
"""Adds a pulse parameter option to the spectrometer.
|
||||
|
||||
Args:
|
||||
name (str) : The name of the pulse parameter
|
||||
pulse_parameter_class (PulseParameter) : The pulse parameter class
|
||||
"""
|
||||
self.pulse_parameter_options[name] = pulse_parameter_class
|
||||
|
||||
@property
|
||||
def target_frequency(self):
|
||||
""" The target frequency of the spectrometer in Hz. This is the frequency where the magnetic resonance experiment is performed. """
|
||||
"""The target frequency of the spectrometer in Hz. This is the frequency where the magnetic resonance experiment is performed."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@target_frequency.setter
|
||||
def target_frequency(self, value):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@property
|
||||
def averages(self):
|
||||
""" The number of averages for the spectrometer."""
|
||||
"""The number of averages for the spectrometer."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@averages.setter
|
||||
def averages(self, value):
|
||||
raise NotImplementedError
|
||||
|
|
|
@ -1,26 +1,34 @@
|
|||
"""The Base Class for all Spectrometer Views."""
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from PyQt6.QtWidgets import QWidget, QLabel, QLineEdit, QHBoxLayout, QSizePolicy, QSpacerItem, QVBoxLayout, QCheckBox, QComboBox
|
||||
from PyQt6.QtGui import QIcon
|
||||
from PyQt6.QtCore import Qt, pyqtSlot
|
||||
from PyQt6.QtWidgets import (
|
||||
QWidget,
|
||||
QLabel,
|
||||
QHBoxLayout,
|
||||
QSizePolicy,
|
||||
QSpacerItem,
|
||||
QVBoxLayout,
|
||||
)
|
||||
from nqrduck.module.module_view import ModuleView
|
||||
from nqrduck.assets.icons import Logos
|
||||
from .settings import FloatSetting, IntSetting, BooleanSetting, SelectionSetting, StringSetting
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseSpectrometerView(ModuleView):
|
||||
"""The View Class for all Spectrometers."""
|
||||
|
||||
|
||||
def __init__(self, module):
|
||||
"""Initializes the spectrometer view."""
|
||||
super().__init__(module)
|
||||
|
||||
def load_settings_ui(self) -> None:
|
||||
"""This method automatically generates a view for the settings of the module.
|
||||
If there is a widget file that has been generated by Qt Designer, it will be used. Otherwise, a default view will be generated."""
|
||||
|
||||
|
||||
If there is a widget file that has been generated by Qt Designer, it will be used. Otherwise, a default view will be generated.
|
||||
"""
|
||||
from .base_spectrometer_widget import Ui_Form
|
||||
|
||||
widget = QWidget()
|
||||
widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||
self._ui_form = Ui_Form()
|
||||
|
@ -55,19 +63,18 @@ class BaseSpectrometerView(ModuleView):
|
|||
|
||||
edit_widget = setting.widget
|
||||
logger.debug("Setting widget: %s", edit_widget)
|
||||
|
||||
|
||||
# Add a icon that can be used as a tooltip
|
||||
if setting.description is not None:
|
||||
logger.debug("Adding tooltip to setting: %s", setting.name)
|
||||
self_path = Path(__file__).parent
|
||||
icon = Logos.QuestionMark_16x16()
|
||||
icon_label = QLabel()
|
||||
icon_label.setPixmap(icon.pixmap(icon.availableSizes()[0]))
|
||||
icon_label.setFixedSize(icon.availableSizes()[0])
|
||||
|
||||
icon_label.setToolTip(setting.description)
|
||||
|
||||
# Add a horizontal layout for the setting
|
||||
|
||||
# Add a horizontal layout for the setting
|
||||
layout = QHBoxLayout()
|
||||
# Add the label and the line edit to the layout
|
||||
layout.addItem(spacer)
|
||||
|
@ -77,12 +84,9 @@ class BaseSpectrometerView(ModuleView):
|
|||
layout.addStretch(1)
|
||||
# Add the layout to the vertical layout of the widget
|
||||
category_layout.addLayout(layout)
|
||||
|
||||
|
||||
category_layout.addStretch(1)
|
||||
self._ui_form.gridLayout.addLayout(category_layout, row, column)
|
||||
|
||||
|
||||
|
||||
# Push all the settings to the top of the widget
|
||||
self._ui_form.verticalLayout.addStretch(1)
|
||||
|
||||
|
|
@ -6,10 +6,10 @@
|
|||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||
from PyQt6 import QtCore, QtWidgets
|
||||
|
||||
|
||||
class Ui_Form(object):
|
||||
class Ui_Form:
|
||||
def setupUi(self, Form):
|
||||
Form.setObjectName("Form")
|
||||
Form.resize(800, 647)
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
"""The Controller for the Spectrometer Module.
|
||||
|
||||
Careful - this is not the base class for the spectrometer submodules, but the controller for the spectrometer module itself.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from PyQt6.QtCore import pyqtSlot
|
||||
from nqrduck.module.module_controller import ModuleController
|
||||
from nqrduck.core.main_controller import MainController
|
||||
from nqrduck_spectrometer.base_spectrometer import BaseSpectrometer
|
||||
|
@ -40,6 +44,7 @@ class SpectrometerController(ModuleController):
|
|||
|
||||
def process_signals(self, key: str, value: object) -> None:
|
||||
"""This method processes the signals from the nqrduck module.
|
||||
|
||||
It is called by the nqrduck module when a signal is emitted.
|
||||
It then calls the corresponding method of the spectrometer model.
|
||||
|
||||
|
@ -59,6 +64,7 @@ class SpectrometerController(ModuleController):
|
|||
|
||||
def on_loading(self) -> None:
|
||||
"""This method is called when the module is loaded.
|
||||
|
||||
It connects the signals from the spectrometer model to the view.
|
||||
"""
|
||||
self._module.model.spectrometer_added.connect(
|
||||
|
@ -71,6 +77,7 @@ class SpectrometerController(ModuleController):
|
|||
|
||||
def on_measurement_start(self) -> None:
|
||||
"""This method is called when a measurement is started.
|
||||
|
||||
It calls the on_measurement_start method of the active spectrometer.
|
||||
"""
|
||||
logger.debug(
|
||||
|
|
|
@ -1,14 +1,25 @@
|
|||
"""Class for handling measurement data."""
|
||||
|
||||
import logging
|
||||
import numpy as np
|
||||
from nqrduck.helpers.signalprocessing import SignalProcessing as sp
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Measurement():
|
||||
"""This class defines how measurement data should look.
|
||||
|
||||
class Measurement:
|
||||
"""This class defines how measurement data should look.
|
||||
|
||||
It includes pulse parameters necessary for further signal processing.
|
||||
Every spectrometer should adhere to this data structure in order to be compatible with the rest of the nqrduck.
|
||||
|
||||
Args:
|
||||
tdx (np.array): Time axis for the x axis of the measurement data.
|
||||
tdy (np.array): Time axis for the y axis of the measurement data.
|
||||
target_frequency (float): Target frequency of the measurement.
|
||||
frequency_shift (float, optional): Frequency shift of the measurement. Defaults to 0.
|
||||
IF_frequency (float, optional): Intermediate frequency of the measurement. Defaults to 0.
|
||||
|
||||
Attributes:
|
||||
tdx (np.array): Time axis for the x axis of the measurement data.
|
||||
tdy (np.array): Time axis for the y axis of the measurement data.
|
||||
|
@ -19,7 +30,15 @@ class Measurement():
|
|||
yf (np.array): Frequency axis for the y axis of the measurement data.
|
||||
"""
|
||||
|
||||
def __init__(self, tdx, tdy, target_frequency, frequency_shift : float = 0, IF_frequency : float = 0) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
tdx,
|
||||
tdy,
|
||||
target_frequency,
|
||||
frequency_shift: float = 0,
|
||||
IF_frequency: float = 0,
|
||||
) -> None:
|
||||
"""Initializes the measurement."""
|
||||
self.tdx = tdx
|
||||
self.tdy = tdy
|
||||
self.target_frequency = target_frequency
|
||||
|
@ -30,33 +49,35 @@ class Measurement():
|
|||
|
||||
def to_json(self):
|
||||
"""Converts the measurement to a json-compatible format.
|
||||
|
||||
|
||||
Returns:
|
||||
dict -- The measurement in json-compatible format.
|
||||
dict : The measurement in json-compatible format.
|
||||
"""
|
||||
return {
|
||||
"tdx": self.tdx.tolist(),
|
||||
"tdy": [[x.real, x.imag] for x in self.tdy], # Convert complex numbers to list
|
||||
"tdy": [
|
||||
[x.real, x.imag] for x in self.tdy
|
||||
], # Convert complex numbers to list
|
||||
"target_frequency": self.target_frequency,
|
||||
"IF_frequency": self.IF_frequency
|
||||
"IF_frequency": self.IF_frequency,
|
||||
}
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
def from_json(cls, json: dict):
|
||||
"""Converts the json format to a measurement.
|
||||
|
||||
Arguments:
|
||||
json (dict) -- The measurement in json-compatible format.
|
||||
|
||||
|
||||
Args:
|
||||
json (dict) : The measurement in json-compatible format.
|
||||
|
||||
Returns:
|
||||
Measurement -- The measurement.
|
||||
Measurement : The measurement.
|
||||
"""
|
||||
tdy = np.array([complex(y[0], y[1]) for y in json["tdy"]])
|
||||
return cls(
|
||||
np.array(json["tdx"]),
|
||||
tdy,
|
||||
target_frequency = json["target_frequency"],
|
||||
IF_frequency = json["IF_frequency"]
|
||||
target_frequency=json["target_frequency"],
|
||||
IF_frequency=json["IF_frequency"],
|
||||
)
|
||||
|
||||
# Measurement data
|
||||
|
@ -64,7 +85,7 @@ class Measurement():
|
|||
def tdx(self):
|
||||
"""Time axis for the x axis of the measurement data."""
|
||||
return self._tdx
|
||||
|
||||
|
||||
@tdx.setter
|
||||
def tdx(self, value):
|
||||
self._tdx = value
|
||||
|
@ -73,7 +94,7 @@ class Measurement():
|
|||
def tdy(self):
|
||||
"""Time axis for the y axis of the measurement data."""
|
||||
return self._tdy
|
||||
|
||||
|
||||
@tdy.setter
|
||||
def tdy(self, value):
|
||||
self._tdy = value
|
||||
|
@ -82,7 +103,7 @@ class Measurement():
|
|||
def fdx(self):
|
||||
"""Frequency axis for the x axis of the measurement data."""
|
||||
return self._fdx
|
||||
|
||||
|
||||
@fdx.setter
|
||||
def fdx(self, value):
|
||||
self._fdx = value
|
||||
|
@ -91,7 +112,7 @@ class Measurement():
|
|||
def fdy(self):
|
||||
"""Frequency axis for the y axis of the measurement data."""
|
||||
return self._fdy
|
||||
|
||||
|
||||
@fdy.setter
|
||||
def fdy(self, value):
|
||||
self._fdy = value
|
||||
|
@ -101,7 +122,7 @@ class Measurement():
|
|||
def target_frequency(self):
|
||||
"""Target frequency of the measurement."""
|
||||
return self._target_frequency
|
||||
|
||||
|
||||
@target_frequency.setter
|
||||
def target_frequency(self, value):
|
||||
self._target_frequency = value
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"""The model for the nqrduck-spectrometer module. This module is responsible for managing the spectrometers."""
|
||||
|
||||
import logging
|
||||
from PyQt6.QtCore import pyqtSignal
|
||||
from nqrduck.module.module_model import ModuleModel
|
||||
|
@ -5,40 +7,62 @@ from .base_spectrometer import BaseSpectrometer
|
|||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SpectrometerModel(ModuleModel):
|
||||
"""The model for the spectrometer module.
|
||||
|
||||
This class is responsible for managing the spectrometers.
|
||||
|
||||
Args:
|
||||
module (Module) : The module that the spectrometer is connected to
|
||||
|
||||
Attributes:
|
||||
active_spectrometer (BaseSpectrometer) : The currently active spectrometer
|
||||
available_spectrometers (dict) : A dictionary of all available spectrometers
|
||||
|
||||
Signals:
|
||||
spectrometer_added (BaseSpectrometer) : Signal emitted when a spectrometer is added
|
||||
active_spectrometer_changed (BaseSpectrometer) : Signal emitted when the active spectrometer is changed
|
||||
"""
|
||||
|
||||
spectrometer_added = pyqtSignal(BaseSpectrometer)
|
||||
active_spectrometer_changed = pyqtSignal(BaseSpectrometer)
|
||||
|
||||
def __init__(self, module) -> None:
|
||||
"""Initializes the spectrometer model."""
|
||||
super().__init__(module)
|
||||
self._active_spectrometer = None
|
||||
self._available_spectrometers = dict()
|
||||
|
||||
@property
|
||||
def active_spectrometer(self):
|
||||
"""The currently active spectrometer. This is the one that is currently being used.
|
||||
"""
|
||||
"""The currently active spectrometer. This is the one that is currently being used."""
|
||||
return self._active_spectrometer
|
||||
|
||||
|
||||
@active_spectrometer.setter
|
||||
def active_spectrometer(self, value):
|
||||
self._active_spectrometer = value
|
||||
self.active_spectrometer_changed.emit(value)
|
||||
spectrometer_module_name = value.model.toolbar_name
|
||||
logger.debug("Active spectrometer changed to %s", spectrometer_module_name)
|
||||
self.module.nqrduck_signal.emit("active_spectrometer_changed", spectrometer_module_name)
|
||||
|
||||
self.module.nqrduck_signal.emit(
|
||||
"active_spectrometer_changed", spectrometer_module_name
|
||||
)
|
||||
|
||||
@property
|
||||
def available_spectrometers(self):
|
||||
"""A dictionary of all available spectrometers. The key is the name of the spectrometer and the value is the module.
|
||||
"""
|
||||
"""A dictionary of all available spectrometers. The key is the name of the spectrometer and the value is the module."""
|
||||
return self._available_spectrometers
|
||||
|
||||
|
||||
def add_spectrometers(self, spectrometer_module_name, module):
|
||||
self._available_spectrometers [spectrometer_module_name] = module
|
||||
"""Adds a spectrometer to the available spectrometers.
|
||||
|
||||
Args:
|
||||
spectrometer_module_name (str) : The name of the spectrometer
|
||||
module (BaseSpectrometer) : The module of the spectrometer
|
||||
"""
|
||||
self._available_spectrometers[spectrometer_module_name] = module
|
||||
logger.debug("Added module: %s", spectrometer_module_name)
|
||||
self.spectrometer_added.emit(module)
|
||||
self.active_spectrometer = module
|
||||
self.add_submodule(spectrometer_module_name)
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
"""Contains the classes for the pulse parameters of the spectrometer. It includes the functions and the options for the pulse parameters.
|
||||
|
||||
Todo:
|
||||
* This shouldn't be in the spectrometer module. It should be in it"s own pulse sequence module.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
import numpy as np
|
||||
import sympy
|
||||
from decimal import Decimal
|
||||
from pathlib import Path
|
||||
from PyQt6.QtGui import QPixmap
|
||||
from nqrduck.contrib.mplwidget import MplWidget
|
||||
from nqrduck.helpers.signalprocessing import SignalProcessing as sp
|
||||
|
@ -14,6 +19,22 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class Function:
|
||||
"""A function that can be used as a pulse parameter.
|
||||
|
||||
This class is the base class for all functions that can be used as pulse parameters. Functions can be used for pulse shapes, for example.
|
||||
|
||||
Args:
|
||||
expr (str | sympy.Expr): The expression of the function.
|
||||
|
||||
Attributes:
|
||||
name (str): The name of the function.
|
||||
parameters (list): The parameters of the function.
|
||||
expr (sympy.Expr): The sympy expression of the function.
|
||||
resolution (Decimal): The resolution of the function in seconds.
|
||||
start_x (float): The x value where the evalution of the function starts.
|
||||
end_x (float): The x value where the evalution of the function ends.
|
||||
"""
|
||||
|
||||
name: str
|
||||
parameters: list
|
||||
expression: str | sympy.Expr
|
||||
|
@ -22,6 +43,7 @@ class Function:
|
|||
end_x: float
|
||||
|
||||
def __init__(self, expr) -> None:
|
||||
"""Initializes the function."""
|
||||
self.parameters = []
|
||||
self.expr = expr
|
||||
self.resolution = Decimal(1 / 30.72e6)
|
||||
|
@ -42,7 +64,7 @@ class Function:
|
|||
t = np.linspace(0, float(pulse_length), n)
|
||||
return t
|
||||
|
||||
def evaluate(self, pulse_length: Decimal, resolution : Decimal = None) -> np.ndarray:
|
||||
def evaluate(self, pulse_length: Decimal, resolution: Decimal = None) -> np.ndarray:
|
||||
"""Evaluates the function for the given pulse length.
|
||||
|
||||
Args:
|
||||
|
@ -121,7 +143,9 @@ class Function:
|
|||
mpl_widget.canvas.ax.grid(True)
|
||||
return mpl_widget
|
||||
|
||||
def get_pulse_amplitude(self, pulse_length: Decimal, resolution : Decimal = None) -> np.array:
|
||||
def get_pulse_amplitude(
|
||||
self, pulse_length: Decimal, resolution: Decimal = None
|
||||
) -> np.array:
|
||||
"""Returns the pulse amplitude in the time domain.
|
||||
|
||||
Args:
|
||||
|
@ -133,11 +157,12 @@ class Function:
|
|||
"""
|
||||
return self.evaluate(pulse_length, resolution=resolution)
|
||||
|
||||
def add_parameter(self, parameter: "Function.Parameter") -> None:
|
||||
def add_parameter(self, parameter: Function.Parameter) -> None:
|
||||
"""Adds a parameter to the function.
|
||||
|
||||
Args:
|
||||
parameter (Function.Parameter): The parameter to add."""
|
||||
parameter (Function.Parameter): The parameter to add.
|
||||
"""
|
||||
self.parameters.append(parameter)
|
||||
|
||||
def to_json(self) -> dict:
|
||||
|
@ -156,7 +181,7 @@ class Function:
|
|||
}
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, data: dict) -> "Function":
|
||||
def from_json(cls, data: dict) -> Function:
|
||||
"""Creates a function from a json representation.
|
||||
|
||||
Args:
|
||||
|
@ -193,7 +218,7 @@ class Function:
|
|||
if isinstance(expr, str):
|
||||
try:
|
||||
self._expr = sympy.sympify(expr)
|
||||
except:
|
||||
except ValueError:
|
||||
logger.error("Could not convert %s to a sympy expression", expr)
|
||||
raise SyntaxError("Could not convert %s to a sympy expression" % expr)
|
||||
elif isinstance(expr, sympy.Expr):
|
||||
|
@ -208,7 +233,7 @@ class Function:
|
|||
def resolution(self, resolution):
|
||||
try:
|
||||
self._resolution = Decimal(resolution)
|
||||
except:
|
||||
except ValueError:
|
||||
logger.error("Could not convert %s to a decimal", resolution)
|
||||
raise SyntaxError("Could not convert %s to a decimal" % resolution)
|
||||
|
||||
|
@ -221,7 +246,7 @@ class Function:
|
|||
def start_x(self, start_x):
|
||||
try:
|
||||
self._start_x = float(start_x)
|
||||
except:
|
||||
except ValueError:
|
||||
logger.error("Could not convert %s to a float", start_x)
|
||||
raise SyntaxError("Could not convert %s to a float" % start_x)
|
||||
|
||||
|
@ -234,7 +259,7 @@ class Function:
|
|||
def end_x(self, end_x):
|
||||
try:
|
||||
self._end_x = float(end_x)
|
||||
except:
|
||||
except ValueError:
|
||||
logger.error("Could not convert %s to a float", end_x)
|
||||
raise SyntaxError("Could not convert %s to a float" % end_x)
|
||||
|
||||
|
@ -242,12 +267,30 @@ class Function:
|
|||
"""This is the default pixmap for every function. If one wants to have a custom pixmap, this method has to be overwritten.
|
||||
|
||||
Returns:
|
||||
QPixmap -- The default pixmap for every function"""
|
||||
QPixmap : The default pixmap for every function
|
||||
"""
|
||||
pixmap = PulseParamters.TXCustom()
|
||||
return pixmap
|
||||
|
||||
class Parameter:
|
||||
"""A parameter of a function.
|
||||
|
||||
This can be for example the standard deviation of a Gaussian function.
|
||||
|
||||
Args:
|
||||
name (str): The name of the parameter.
|
||||
symbol (str): The symbol of the parameter.
|
||||
value (float): The value of the parameter.
|
||||
|
||||
Attributes:
|
||||
name (str): The name of the parameter.
|
||||
symbol (str): The symbol of the parameter.
|
||||
value (float): The value of the parameter.
|
||||
default (float): The default value of the parameter.
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, symbol: str, value: float) -> None:
|
||||
"""Initializes the parameter."""
|
||||
self.name = name
|
||||
self.symbol = symbol
|
||||
self.value = value
|
||||
|
@ -296,20 +339,31 @@ class RectFunction(Function):
|
|||
name = "Rectangular"
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initializes the RecFunction."""
|
||||
expr = sympy.sympify("1")
|
||||
super().__init__(expr)
|
||||
|
||||
def get_pixmap(self):
|
||||
def get_pixmap(self) -> QPixmap:
|
||||
"""Returns the pixmap of the rectangular function.
|
||||
|
||||
Returns:
|
||||
QPixmap: The pixmap of the rectangular function.
|
||||
"""
|
||||
pixmap = PulseParamters.TXRect()
|
||||
return pixmap
|
||||
|
||||
|
||||
class SincFunction(Function):
|
||||
"""The sinc function."""
|
||||
"""The sinc function.
|
||||
|
||||
The sinc function is defined as sin(x * l) / (x * l).
|
||||
The parameter is the scale factor l.
|
||||
"""
|
||||
|
||||
name = "Sinc"
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initializes the SincFunction."""
|
||||
expr = sympy.sympify("sin(x * l)/ (x * l)")
|
||||
super().__init__(expr)
|
||||
self.add_parameter(Function.Parameter("Scale Factor", "l", 2))
|
||||
|
@ -317,16 +371,26 @@ class SincFunction(Function):
|
|||
self.end_x = np.pi
|
||||
|
||||
def get_pixmap(self):
|
||||
"""Returns the pixmap of the sinc function.
|
||||
|
||||
Returns:
|
||||
QPixmap: The pixmap of the sinc function.
|
||||
"""
|
||||
pixmap = PulseParamters.TXSinc()
|
||||
return pixmap
|
||||
|
||||
|
||||
class GaussianFunction(Function):
|
||||
"""The Gaussian function."""
|
||||
"""The Gaussian function.
|
||||
|
||||
The Gaussian function is defined as exp(-0.5 * ((x - mu) / sigma)**2).
|
||||
The parameters are the mean and the standard deviation.
|
||||
"""
|
||||
|
||||
name = "Gaussian"
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initializes the GaussianFunction."""
|
||||
expr = sympy.sympify("exp(-0.5 * ((x - mu) / sigma)**2)")
|
||||
super().__init__(expr)
|
||||
self.add_parameter(Function.Parameter("Mean", "mu", 0))
|
||||
|
@ -335,6 +399,11 @@ class GaussianFunction(Function):
|
|||
self.end_x = np.pi
|
||||
|
||||
def get_pixmap(self):
|
||||
"""Returns the QPixmap of the Gaussian function.
|
||||
|
||||
Returns:
|
||||
QPixmap: The QPixmap of the Gaussian function.
|
||||
"""
|
||||
pixmap = PulseParamters.TXGauss()
|
||||
return pixmap
|
||||
|
||||
|
@ -351,25 +420,55 @@ class CustomFunction(Function):
|
|||
name = "Custom"
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initializes the Custom Function."""
|
||||
expr = sympy.sympify(" 2 * x**2 + 3 * x + 1")
|
||||
super().__init__(expr)
|
||||
|
||||
|
||||
class Option:
|
||||
"""Defines options for the pulse parameters which can then be set accordingly."""
|
||||
"""Defines options for the pulse parameters which can then be set accordingly.
|
||||
|
||||
Options can be of different types, for example boolean, numeric or function.
|
||||
|
||||
Args:
|
||||
name (str): The name of the option.
|
||||
value: The value of the option.
|
||||
|
||||
Attributes:
|
||||
name (str): The name of the option.
|
||||
value: The value of the option.
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, value) -> None:
|
||||
"""Initializes the option."""
|
||||
self.name = name
|
||||
self.value = value
|
||||
|
||||
def set_value(self):
|
||||
"""Sets the value of the option.
|
||||
|
||||
This method has to be implemented in the derived classes.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def to_json(self):
|
||||
"""Returns a json representation of the option.
|
||||
|
||||
Returns:
|
||||
dict: The json representation of the option.
|
||||
"""
|
||||
return {"name": self.name, "value": self.value, "type": self.TYPE}
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, data) -> "Option":
|
||||
def from_json(cls, data) -> Option:
|
||||
"""Creates an option from a json representation.
|
||||
|
||||
Args:
|
||||
data (dict): The json representation of the option.
|
||||
|
||||
Returns:
|
||||
Option: The option.
|
||||
"""
|
||||
for subclass in cls.__subclasses__():
|
||||
if subclass.TYPE == data["type"]:
|
||||
cls = subclass
|
||||
|
@ -390,6 +489,7 @@ class BooleanOption(Option):
|
|||
TYPE = "Boolean"
|
||||
|
||||
def set_value(self, value):
|
||||
"""Sets the value of the option."""
|
||||
self.value = value
|
||||
|
||||
|
||||
|
@ -399,49 +499,102 @@ class NumericOption(Option):
|
|||
TYPE = "Numeric"
|
||||
|
||||
def set_value(self, value):
|
||||
"""Sets the value of the option."""
|
||||
self.value = float(value)
|
||||
|
||||
|
||||
class FunctionOption(Option):
|
||||
"""Defines a selection option for a pulse parameter option.
|
||||
It takes different function objects."""
|
||||
|
||||
It takes different function objects.
|
||||
|
||||
Args:
|
||||
name (str): The name of the option.
|
||||
functions (list): The functions that can be selected.
|
||||
|
||||
Attributes:
|
||||
name (str): The name of the option.
|
||||
functions (list): The functions that can be selected.
|
||||
"""
|
||||
|
||||
TYPE = "Function"
|
||||
|
||||
def __init__(self, name, functions) -> None:
|
||||
"""Initializes the FunctionOption."""
|
||||
super().__init__(name, functions[0])
|
||||
self.functions = functions
|
||||
|
||||
def set_value(self, value):
|
||||
"""Sets the value of the option.
|
||||
|
||||
Args:
|
||||
value: The value of the option.
|
||||
"""
|
||||
self.value = value
|
||||
|
||||
def get_function_by_name(self, name):
|
||||
"""Returns the function with the given name.
|
||||
|
||||
Args:
|
||||
name (str): The name of the function.
|
||||
|
||||
Returns:
|
||||
Function: The function with the given name.
|
||||
"""
|
||||
for function in self.functions:
|
||||
if function.name == name:
|
||||
return function
|
||||
raise ValueError("Function with name %s not found" % name)
|
||||
|
||||
def to_json(self):
|
||||
"""Returns a json representation of the option.
|
||||
|
||||
Returns:
|
||||
dict: The json representation of the option.
|
||||
"""
|
||||
return {"name": self.name, "value": self.value.to_json(), "type": self.TYPE}
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, data):
|
||||
"""Creates a FunctionOption from a json representation.
|
||||
|
||||
Args:
|
||||
data (dict): The json representation of the FunctionOption.
|
||||
|
||||
Returns:
|
||||
FunctionOption: The FunctionOption.
|
||||
"""
|
||||
functions = [function() for function in Function.__subclasses__()]
|
||||
obj = cls(data["name"], functions)
|
||||
obj.value = Function.from_json(data["value"])
|
||||
return obj
|
||||
|
||||
def get_pixmap(self):
|
||||
"""Returns the pixmap of the function."""
|
||||
return self.value.get_pixmap()
|
||||
|
||||
|
||||
class TXPulse(BaseSpectrometerModel.PulseParameter):
|
||||
""" Basic TX Pulse Parameter. It includes options for the relative amplitude, the phase and the pulse shape."""
|
||||
"""Basic TX Pulse Parameter. It includes options for the relative amplitude, the phase and the pulse shape.
|
||||
|
||||
Args:
|
||||
name (str): The name of the pulse parameter.
|
||||
|
||||
Attributes:
|
||||
RELATIVE_AMPLITUDE (str): The relative amplitude of the pulse.
|
||||
TX_PHASE (str): The phase of the pulse.
|
||||
TX_PULSE_SHAPE (str): The pulse shape.
|
||||
"""
|
||||
|
||||
RELATIVE_AMPLITUDE = "Relative TX Amplitude"
|
||||
TX_PHASE = "TX Phase"
|
||||
TX_PULSE_SHAPE = "TX Pulse Shape"
|
||||
|
||||
def __init__(self, name) -> None:
|
||||
"""Initializes the TX Pulse Parameter.
|
||||
|
||||
It adds the options for the relative amplitude, the phase and the pulse shape.
|
||||
"""
|
||||
super().__init__(name)
|
||||
self.add_option(NumericOption(self.RELATIVE_AMPLITUDE, 0))
|
||||
self.add_option(NumericOption(self.TX_PHASE, 0))
|
||||
|
@ -453,6 +606,11 @@ class TXPulse(BaseSpectrometerModel.PulseParameter):
|
|||
)
|
||||
|
||||
def get_pixmap(self):
|
||||
"""Returns the pixmap of the TX Pulse Parameter.
|
||||
|
||||
Returns:
|
||||
QPixmap: The pixmap of the TX Pulse Parameter depending on the relative amplitude.
|
||||
"""
|
||||
if self.get_option_by_name(self.RELATIVE_AMPLITUDE).value > 0:
|
||||
return self.get_option_by_name(self.TX_PULSE_SHAPE).get_pixmap()
|
||||
else:
|
||||
|
@ -461,15 +619,32 @@ class TXPulse(BaseSpectrometerModel.PulseParameter):
|
|||
|
||||
|
||||
class RXReadout(BaseSpectrometerModel.PulseParameter):
|
||||
"""Basic PulseParameter for the RX Readout. It includes an option for the RX Readout state."""
|
||||
"""Basic PulseParameter for the RX Readout. It includes an option for the RX Readout state.
|
||||
|
||||
Args:
|
||||
name (str): The name of the pulse parameter.
|
||||
|
||||
Attributes:
|
||||
RX (str): The RX Readout state.
|
||||
"""
|
||||
|
||||
RX = "RX"
|
||||
|
||||
def __init__(self, name) -> None:
|
||||
"""Initializes the RX Readout PulseParameter.
|
||||
|
||||
It adds an option for the RX Readout state.
|
||||
"""
|
||||
super().__init__(name)
|
||||
self.add_option(BooleanOption(self.RX, False))
|
||||
|
||||
def get_pixmap(self):
|
||||
if self.get_option_by_name(self.RX).value == False:
|
||||
"""Returns the pixmap of the RX Readout PulseParameter.
|
||||
|
||||
Returns:
|
||||
QPixmap: The pixmap of the RX Readout PulseParameter depending on the RX Readout state.
|
||||
"""
|
||||
if self.get_option_by_name(self.RX).value is False:
|
||||
pixmap = PulseParamters.RXOff()
|
||||
else:
|
||||
pixmap = PulseParamters.RXOn()
|
||||
|
@ -477,14 +652,32 @@ class RXReadout(BaseSpectrometerModel.PulseParameter):
|
|||
|
||||
|
||||
class Gate(BaseSpectrometerModel.PulseParameter):
|
||||
"""Basic PulseParameter for the Gate. It includes an option for the Gate state.
|
||||
|
||||
Args:
|
||||
name (str): The name of the pulse parameter.
|
||||
|
||||
Attributes:
|
||||
GATE_STATE (str): The Gate state.
|
||||
"""
|
||||
|
||||
GATE_STATE = "Gate State"
|
||||
|
||||
def __init__(self, name) -> None:
|
||||
"""Initializes the Gate PulseParameter.
|
||||
|
||||
It adds an option for the Gate state.
|
||||
"""
|
||||
super().__init__(name)
|
||||
self.add_option(BooleanOption(self.GATE_STATE, False))
|
||||
|
||||
def get_pixmap(self):
|
||||
if self.get_option_by_name(self.GATE_STATE).value == False:
|
||||
"""Returns the pixmap of the Gate PulseParameter.
|
||||
|
||||
Returns:
|
||||
QPixmap: The pixmap of the Gate PulseParameter depending on the Gate state.
|
||||
"""
|
||||
if self.get_option_by_name(self.GATE_STATE).value is False:
|
||||
pixmap = PulseParamters.GateOff()
|
||||
else:
|
||||
pixmap = PulseParamters.GateOn()
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"""Contains the PulseSequence class that is used to store a pulse sequence and its events."""
|
||||
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
from nqrduck.helpers.unitconverter import UnitConverter
|
||||
|
@ -7,34 +9,70 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class PulseSequence:
|
||||
"""A pulse sequence is a collection of events that are executed in a certain order."""
|
||||
"""A pulse sequence is a collection of events that are executed in a certain order.
|
||||
|
||||
Args:
|
||||
name (str): The name of the pulse sequence
|
||||
|
||||
Attributes:
|
||||
name (str): The name of the pulse sequence
|
||||
events (list): The events of the pulse sequence
|
||||
"""
|
||||
|
||||
def __init__(self, name) -> None:
|
||||
"""Initializes the pulse sequence."""
|
||||
self.name = name
|
||||
self.events = list()
|
||||
|
||||
def get_event_names(self) -> list:
|
||||
"""Returns a list of the names of the events in the pulse sequence.
|
||||
|
||||
Returns:
|
||||
list: The names of the events
|
||||
"""
|
||||
return [event.name for event in self.events]
|
||||
|
||||
class Event:
|
||||
"""An event is a part of a pulse sequence. It has a name and a duration and different parameters that have to be set."""
|
||||
"""An event is a part of a pulse sequence. It has a name and a duration and different parameters that have to be set.
|
||||
|
||||
Args:
|
||||
name (str): The name of the event
|
||||
duration (str): The duration of the event
|
||||
|
||||
Attributes:
|
||||
name (str): The name of the event
|
||||
duration (str): The duration of the event
|
||||
parameters (OrderedDict): The parameters of the event
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, duration: str) -> None:
|
||||
"""Initializes the event."""
|
||||
self.parameters = OrderedDict()
|
||||
self.name = name
|
||||
self.duration = duration
|
||||
|
||||
def add_parameter(self, parameter) -> None:
|
||||
"""Adds a parameter to the event.
|
||||
|
||||
Args:
|
||||
parameter: The parameter to add
|
||||
"""
|
||||
self.parameters.append(parameter)
|
||||
|
||||
def on_duration_changed(self, duration: str) -> None:
|
||||
"""This method is called when the duration of the event is changed.
|
||||
|
||||
Args:
|
||||
duration (str): The new duration of the event
|
||||
"""
|
||||
logger.debug("Duration of event %s changed to %s", self.name, duration)
|
||||
self.duration = duration
|
||||
|
||||
@classmethod
|
||||
def load_event(cls, event, pulse_parameter_options):
|
||||
"""
|
||||
Loads an event from a dict. The pulse paramter options are needed to load the parameters
|
||||
"""Loads an event from a dict.
|
||||
|
||||
The pulse paramter options are needed to load the parameters
|
||||
and determine if the correct spectrometer is active.
|
||||
|
||||
Args:
|
||||
|
@ -63,13 +101,14 @@ class PulseSequence:
|
|||
)
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
@property
|
||||
def duration(self):
|
||||
"""The duration of the event."""
|
||||
return self._duration
|
||||
|
||||
|
||||
@duration.setter
|
||||
def duration(self, duration : str):
|
||||
def duration(self, duration: str):
|
||||
# Duration needs to be a positive number
|
||||
try:
|
||||
duration = UnitConverter.to_decimal(duration)
|
||||
|
@ -77,15 +116,15 @@ class PulseSequence:
|
|||
raise ValueError("Duration needs to be a number")
|
||||
if duration < 0:
|
||||
raise ValueError("Duration needs to be a positive number")
|
||||
|
||||
|
||||
self._duration = duration
|
||||
|
||||
|
||||
def to_json(self):
|
||||
"""Returns a dict with all the data in the pulse sequence
|
||||
"""Returns a dict with all the data in the pulse sequence.
|
||||
|
||||
Returns:
|
||||
dict: The dict with the sequence data"""
|
||||
dict: The dict with the sequence data
|
||||
"""
|
||||
data = {"name": self.name, "events": []}
|
||||
for event in self.events:
|
||||
event_data = {
|
||||
|
@ -102,7 +141,9 @@ class PulseSequence:
|
|||
|
||||
@classmethod
|
||||
def load_sequence(cls, sequence, pulse_parameter_options):
|
||||
"""Loads a pulse sequence from a dict. The pulse paramter options are needed to load the parameters
|
||||
"""Loads a pulse sequence from a dict.
|
||||
|
||||
The pulse paramter options are needed to load the parameters
|
||||
and make sure the correct spectrometer is active.
|
||||
|
||||
Args:
|
||||
|
@ -120,54 +161,62 @@ class PulseSequence:
|
|||
obj.events.append(cls.Event.load_event(event_data, pulse_parameter_options))
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
class Variable:
|
||||
""" A variable is a parameter that can be used within a pulsesequence as a placeholder.
|
||||
"""A variable is a parameter that can be used within a pulsesequence as a placeholder.
|
||||
|
||||
For example the event duration a Variable with name a can be set. This variable can then be set to a list of different values.
|
||||
On execution of the pulse sequence the event duration will be set to the first value in the list.
|
||||
On execution of the pulse sequence the event duration will be set to the first value in the list.
|
||||
Then the pulse sequence will be executed with the second value of the list. This is repeated until the pulse sequence has
|
||||
been executed with all values in the list."""
|
||||
|
||||
been executed with all values in the list.
|
||||
"""
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""The name of the variable."""
|
||||
return self._name
|
||||
|
||||
|
||||
@name.setter
|
||||
def name(self, name : str):
|
||||
def name(self, name: str):
|
||||
if not isinstance(name, str):
|
||||
raise TypeError("Name needs to be a string")
|
||||
self._name = name
|
||||
|
||||
|
||||
@property
|
||||
def values(self):
|
||||
"""The values of the variable. This is a list of values that the variable can take."""
|
||||
return self._values
|
||||
|
||||
|
||||
@values.setter
|
||||
def values(self, values : list):
|
||||
def values(self, values: list):
|
||||
if not isinstance(values, list):
|
||||
raise TypeError("Values needs to be a list")
|
||||
self._values = values
|
||||
|
||||
class VariableGroup:
|
||||
""" Variables can be grouped together.
|
||||
If we have groups a and b the pulse sequence will be executed for all combinations of variables in a and b."""
|
||||
|
||||
"""Variables can be grouped together.
|
||||
|
||||
If we have groups a and b the pulse sequence will be executed for all combinations of variables in a and b.
|
||||
"""
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""The name of the variable group."""
|
||||
return self._name
|
||||
|
||||
|
||||
@name.setter
|
||||
def name(self, name : str):
|
||||
def name(self, name: str):
|
||||
if not isinstance(name, str):
|
||||
raise TypeError("Name needs to be a string")
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
def variables(self):
|
||||
"""The variables in the group. This is a list of variables."""
|
||||
return self._variables
|
||||
|
||||
|
||||
@variables.setter
|
||||
def variables(self, variables : list):
|
||||
def variables(self, variables: list):
|
||||
if not isinstance(variables, list):
|
||||
raise TypeError("Variables needs to be a list")
|
||||
self._variables = variables
|
||||
self._variables = variables
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"""Settings for the different spectrometers."""
|
||||
|
||||
import logging
|
||||
import ipaddress
|
||||
from PyQt6.QtCore import QObject, pyqtSignal, pyqtSlot
|
||||
|
@ -10,7 +12,20 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
class Setting(QObject):
|
||||
"""A setting for the spectrometer is a value that is the same for all events in a pulse sequence.
|
||||
E.g. the Transmit gain or the number of points in a spectrum."""
|
||||
|
||||
E.g. the Transmit gain or the number of points in a spectrum.
|
||||
|
||||
Args:
|
||||
name (str) : The name of the setting
|
||||
description (str) : A description of the setting
|
||||
default : The default value of the setting
|
||||
|
||||
Attributes:
|
||||
name (str) : The name of the setting
|
||||
description (str) : A description of the setting
|
||||
value : The value of the setting
|
||||
widget : The widget that is used to change the setting
|
||||
"""
|
||||
|
||||
settings_changed = pyqtSignal()
|
||||
|
||||
|
@ -20,6 +35,7 @@ class Setting(QObject):
|
|||
Args:
|
||||
name (str): The name of the setting.
|
||||
description (str): A description of the setting.
|
||||
default: The default value of the setting.
|
||||
"""
|
||||
super().__init__()
|
||||
self.name = name
|
||||
|
@ -32,21 +48,31 @@ class Setting(QObject):
|
|||
|
||||
@pyqtSlot(str)
|
||||
def on_value_changed(self, value):
|
||||
"""This method is called when the value of the setting is changed.
|
||||
|
||||
Args:
|
||||
value (str): The new value of the setting.
|
||||
"""
|
||||
logger.debug("Setting %s changed to %s", self.name, value)
|
||||
self.value = value
|
||||
self.settings_changed.emit()
|
||||
|
||||
def get_setting(self):
|
||||
"""Return the value of the setting.
|
||||
|
||||
Returns:
|
||||
The value of the setting.
|
||||
"""
|
||||
return float(self.value)
|
||||
|
||||
def get_widget(self):
|
||||
"""Return a widget for the setting.
|
||||
|
||||
The default widget is simply a QLineEdit.
|
||||
This method can be overwritten by subclasses to return a different widget.
|
||||
|
||||
Returns:
|
||||
QLineEdit: A QLineEdit widget that can be used to change the setting.
|
||||
|
||||
"""
|
||||
widget = QLineEdit(str(self.value))
|
||||
widget.setMinimumWidth(100)
|
||||
|
@ -72,7 +98,16 @@ class Setting(QObject):
|
|||
|
||||
|
||||
class FloatSetting(Setting):
|
||||
"""A setting that is a Float."""
|
||||
"""A setting that is a Float.
|
||||
|
||||
Args:
|
||||
name (str) : The name of the setting
|
||||
default : The default value of the setting
|
||||
description (str) : A description of the setting
|
||||
validator (QValidator) : A validator for the setting
|
||||
min_value : The minimum value of the setting
|
||||
max_value : The maximum value of the setting
|
||||
"""
|
||||
|
||||
DEFAULT_LENGTH = 100
|
||||
|
||||
|
@ -85,6 +120,7 @@ class FloatSetting(Setting):
|
|||
min_value: float = None,
|
||||
max_value: float = None,
|
||||
) -> None:
|
||||
"""Create a new float setting."""
|
||||
super().__init__(name, description, default)
|
||||
|
||||
# If a validator is given, set it for the QLineEdit widget
|
||||
|
@ -100,6 +136,7 @@ class FloatSetting(Setting):
|
|||
|
||||
@property
|
||||
def value(self):
|
||||
"""The value of the setting. In this case, a float."""
|
||||
return self._value
|
||||
|
||||
@value.setter
|
||||
|
@ -118,7 +155,16 @@ class FloatSetting(Setting):
|
|||
|
||||
|
||||
class IntSetting(Setting):
|
||||
"""A setting that is an Integer."""
|
||||
"""A setting that is an Integer.
|
||||
|
||||
Args:
|
||||
name (str) : The name of the setting
|
||||
default : The default value of the setting
|
||||
description (str) : A description of the setting
|
||||
validator (QValidator) : A validator for the setting
|
||||
min_value : The minimum value of the setting
|
||||
max_value : The maximum value of the setting
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -129,6 +175,7 @@ class IntSetting(Setting):
|
|||
min_value=None,
|
||||
max_value=None,
|
||||
) -> None:
|
||||
"""Create a new int setting."""
|
||||
super().__init__(name, description, default)
|
||||
|
||||
# If a validator is given, set it for the QLineEdit widget
|
||||
|
@ -146,6 +193,7 @@ class IntSetting(Setting):
|
|||
|
||||
@property
|
||||
def value(self):
|
||||
"""The value of the setting. In this case, an int."""
|
||||
return self._value
|
||||
|
||||
@value.setter
|
||||
|
@ -166,9 +214,16 @@ class IntSetting(Setting):
|
|||
|
||||
|
||||
class BooleanSetting(Setting):
|
||||
"""A setting that is a Boolean."""
|
||||
"""A setting that is a Boolean.
|
||||
|
||||
Args:
|
||||
name (str) : The name of the setting
|
||||
default : The default value of the setting
|
||||
description (str) : A description of the setting
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, default: bool, description: str) -> None:
|
||||
"""Create a new boolean setting."""
|
||||
super().__init__(name, description, default)
|
||||
|
||||
# Overrides the default widget
|
||||
|
@ -176,6 +231,7 @@ class BooleanSetting(Setting):
|
|||
|
||||
@property
|
||||
def value(self):
|
||||
"""The value of the setting. In this case, a bool."""
|
||||
return self._value
|
||||
|
||||
@value.setter
|
||||
|
@ -188,6 +244,7 @@ class BooleanSetting(Setting):
|
|||
|
||||
def get_widget(self):
|
||||
"""Return a widget for the setting.
|
||||
|
||||
This returns a QCheckBox widget.
|
||||
|
||||
Returns:
|
||||
|
@ -202,11 +259,19 @@ class BooleanSetting(Setting):
|
|||
|
||||
|
||||
class SelectionSetting(Setting):
|
||||
"""A setting that is a selection from a list of options."""
|
||||
"""A setting that is a selection from a list of options.
|
||||
|
||||
Args:
|
||||
name (str) : The name of the setting
|
||||
options (list) : A list of options to choose from
|
||||
default : The default value of the setting
|
||||
description (str) : A description of the setting
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, name: str, options: list, default: str, description: str
|
||||
) -> None:
|
||||
"""Create a new selection setting."""
|
||||
super().__init__(name, description, default)
|
||||
# Check if default is in options
|
||||
if default not in options:
|
||||
|
@ -219,6 +284,7 @@ class SelectionSetting(Setting):
|
|||
|
||||
@property
|
||||
def value(self):
|
||||
"""The value of the setting. In this case, a string."""
|
||||
return self._value
|
||||
|
||||
@value.setter
|
||||
|
@ -237,6 +303,7 @@ class SelectionSetting(Setting):
|
|||
|
||||
def get_widget(self):
|
||||
"""Return a widget for the setting.
|
||||
|
||||
This returns a QComboBox widget.
|
||||
|
||||
Returns:
|
||||
|
@ -252,14 +319,22 @@ class SelectionSetting(Setting):
|
|||
|
||||
|
||||
class IPSetting(Setting):
|
||||
"""A setting that is an IP address."""
|
||||
"""A setting that is an IP address.
|
||||
|
||||
Args:
|
||||
name (str) : The name of the setting
|
||||
default : The default value of the setting
|
||||
description (str) : A description of the setting
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, default: str, description: str) -> None:
|
||||
"""Create a new IP setting."""
|
||||
super().__init__(name, description)
|
||||
self.value = default
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
"""The value of the setting. In this case, an IP address."""
|
||||
return self._value
|
||||
|
||||
@value.setter
|
||||
|
@ -273,13 +348,21 @@ class IPSetting(Setting):
|
|||
|
||||
|
||||
class StringSetting(Setting):
|
||||
"""A setting that is a string."""
|
||||
"""A setting that is a string.
|
||||
|
||||
Args:
|
||||
name (str) : The name of the setting
|
||||
default : The default value of the setting
|
||||
description (str) : A description of the setting
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, default: str, description: str) -> None:
|
||||
"""Create a new string setting."""
|
||||
super().__init__(name, description, default)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
"""The value of the setting. In this case, a string."""
|
||||
return self._value
|
||||
|
||||
@value.setter
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Module creation for the spectrometer module."""
|
||||
from nqrduck.module.module import Module
|
||||
from .model import SpectrometerModel
|
||||
from .view import SpectrometerView
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
"""View for the Spectrometer Module. Careful - this is not the base class for the spectrometer submodules, but the view for the spectrometer module itself."""
|
||||
|
||||
import logging
|
||||
from PyQt6.QtWidgets import QWidget, QToolButton, QToolBar, QMenu
|
||||
from PyQt6.QtWidgets import QWidget, QMenu
|
||||
from PyQt6.QtGui import QAction
|
||||
from PyQt6.QtCore import pyqtSlot, pyqtSignal
|
||||
from PyQt6.QtCore import pyqtSlot
|
||||
from nqrduck.module.module_view import ModuleView
|
||||
from .widget import Ui_Form
|
||||
|
||||
|
@ -9,11 +11,16 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class SpectrometerView(ModuleView):
|
||||
"""The view for the spectrometer module. This class is responsible for displaying the spectrometer module in the main window.
|
||||
|
||||
It contains the menu buttons for the different spectrometers and the stacked widget that shows the different spectrometer views.
|
||||
|
||||
Args:
|
||||
module (Module) : The spectrometer module that this view belongs to
|
||||
"""
|
||||
|
||||
def __init__(self, module):
|
||||
"""This class is the view for the spectrometer module. It contains the menu buttons for the different spectrometers.
|
||||
It also contains the stacked widget that shows the different spectrometer views.
|
||||
:param module: The spectrometer module that this view belongs to.
|
||||
"""
|
||||
"""Initializes the spectrometer view. It sets up the UI and the stacked widget for the spectrometers."""
|
||||
super().__init__(module)
|
||||
|
||||
widget = QWidget()
|
||||
|
@ -34,8 +41,11 @@ class SpectrometerView(ModuleView):
|
|||
|
||||
def on_active_spectrometer_changed(self, module):
|
||||
"""This method is called when the active spectrometer is changed.
|
||||
|
||||
It changes the active view in the stacked widget to the one that was just activated.
|
||||
:param module: The BaseSpectrometer module that was just activated.
|
||||
|
||||
Args:
|
||||
module (BaseSpectrometer) : The spectrometer module that was just activated
|
||||
"""
|
||||
self._ui_form.stackedWidgetSettings.setCurrentWidget(module.settings_view)
|
||||
|
||||
|
@ -52,8 +62,11 @@ class SpectrometerView(ModuleView):
|
|||
|
||||
def on_spectrometer_widget_changed(self, module):
|
||||
"""This method is called when a new spectrometer widget is added to the module.
|
||||
|
||||
It adds the widget to the stacked widget and sets it as the current widget.
|
||||
:param widget: The widget that was added to the module.
|
||||
|
||||
Args:
|
||||
module (BaseSpectrometer) : The spectrometer module that was just added
|
||||
"""
|
||||
logger.debug(
|
||||
"Adding settings widget to stacked widget: %s", module.settings_view
|
||||
|
@ -72,7 +85,7 @@ class SpectrometerView(ModuleView):
|
|||
self._ui_form.stackedWidgetPulseProgrammer.setCurrentWidget(
|
||||
module.model.pulse_programmer.pulse_programmer_view
|
||||
)
|
||||
except AttributeError as e:
|
||||
except AttributeError:
|
||||
logger.debug(
|
||||
"No pulse programmer widget to add for spectrometer %s",
|
||||
module.model.name,
|
||||
|
@ -83,7 +96,9 @@ class SpectrometerView(ModuleView):
|
|||
|
||||
def on_spectrometer_added(self, module):
|
||||
"""This method changes the active spectrometer to the one that was just added.
|
||||
:param module: The BaseSpectrometer module that was just added.
|
||||
|
||||
Args:
|
||||
module (BaseSpectrometer) : The spectrometer module that was just added
|
||||
"""
|
||||
module.change_spectrometer.connect(self.on_menu_button_clicked)
|
||||
self.on_spectrometer_widget_changed(module)
|
||||
|
@ -119,10 +134,14 @@ class SpectrometerView(ModuleView):
|
|||
|
||||
@pyqtSlot(str)
|
||||
def on_menu_button_clicked(self, spectrometer_name):
|
||||
"""This method is called when a menu button is clicked. It changes the active spectrometer to the one that was clicked.
|
||||
"""This method is called when a menu button is clicked.
|
||||
|
||||
It changes the active spectrometer to the one that was clicked.
|
||||
It also unchecks all other menu buttons.
|
||||
|
||||
:param spectrometer_name: The name of the spectrometer that was clicked."""
|
||||
Args:
|
||||
spectrometer_name (str) : The name of the spectrometer that was clicked
|
||||
"""
|
||||
logger.debug("Active module changed to: %s", spectrometer_name)
|
||||
for action in self._actions.values():
|
||||
action.setChecked(False)
|
||||
|
|
Loading…
Reference in a new issue