diff --git a/src/nqrduck_spectrometer/pulseparameters.py b/src/nqrduck_spectrometer/pulseparameters.py index 0698156..b4f7976 100644 --- a/src/nqrduck_spectrometer/pulseparameters.py +++ b/src/nqrduck_spectrometer/pulseparameters.py @@ -1,6 +1,7 @@ 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 @@ -14,34 +15,30 @@ class Function: name: str parameters: list expression: str | sympy.Expr - resolution: float + resolution: Decimal start_x: float end_x: float def __init__(self, expr) -> None: self.parameters = [] self.expr = expr - self.resolution = 1/30.72e6 + self.resolution = Decimal(1/30.72e6) self.start_x = -1 self.end_x = 1 - def get_time_points(self, pulse_length: float) -> np.ndarray: + def get_time_points(self, pulse_length: Decimal) -> np.ndarray: """Returns the time domain points for the function with the given pulse length.""" # Get the time domain points n = int(pulse_length / self.resolution) - t = np.linspace(0, pulse_length, n) + t = np.linspace(0, float(pulse_length), n) return t - def evaluate(self, pulse_length: float) -> np.ndarray: + def evaluate(self, pulse_length: Decimal) -> np.ndarray: """Evaluates the function for the given pulse length.""" n = int(pulse_length / self.resolution) t = np.linspace(self.start_x, self.end_x, n) x = sympy.symbols("x") - # If the expression is a string, convert it to a sympy expression - if isinstance(self.expr, str): - self.expr = sympy.sympify(self.expr) - found_variables = dict() # Create a dictionary of the parameters and their values for parameter in self.parameters: @@ -56,7 +53,7 @@ class Function: return f(t) - def frequency_domain_plot(self, pulse_length: float) -> MplWidget: + def frequency_domain_plot(self, pulse_length: Decimal) -> MplWidget: mpl_widget = MplWidget() td = self.get_time_points(pulse_length) yd = self.evaluate(pulse_length) @@ -64,17 +61,19 @@ class Function: mpl_widget.canvas.ax.plot(xdf, ydf) mpl_widget.canvas.ax.set_xlabel("Frequency in Hz") mpl_widget.canvas.ax.set_ylabel("Magnitude") + mpl_widget.canvas.ax.grid(True) return mpl_widget - def time_domain_plot(self, pulse_length: float) -> MplWidget: + def time_domain_plot(self, pulse_length: Decimal) -> MplWidget: mpl_widget = MplWidget() td = self.get_time_points(pulse_length) mpl_widget.canvas.ax.plot(td, abs(self.evaluate(pulse_length))) mpl_widget.canvas.ax.set_xlabel("Time in s") mpl_widget.canvas.ax.set_ylabel("Magnitude") + mpl_widget.canvas.ax.grid(True) return mpl_widget - def get_pulse_amplitude(self, pulse_length: float) -> np.array: + def get_pulse_amplitude(self, pulse_length: Decimal) -> np.array: """Returns the pulse amplitude in the time domain.""" return self.evaluate(pulse_length) @@ -110,6 +109,61 @@ class Function: obj.add_parameter(Function.Parameter.from_json(parameter)) return obj + + @property + def expr(self): + return self._expr + + @expr.setter + def expr(self, expr): + if isinstance(expr, str): + try: + self._expr = sympy.sympify(expr) + except: + 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): + self._expr = expr + + @property + def resolution(self): + """ The resolution of the function in seconds.""" + return self._resolution + + @resolution.setter + def resolution(self, resolution): + try: + self._resolution = Decimal(resolution) + 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.""" + return self._start_x + + @start_x.setter + def start_x(self, start_x): + try: + self._start_x = float(start_x) + 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.""" + return self._end_x + + @end_x.setter + def end_x(self, end_x): + try: + self._end_x = float(end_x) + except: + logger.error("Could not convert %s to a float", end_x) + raise SyntaxError("Could not convert %s to a float" % end_x) + class Parameter: def __init__(self, name: str, symbol: str, value: float) -> None: diff --git a/src/nqrduck_spectrometer/pulsesequence.py b/src/nqrduck_spectrometer/pulsesequence.py index f35a05d..c84372a 100644 --- a/src/nqrduck_spectrometer/pulsesequence.py +++ b/src/nqrduck_spectrometer/pulsesequence.py @@ -1,5 +1,6 @@ import logging from collections import OrderedDict +from nqrduck.helpers.unitconverter import UnitConverter from nqrduck_spectrometer.pulseparameters import Option logger = logging.getLogger(__name__) @@ -18,7 +19,7 @@ class PulseSequence: 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.""" - def __init__(self, name: str, duration: float) -> None: + def __init__(self, name: str, duration: str) -> None: self.parameters = OrderedDict() self.name = name self.duration = duration @@ -26,7 +27,7 @@ class PulseSequence: def add_parameter(self, parameter) -> None: self.parameters.append(parameter) - def on_duration_changed(self, duration: float) -> None: + def on_duration_changed(self, duration: str) -> None: logger.debug("Duration of event %s changed to %s", self.name, duration) self.duration = duration @@ -62,6 +63,23 @@ class PulseSequence: ) return obj + + @property + def duration(self): + return self._duration + + @duration.setter + def duration(self, duration : str): + # Duration needs to be a positive number + try: + duration = UnitConverter.to_decimal(duration) + except ValueError: + 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 @@ -102,3 +120,16 @@ 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. + 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. + 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.""" + pass + + 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.""" + pass \ No newline at end of file