Added docstrings.

This commit is contained in:
jupfi 2023-08-20 17:52:35 +02:00
parent 8371149a43
commit 3882be0054
2 changed files with 143 additions and 41 deletions

View file

@ -11,12 +11,10 @@ class SpectrometerController(ModuleController):
"""This class is the controller for the spectrometer module.""" """This class is the controller for the spectrometer module."""
def __init__(self, module): def __init__(self, module):
"""This method initializes the controller. """This method initializes the controller."""
:param module: The module that this controller belongs to.
"""
super().__init__(module) 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.""" """This method loads the spectrometer (sub-)modules and adds them to the spectrometer model."""
# Get the modules with entry points in the nqrduck group # Get the modules with entry points in the nqrduck group
modules = MainController._get_modules() modules = MainController._get_modules()
@ -40,7 +38,15 @@ class SpectrometerController(ModuleController):
self._module.view.create_menu_entry() 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 # This signal starts a measurement
if key == "start_measurement": if key == "start_measurement":
self.on_measurement_start() self.on_measurement_start()
@ -51,7 +57,7 @@ class SpectrometerController(ModuleController):
elif key == "set_averages": elif key == "set_averages":
self.module.model.active_spectrometer.controller.set_averages(value) 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. """This method is called when the module is loaded.
It connects the signals from the spectrometer model to the view. It connects the signals from the spectrometer model to the view.
""" """
@ -63,7 +69,7 @@ class SpectrometerController(ModuleController):
) )
self._load_spectrometer_modules() self._load_spectrometer_modules()
def on_measurement_start(self): def on_measurement_start(self) -> None:
"""This method is called when a measurement is started. """This method is called when a measurement is started.
It calls the on_measurement_start method of the active spectrometer. It calls the on_measurement_start method of the active spectrometer.
""" """

View file

@ -23,19 +23,33 @@ class Function:
def __init__(self, expr) -> None: def __init__(self, expr) -> None:
self.parameters = [] self.parameters = []
self.expr = expr self.expr = expr
self.resolution = Decimal(1/30.72e6) self.resolution = Decimal(1 / 30.72e6)
self.start_x = -1 self.start_x = -1
self.end_x = 1 self.end_x = 1
def get_time_points(self, pulse_length: Decimal) -> 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.""" """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 # Get the time domain points
n = int(pulse_length / self.resolution) n = int(pulse_length / self.resolution)
t = np.linspace(0, float(pulse_length), n) t = np.linspace(0, float(pulse_length), n)
return t return t
def evaluate(self, pulse_length: Decimal) -> np.ndarray: 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) n = int(pulse_length / self.resolution)
t = np.linspace(self.start_x, self.end_x, n) t = np.linspace(self.start_x, self.end_x, n)
x = sympy.symbols("x") x = sympy.symbols("x")
@ -53,13 +67,29 @@ class Function:
f = sympy.lambdify([x], final_expr, "numpy") f = sympy.lambdify([x], final_expr, "numpy")
return f(t) 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) n = int(pulse_length / self.resolution)
t = np.linspace(self.start_x, self.end_x, n) t = np.linspace(self.start_x, self.end_x, n)
return t return t
def frequency_domain_plot(self, pulse_length: Decimal) -> MplWidget: 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() mpl_widget = MplWidget()
td = self.get_time_points(pulse_length) td = self.get_time_points(pulse_length)
yd = self.evaluate(pulse_length) yd = self.evaluate(pulse_length)
@ -71,6 +101,14 @@ class Function:
return mpl_widget return mpl_widget
def time_domain_plot(self, pulse_length: Decimal) -> MplWidget: 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() mpl_widget = MplWidget()
td = self.get_time_points(pulse_length) td = self.get_time_points(pulse_length)
mpl_widget.canvas.ax.plot(td, abs(self.evaluate(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.set_ylabel("Magnitude")
mpl_widget.canvas.ax.grid(True) mpl_widget.canvas.ax.grid(True)
return mpl_widget return mpl_widget
def get_pulse_amplitude(self, pulse_length: Decimal) -> np.array: 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) 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) 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 { return {
"name": self.name, "name": self.name,
"parameters": [parameter.to_json() for parameter in self.parameters], "parameters": [parameter.to_json() for parameter in self.parameters],
@ -97,7 +151,15 @@ class Function:
} }
@classmethod @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__(): for subclass in cls.__subclasses__():
if subclass.name == data["name"]: if subclass.name == data["name"]:
cls = subclass cls = subclass
@ -115,11 +177,12 @@ class Function:
obj.add_parameter(Function.Parameter.from_json(parameter)) obj.add_parameter(Function.Parameter.from_json(parameter))
return obj return obj
@property @property
def expr(self): def expr(self):
"""The sympy expression of the function."""
return self._expr return self._expr
@expr.setter @expr.setter
def expr(self, expr): def expr(self, expr):
if isinstance(expr, str): if isinstance(expr, str):
@ -133,9 +196,9 @@ class Function:
@property @property
def resolution(self): def resolution(self):
""" The resolution of the function in seconds.""" """The resolution of the function in seconds."""
return self._resolution return self._resolution
@resolution.setter @resolution.setter
def resolution(self, resolution): def resolution(self, resolution):
try: try:
@ -143,12 +206,12 @@ class Function:
except: except:
logger.error("Could not convert %s to a decimal", resolution) logger.error("Could not convert %s to a decimal", resolution)
raise SyntaxError("Could not convert %s to a decimal" % resolution) raise SyntaxError("Could not convert %s to a decimal" % resolution)
@property @property
def start_x(self): 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 return self._start_x
@start_x.setter @start_x.setter
def start_x(self, start_x): def start_x(self, start_x):
try: try:
@ -156,12 +219,12 @@ class Function:
except: except:
logger.error("Could not convert %s to a float", start_x) logger.error("Could not convert %s to a float", start_x)
raise SyntaxError("Could not convert %s to a float" % start_x) raise SyntaxError("Could not convert %s to a float" % start_x)
@property @property
def end_x(self): 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 return self._end_x
@end_x.setter @end_x.setter
def end_x(self, end_x): def end_x(self, end_x):
try: try:
@ -169,16 +232,15 @@ class Function:
except: except:
logger.error("Could not convert %s to a float", end_x) logger.error("Could not convert %s to a float", end_x)
raise SyntaxError("Could not convert %s to a float" % end_x) raise SyntaxError("Could not convert %s to a float" % end_x)
def get_pixmap(self): 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. """This is the default pixmap for every function. If one wants to have a custom pixmap, this method has to be overwritten.
Returns: Returns:
QPixmap -- The default pixmap for every function""" QPixmap -- The default pixmap for every function"""
pixmap = PulseParamters.TXCustom() pixmap = PulseParamters.TXCustom()
return pixmap return pixmap
class Parameter: class Parameter:
def __init__(self, name: str, symbol: str, value: float) -> None: def __init__(self, name: str, symbol: str, value: float) -> None:
self.name = name self.name = name
@ -186,11 +248,21 @@ class Function:
self.value = value self.value = value
self.default = 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 self.value = value
logger.debug("Parameter %s set to %s", self.name, self.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 { return {
"name": self.name, "name": self.name,
"symbol": self.symbol, "symbol": self.symbol,
@ -200,11 +272,22 @@ class Function:
@classmethod @classmethod
def from_json(cls, data): 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 = cls(data["name"], data["symbol"], data["value"])
obj.default = data["default"] obj.default = data["default"]
return obj return obj
class RectFunction(Function): class RectFunction(Function):
"""The rectangular function."""
name = "Rectangular" name = "Rectangular"
def __init__(self) -> None: def __init__(self) -> None:
@ -217,6 +300,8 @@ class RectFunction(Function):
class SincFunction(Function): class SincFunction(Function):
"""The sinc function."""
name = "Sinc" name = "Sinc"
def __init__(self) -> None: def __init__(self) -> None:
@ -225,13 +310,15 @@ class SincFunction(Function):
self.add_parameter(Function.Parameter("Scale Factor", "l", 2)) self.add_parameter(Function.Parameter("Scale Factor", "l", 2))
self.start_x = -np.pi self.start_x = -np.pi
self.end_x = np.pi self.end_x = np.pi
def get_pixmap(self): def get_pixmap(self):
pixmap = PulseParamters.TXSinc() pixmap = PulseParamters.TXSinc()
return pixmap return pixmap
class GaussianFunction(Function): class GaussianFunction(Function):
"""The Gaussian function."""
name = "Gaussian" name = "Gaussian"
def __init__(self) -> None: def __init__(self) -> None:
@ -254,12 +341,15 @@ class GaussianFunction(Function):
class CustomFunction(Function): class CustomFunction(Function):
"""A custom function."""
name = "Custom" name = "Custom"
def __init__(self) -> None: def __init__(self) -> None:
expr = sympy.sympify(" 2 * x**2 + 3 * x + 1") expr = sympy.sympify(" 2 * x**2 + 3 * x + 1")
super().__init__(expr) super().__init__(expr)
class Option: 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."""
@ -272,14 +362,14 @@ class Option:
def to_json(self): def to_json(self):
return {"name": self.name, "value": self.value, "type": self.TYPE} return {"name": self.name, "value": self.value, "type": self.TYPE}
@classmethod @classmethod
def from_json(cls, data) -> "Option": def from_json(cls, data) -> "Option":
for subclass in cls.__subclasses__(): for subclass in cls.__subclasses__():
if subclass.TYPE == data["type"]: if subclass.TYPE == data["type"]:
cls = subclass cls = subclass
break break
# Check if from_json is implemented for the subclass # Check if from_json is implemented for the subclass
if cls.from_json.__func__ == Option.from_json.__func__: if cls.from_json.__func__ == Option.from_json.__func__:
obj = cls(data["name"], data["value"]) obj = cls(data["name"], data["value"])
@ -288,8 +378,10 @@ class Option:
return obj return obj
class BooleanOption(Option): class BooleanOption(Option):
"""Defines a boolean option for a pulse parameter option.""" """Defines a boolean option for a pulse parameter option."""
TYPE = "Boolean" TYPE = "Boolean"
def set_value(self, value): def set_value(self, value):
@ -298,6 +390,7 @@ class BooleanOption(Option):
class NumericOption(Option): class NumericOption(Option):
"""Defines a numeric option for a pulse parameter option.""" """Defines a numeric option for a pulse parameter option."""
TYPE = "Numeric" TYPE = "Numeric"
def set_value(self, value): def set_value(self, value):
@ -307,6 +400,7 @@ class NumericOption(Option):
class FunctionOption(Option): class FunctionOption(Option):
"""Defines a selection option for a pulse parameter option. """Defines a selection option for a pulse parameter option.
It takes different function objects.""" It takes different function objects."""
TYPE = "Function" TYPE = "Function"
def __init__(self, name, functions) -> None: def __init__(self, name, functions) -> None:
@ -324,14 +418,14 @@ class FunctionOption(Option):
def to_json(self): def to_json(self):
return {"name": self.name, "value": self.value.to_json(), "type": self.TYPE} return {"name": self.name, "value": self.value.to_json(), "type": self.TYPE}
@classmethod @classmethod
def from_json(cls, data): def from_json(cls, data):
functions = [function() for function in Function.__subclasses__()] functions = [function() for function in Function.__subclasses__()]
obj = cls(data["name"], functions) obj = cls(data["name"], functions)
obj.value = Function.from_json(data["value"]) obj.value = Function.from_json(data["value"])
return obj return obj
def get_pixmap(self): def get_pixmap(self):
return self.value.get_pixmap() 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.RELATIVE_AMPLITUDE, 0))
self.add_option(NumericOption(self.TX_PHASE, 0)) self.add_option(NumericOption(self.TX_PHASE, 0))
self.add_option( 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): def get_pixmap(self):
if self.get_option_by_name(self.RELATIVE_AMPLITUDE).value > 0: if self.get_option_by_name(self.RELATIVE_AMPLITUDE).value > 0:
return self.get_option_by_name(self.TX_PULSE_SHAPE).get_pixmap() return self.get_option_by_name(self.TX_PULSE_SHAPE).get_pixmap()
else: else:
@ -360,12 +456,12 @@ class TXPulse(BaseSpectrometerModel.PulseParameter):
class RXReadout(BaseSpectrometerModel.PulseParameter): class RXReadout(BaseSpectrometerModel.PulseParameter):
RX = "RX" RX = "RX"
def __init__(self, name) -> None: def __init__(self, name) -> None:
super().__init__(name) super().__init__(name)
self.add_option(BooleanOption(self.RX, False)) self.add_option(BooleanOption(self.RX, False))
def get_pixmap(self): def get_pixmap(self):
if self.get_option_by_name(self.RX).value == False: if self.get_option_by_name(self.RX).value == False:
pixmap = PulseParamters.RXOff() pixmap = PulseParamters.RXOff()
else: else:
@ -375,12 +471,12 @@ class RXReadout(BaseSpectrometerModel.PulseParameter):
class Gate(BaseSpectrometerModel.PulseParameter): class Gate(BaseSpectrometerModel.PulseParameter):
GATE_STATE = "Gate State" GATE_STATE = "Gate State"
def __init__(self, name) -> None: def __init__(self, name) -> None:
super().__init__(name) super().__init__(name)
self.add_option(BooleanOption(self.GATE_STATE, False)) self.add_option(BooleanOption(self.GATE_STATE, False))
def get_pixmap(self): def get_pixmap(self):
if self.get_option_by_name(self.GATE_STATE).value == False: if self.get_option_by_name(self.GATE_STATE).value == False:
pixmap = PulseParamters.GateOff() pixmap = PulseParamters.GateOff()
else: else: