mirror of
https://github.com/nqrduck/nqrduck-spectrometer.git
synced 2024-12-22 08:17:49 +00:00
Merge branch 'main' of github.com:nqrduck/nqrduck-spectrometer
This commit is contained in:
commit
44b8b84823
2 changed files with 99 additions and 14 deletions
|
@ -1,6 +1,7 @@
|
||||||
import logging
|
import logging
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import sympy
|
import sympy
|
||||||
|
from decimal import Decimal
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from PyQt6.QtGui import QPixmap
|
from PyQt6.QtGui import QPixmap
|
||||||
from nqrduck.contrib.mplwidget import MplWidget
|
from nqrduck.contrib.mplwidget import MplWidget
|
||||||
|
@ -14,34 +15,30 @@ class Function:
|
||||||
name: str
|
name: str
|
||||||
parameters: list
|
parameters: list
|
||||||
expression: str | sympy.Expr
|
expression: str | sympy.Expr
|
||||||
resolution: float
|
resolution: Decimal
|
||||||
start_x: float
|
start_x: float
|
||||||
end_x: float
|
end_x: float
|
||||||
|
|
||||||
def __init__(self, expr) -> None:
|
def __init__(self, expr) -> None:
|
||||||
self.parameters = []
|
self.parameters = []
|
||||||
self.expr = expr
|
self.expr = expr
|
||||||
self.resolution = 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: 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."""
|
"""Returns the time domain points for the function with the given pulse length."""
|
||||||
# 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, pulse_length, n)
|
t = np.linspace(0, float(pulse_length), n)
|
||||||
return t
|
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."""
|
"""Evaluates the function for the given pulse length."""
|
||||||
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")
|
||||||
|
|
||||||
# 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()
|
found_variables = dict()
|
||||||
# Create a dictionary of the parameters and their values
|
# Create a dictionary of the parameters and their values
|
||||||
for parameter in self.parameters:
|
for parameter in self.parameters:
|
||||||
|
@ -56,7 +53,7 @@ class Function:
|
||||||
|
|
||||||
return f(t)
|
return f(t)
|
||||||
|
|
||||||
def frequency_domain_plot(self, pulse_length: float) -> MplWidget:
|
def frequency_domain_plot(self, pulse_length: Decimal) -> MplWidget:
|
||||||
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)
|
||||||
|
@ -64,17 +61,19 @@ class Function:
|
||||||
mpl_widget.canvas.ax.plot(xdf, ydf)
|
mpl_widget.canvas.ax.plot(xdf, ydf)
|
||||||
mpl_widget.canvas.ax.set_xlabel("Frequency in Hz")
|
mpl_widget.canvas.ax.set_xlabel("Frequency in Hz")
|
||||||
mpl_widget.canvas.ax.set_ylabel("Magnitude")
|
mpl_widget.canvas.ax.set_ylabel("Magnitude")
|
||||||
|
mpl_widget.canvas.ax.grid(True)
|
||||||
return mpl_widget
|
return mpl_widget
|
||||||
|
|
||||||
def time_domain_plot(self, pulse_length: float) -> MplWidget:
|
def time_domain_plot(self, pulse_length: Decimal) -> MplWidget:
|
||||||
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)))
|
||||||
mpl_widget.canvas.ax.set_xlabel("Time in s")
|
mpl_widget.canvas.ax.set_xlabel("Time in s")
|
||||||
mpl_widget.canvas.ax.set_ylabel("Magnitude")
|
mpl_widget.canvas.ax.set_ylabel("Magnitude")
|
||||||
|
mpl_widget.canvas.ax.grid(True)
|
||||||
return mpl_widget
|
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."""
|
"""Returns the pulse amplitude in the time domain."""
|
||||||
return self.evaluate(pulse_length)
|
return self.evaluate(pulse_length)
|
||||||
|
|
||||||
|
@ -110,6 +109,61 @@ class Function:
|
||||||
obj.add_parameter(Function.Parameter.from_json(parameter))
|
obj.add_parameter(Function.Parameter.from_json(parameter))
|
||||||
|
|
||||||
return obj
|
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:
|
class Parameter:
|
||||||
def __init__(self, name: str, symbol: str, value: float) -> None:
|
def __init__(self, name: str, symbol: str, value: float) -> None:
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from nqrduck.helpers.unitconverter import UnitConverter
|
||||||
from nqrduck_spectrometer.pulseparameters import Option
|
from nqrduck_spectrometer.pulseparameters import Option
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -18,7 +19,7 @@ class PulseSequence:
|
||||||
class Event:
|
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."""
|
||||||
|
|
||||||
def __init__(self, name: str, duration: float) -> None:
|
def __init__(self, name: str, duration: str) -> None:
|
||||||
self.parameters = OrderedDict()
|
self.parameters = OrderedDict()
|
||||||
self.name = name
|
self.name = name
|
||||||
self.duration = duration
|
self.duration = duration
|
||||||
|
@ -26,7 +27,7 @@ class PulseSequence:
|
||||||
def add_parameter(self, parameter) -> None:
|
def add_parameter(self, parameter) -> None:
|
||||||
self.parameters.append(parameter)
|
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)
|
logger.debug("Duration of event %s changed to %s", self.name, duration)
|
||||||
self.duration = duration
|
self.duration = duration
|
||||||
|
|
||||||
|
@ -62,6 +63,23 @@ class PulseSequence:
|
||||||
)
|
)
|
||||||
|
|
||||||
return obj
|
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):
|
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
|
||||||
|
@ -102,3 +120,16 @@ class PulseSequence:
|
||||||
obj.events.append(cls.Event.load_event(event_data, pulse_parameter_options))
|
obj.events.append(cls.Event.load_event(event_data, pulse_parameter_options))
|
||||||
|
|
||||||
return obj
|
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
|
Loading…
Reference in a new issue