diff --git a/src/nqrduck_spectrometer/controller.py b/src/nqrduck_spectrometer/controller.py index f780ddf..def160f 100644 --- a/src/nqrduck_spectrometer/controller.py +++ b/src/nqrduck_spectrometer/controller.py @@ -11,12 +11,10 @@ class SpectrometerController(ModuleController): """This class is the controller for the spectrometer module.""" def __init__(self, module): - """This method initializes the controller. - :param module: The module that this controller belongs to. - """ + """This method initializes the controller.""" super().__init__(module) - def _load_spectrometer_modules(self): + def _load_spectrometer_modules(self) -> None: """This method loads the spectrometer (sub-)modules and adds them to the spectrometer model.""" # Get the modules with entry points in the nqrduck group modules = MainController._get_modules() @@ -40,7 +38,15 @@ class SpectrometerController(ModuleController): self._module.view.create_menu_entry() - def process_signals(self, key: str, value: None): + 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. + + Args: + key (str): Name of the signal. + value (object): Value of the signal. + """ # This signal starts a measurement if key == "start_measurement": self.on_measurement_start() @@ -51,7 +57,7 @@ class SpectrometerController(ModuleController): elif key == "set_averages": self.module.model.active_spectrometer.controller.set_averages(value) - def on_loading(self): + 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. """ @@ -63,7 +69,7 @@ class SpectrometerController(ModuleController): ) self._load_spectrometer_modules() - def on_measurement_start(self): + 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. """ diff --git a/src/nqrduck_spectrometer/pulseparameters.py b/src/nqrduck_spectrometer/pulseparameters.py index f1c9e52..fa4e059 100644 --- a/src/nqrduck_spectrometer/pulseparameters.py +++ b/src/nqrduck_spectrometer/pulseparameters.py @@ -23,19 +23,33 @@ class Function: def __init__(self, expr) -> None: self.parameters = [] self.expr = expr - self.resolution = Decimal(1/30.72e6) + self.resolution = Decimal(1 / 30.72e6) self.start_x = -1 self.end_x = 1 def get_time_points(self, pulse_length: Decimal) -> np.ndarray: - """Returns the time domain points for the function with the given pulse length.""" + """Returns the time domain points for the function with the given pulse length. + + Args: + pulse_length (Decimal): The pulse length in seconds. + + Returns: + np.ndarray: The time domain points. + """ # Get the time domain points n = int(pulse_length / self.resolution) t = np.linspace(0, float(pulse_length), n) return t def evaluate(self, pulse_length: Decimal) -> np.ndarray: - """Evaluates the function for the given pulse length.""" + """Evaluates the function for the given pulse length. + + Args: + pulse_length (Decimal): The pulse length in seconds. + + Returns: + np.ndarray: The evaluated function. + """ n = int(pulse_length / self.resolution) t = np.linspace(self.start_x, self.end_x, n) x = sympy.symbols("x") @@ -53,13 +67,29 @@ class Function: f = sympy.lambdify([x], final_expr, "numpy") return f(t) - - def get_tdx(self, pulse_length : float) -> None: + + def get_tdx(self, pulse_length: float) -> np.ndarray: + """Returns the time domain points and the evaluated function for the given pulse length. + + Args: + pulse_length (float): The pulse length in seconds. + + Returns: + np.ndarray: The time domain points. + """ n = int(pulse_length / self.resolution) t = np.linspace(self.start_x, self.end_x, n) return t def frequency_domain_plot(self, pulse_length: Decimal) -> MplWidget: + """Plots the frequency domain of the function for the given pulse length. + + Args: + pulse_length (Decimal): The pulse length in seconds. + + Returns: + MplWidget: The matplotlib widget containing the plot. + """ mpl_widget = MplWidget() td = self.get_time_points(pulse_length) yd = self.evaluate(pulse_length) @@ -71,6 +101,14 @@ class Function: return mpl_widget def time_domain_plot(self, pulse_length: Decimal) -> MplWidget: + """Plots the time domain of the function for the given pulse length. + + Args: + pulse_length (Decimal): The pulse length in seconds. + + Returns: + MplWidget: The matplotlib widget containing the plot. + """ mpl_widget = MplWidget() td = self.get_time_points(pulse_length) mpl_widget.canvas.ax.plot(td, abs(self.evaluate(pulse_length))) @@ -78,15 +116,31 @@ class Function: mpl_widget.canvas.ax.set_ylabel("Magnitude") mpl_widget.canvas.ax.grid(True) return mpl_widget - + def get_pulse_amplitude(self, pulse_length: Decimal) -> np.array: - """Returns the pulse amplitude in the time domain.""" + """Returns the pulse amplitude in the time domain. + + Args: + pulse_length (Decimal): The pulse length in seconds. + + Returns: + np.array: The pulse amplitude. + """ return self.evaluate(pulse_length) - def add_parameter(self, parameter: "Function.Parameter"): + def add_parameter(self, parameter: "Function.Parameter") -> None: + """Adds a parameter to the function. + + Args: + parameter (Function.Parameter): The parameter to add.""" self.parameters.append(parameter) - def to_json(self): + def to_json(self) -> dict: + """Returns a json representation of the function. + + Returns: + dict: The json representation of the function. + """ return { "name": self.name, "parameters": [parameter.to_json() for parameter in self.parameters], @@ -97,7 +151,15 @@ class Function: } @classmethod - def from_json(cls, data): + def from_json(cls, data: dict) -> "Function": + """Creates a function from a json representation. + + Args: + data (dict): The json representation of the function. + + Returns: + Function: The function. + """ for subclass in cls.__subclasses__(): if subclass.name == data["name"]: cls = subclass @@ -115,11 +177,12 @@ class Function: obj.add_parameter(Function.Parameter.from_json(parameter)) return obj - + @property def expr(self): + """The sympy expression of the function.""" return self._expr - + @expr.setter def expr(self, expr): if isinstance(expr, str): @@ -133,9 +196,9 @@ class Function: @property def resolution(self): - """ The resolution of the function in seconds.""" + """The resolution of the function in seconds.""" return self._resolution - + @resolution.setter def resolution(self, resolution): try: @@ -143,12 +206,12 @@ class Function: except: logger.error("Could not convert %s to a decimal", resolution) raise SyntaxError("Could not convert %s to a decimal" % resolution) - + @property def start_x(self): - """ The x value where the evalution of the function starts.""" + """The x value where the evalution of the function starts.""" return self._start_x - + @start_x.setter def start_x(self, start_x): try: @@ -156,12 +219,12 @@ class Function: except: logger.error("Could not convert %s to a float", start_x) raise SyntaxError("Could not convert %s to a float" % start_x) - + @property def end_x(self): - """ The x value where the evalution of the function ends.""" + """The x value where the evalution of the function ends.""" return self._end_x - + @end_x.setter def end_x(self, end_x): try: @@ -169,16 +232,15 @@ class Function: except: logger.error("Could not convert %s to a float", end_x) raise SyntaxError("Could not convert %s to a float" % end_x) - + def get_pixmap(self): """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""" pixmap = PulseParamters.TXCustom() return pixmap - class Parameter: def __init__(self, name: str, symbol: str, value: float) -> None: self.name = name @@ -186,11 +248,21 @@ class Function: self.value = value self.default = value - def set_value(self, value: float): + def set_value(self, value: float) -> None: + """Sets the value of the parameter. + + Args: + value (float): The new value of the parameter. + """ self.value = value logger.debug("Parameter %s set to %s", self.name, self.value) - def to_json(self): + def to_json(self) -> dict: + """Returns a json representation of the parameter. + + Returns: + dict: The json representation of the parameter. + """ return { "name": self.name, "symbol": self.symbol, @@ -200,11 +272,22 @@ class Function: @classmethod def from_json(cls, data): + """Creates a parameter from a json representation. + + Args: + data (dict): The json representation of the parameter. + + Returns: + Function.Parameter: The parameter. + """ obj = cls(data["name"], data["symbol"], data["value"]) obj.default = data["default"] return obj + class RectFunction(Function): + """The rectangular function.""" + name = "Rectangular" def __init__(self) -> None: @@ -217,6 +300,8 @@ class RectFunction(Function): class SincFunction(Function): + """The sinc function.""" + name = "Sinc" def __init__(self) -> None: @@ -225,13 +310,15 @@ class SincFunction(Function): self.add_parameter(Function.Parameter("Scale Factor", "l", 2)) self.start_x = -np.pi self.end_x = np.pi - + def get_pixmap(self): pixmap = PulseParamters.TXSinc() return pixmap class GaussianFunction(Function): + """The Gaussian function.""" + name = "Gaussian" def __init__(self) -> None: @@ -254,12 +341,15 @@ class GaussianFunction(Function): class CustomFunction(Function): + """A custom function.""" + name = "Custom" def __init__(self) -> None: 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.""" @@ -272,14 +362,14 @@ class Option: def to_json(self): return {"name": self.name, "value": self.value, "type": self.TYPE} - + @classmethod def from_json(cls, data) -> "Option": for subclass in cls.__subclasses__(): if subclass.TYPE == data["type"]: cls = subclass break - + # Check if from_json is implemented for the subclass if cls.from_json.__func__ == Option.from_json.__func__: obj = cls(data["name"], data["value"]) @@ -288,8 +378,10 @@ class Option: return obj + class BooleanOption(Option): """Defines a boolean option for a pulse parameter option.""" + TYPE = "Boolean" def set_value(self, value): @@ -298,6 +390,7 @@ class BooleanOption(Option): class NumericOption(Option): """Defines a numeric option for a pulse parameter option.""" + TYPE = "Numeric" def set_value(self, value): @@ -307,6 +400,7 @@ class NumericOption(Option): class FunctionOption(Option): """Defines a selection option for a pulse parameter option. It takes different function objects.""" + TYPE = "Function" def __init__(self, name, functions) -> None: @@ -324,14 +418,14 @@ class FunctionOption(Option): def to_json(self): return {"name": self.name, "value": self.value.to_json(), "type": self.TYPE} - + @classmethod def from_json(cls, data): 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): return self.value.get_pixmap() @@ -346,11 +440,13 @@ class TXPulse(BaseSpectrometerModel.PulseParameter): self.add_option(NumericOption(self.RELATIVE_AMPLITUDE, 0)) self.add_option(NumericOption(self.TX_PHASE, 0)) self.add_option( - FunctionOption(self.TX_PULSE_SHAPE, [RectFunction(), SincFunction(), GaussianFunction(), CustomFunction()]), + FunctionOption( + self.TX_PULSE_SHAPE, + [RectFunction(), SincFunction(), GaussianFunction(), CustomFunction()], + ), ) def get_pixmap(self): - if self.get_option_by_name(self.RELATIVE_AMPLITUDE).value > 0: return self.get_option_by_name(self.TX_PULSE_SHAPE).get_pixmap() else: @@ -360,12 +456,12 @@ class TXPulse(BaseSpectrometerModel.PulseParameter): class RXReadout(BaseSpectrometerModel.PulseParameter): RX = "RX" + def __init__(self, name) -> None: super().__init__(name) self.add_option(BooleanOption(self.RX, False)) def get_pixmap(self): - if self.get_option_by_name(self.RX).value == False: pixmap = PulseParamters.RXOff() else: @@ -375,12 +471,12 @@ class RXReadout(BaseSpectrometerModel.PulseParameter): class Gate(BaseSpectrometerModel.PulseParameter): GATE_STATE = "Gate State" + def __init__(self, name) -> None: 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: pixmap = PulseParamters.GateOff() else: