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."""
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.
"""

View file

@ -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: