mirror of
https://github.com/nqrduck/nqrduck-spectrometer.git
synced 2024-11-09 12:30:01 +00:00
Started implementation of pulseshaping.
This commit is contained in:
parent
7f245f4c89
commit
d868c60f4e
2 changed files with 118 additions and 18 deletions
|
@ -27,6 +27,9 @@ dependencies = [
|
||||||
"matplotlib",
|
"matplotlib",
|
||||||
"pyqt6",
|
"pyqt6",
|
||||||
"NQRduck",
|
"NQRduck",
|
||||||
|
"sympy",
|
||||||
|
"numpy",
|
||||||
|
"scipy",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.entry-points."nqrduck"]
|
[project.entry-points."nqrduck"]
|
||||||
|
|
|
@ -1,7 +1,110 @@
|
||||||
from PyQt6.QtGui import QPixmap
|
import numpy as np
|
||||||
|
import sympy
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from PyQt6.QtGui import QPixmap
|
||||||
|
from nqrduck.contrib.mplwidget import MplWidget
|
||||||
|
from nqrduck.helpers.signalprocessing import SignalProcessing as sp
|
||||||
from .base_spectrometer_model import BaseSpectrometerModel
|
from .base_spectrometer_model import BaseSpectrometerModel
|
||||||
|
|
||||||
|
class Function():
|
||||||
|
name: str
|
||||||
|
parameters : list
|
||||||
|
expression: str | sympy.Expr
|
||||||
|
resolution: float
|
||||||
|
start_x: float
|
||||||
|
end_x: float
|
||||||
|
|
||||||
|
def __init__(self, expr) -> None:
|
||||||
|
self.parameters = []
|
||||||
|
self.expr = expr
|
||||||
|
self.resolution = 22e-9 * 16# 1e-6
|
||||||
|
self.start_x = -1
|
||||||
|
self.end_x = 1
|
||||||
|
|
||||||
|
def get_time_points(self, pulse_length : float) -> 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)
|
||||||
|
return t
|
||||||
|
|
||||||
|
def evaluate(self, pulse_length: float) -> np.ndarray:
|
||||||
|
"""Evaluates the function for the given pulse length."""
|
||||||
|
n = int(pulse_length / self.resolution)
|
||||||
|
t = np.linspace(-np.pi, np.pi, n)
|
||||||
|
x = sympy.symbols("x")
|
||||||
|
|
||||||
|
found_variables = dict()
|
||||||
|
# Create a dictionary of the parameters and their values
|
||||||
|
for parameter in self.parameters:
|
||||||
|
found_variables[parameter.symbol] = parameter.value
|
||||||
|
|
||||||
|
final_expr = self.expr.subs(found_variables)
|
||||||
|
# If the expression is a number (does not depend on x), return an array of that number
|
||||||
|
if final_expr.is_number:
|
||||||
|
return np.full(t.shape, float(final_expr))
|
||||||
|
|
||||||
|
f = sympy.lambdify([x], final_expr, "numpy")
|
||||||
|
|
||||||
|
return f(t)
|
||||||
|
|
||||||
|
def frequency_domain_plot(self, pulse_length : float) -> MplWidget:
|
||||||
|
mpl_widget = MplWidget()
|
||||||
|
td = self.get_time_points(pulse_length)
|
||||||
|
yd = self.evaluate(pulse_length)
|
||||||
|
xdf, ydf = sp.fft(td, yd)
|
||||||
|
mpl_widget.canvas.ax.plot(xdf, ydf)
|
||||||
|
mpl_widget.canvas.ax.set_xlabel("Frequency in Hz")
|
||||||
|
mpl_widget.canvas.ax.set_ylabel("Magnitude")
|
||||||
|
return mpl_widget
|
||||||
|
|
||||||
|
def time_domain_plot(self, pulse_length : float) -> MplWidget:
|
||||||
|
mpl_widget = MplWidget()
|
||||||
|
td = self.get_time_points(pulse_length)
|
||||||
|
mpl_widget.canvas.ax.plot(td, self.evaluate(pulse_length))
|
||||||
|
mpl_widget.canvas.ax.set_xlabel("Time in s")
|
||||||
|
mpl_widget.canvas.ax.set_ylabel("Magnitude")
|
||||||
|
return mpl_widget
|
||||||
|
|
||||||
|
def add_parameter(self, parameter : "Function.Parameter"):
|
||||||
|
self.parameters.append(parameter)
|
||||||
|
|
||||||
|
class Parameter:
|
||||||
|
def __init__(self, name : str, symbol : str, value : float) -> None:
|
||||||
|
self.name = name
|
||||||
|
self.symbol = symbol
|
||||||
|
self.value = value
|
||||||
|
self.default = value
|
||||||
|
|
||||||
|
class RectFunction(Function):
|
||||||
|
name = "Rectangular"
|
||||||
|
def __init__(self) -> None:
|
||||||
|
expr = sympy.sympify("1")
|
||||||
|
super().__init__(expr)
|
||||||
|
|
||||||
|
class SincFunction(Function):
|
||||||
|
name = "Sinc"
|
||||||
|
def __init__(self) -> None:
|
||||||
|
expr = sympy.sympify("sin(x * l)/ (x * l)")
|
||||||
|
super().__init__(expr)
|
||||||
|
self.add_parameter(Function.Parameter("Scale Factor", "l", 2))
|
||||||
|
|
||||||
|
class GaussianFunction(Function):
|
||||||
|
name = "Gaussian"
|
||||||
|
def __init__(self) -> None:
|
||||||
|
expr = sympy.sympify("exp(-0.5 * ((x - mu) / sigma)**2)")
|
||||||
|
super().__init__(expr)
|
||||||
|
self.add_parameter(Function.Parameter("Mean", "mu", 0))
|
||||||
|
self.add_parameter(Function.Parameter("Standard Deviation", "sigma", 1))
|
||||||
|
|
||||||
|
#class TriangleFunction(Function):
|
||||||
|
# def __init__(self) -> None:
|
||||||
|
# expr = sympy.sympify("triang(x)")
|
||||||
|
# super().__init__(lambda x: triang(x))
|
||||||
|
|
||||||
|
class CustomFunction(Function):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
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."""
|
||||||
|
@ -32,18 +135,25 @@ class NumericOption(Option):
|
||||||
self.value = float(value)
|
self.value = float(value)
|
||||||
|
|
||||||
|
|
||||||
class WidgetSelectionOption(Option):
|
class FunctionOption(Option):
|
||||||
"""Defines a widget selection option for a pulse parameter option."""
|
"""Defines a selection option for a pulse parameter option.
|
||||||
|
It takes different function objects."""
|
||||||
def __init__(self, widgets) -> None:
|
|
||||||
|
def __init__(self, functions) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.functions = functions
|
||||||
|
self.value = functions[0]
|
||||||
|
|
||||||
|
def set_value(self, value):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
|
||||||
class TXPulse(BaseSpectrometerModel.PulseParameter):
|
class TXPulse(BaseSpectrometerModel.PulseParameter):
|
||||||
def __init__(self, name) -> None:
|
def __init__(self, name) -> None:
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
self.add_option("TX Amplitude", NumericOption(0))
|
self.add_option("TX Amplitude", NumericOption(0))
|
||||||
self.add_option("TX Phase", NumericOption(0))
|
self.add_option("TX Phase", NumericOption(0))
|
||||||
|
self.add_option("TX Pulse Shape", FunctionOption([RectFunction(), SincFunction(), GaussianFunction()]))
|
||||||
|
|
||||||
def get_pixmap(self):
|
def get_pixmap(self):
|
||||||
self_path = Path(__file__).parent
|
self_path = Path(__file__).parent
|
||||||
|
@ -54,19 +164,6 @@ class TXPulse(BaseSpectrometerModel.PulseParameter):
|
||||||
pixmap = QPixmap(str(image_path))
|
pixmap = QPixmap(str(image_path))
|
||||||
return pixmap
|
return pixmap
|
||||||
|
|
||||||
class RectPulse:
|
|
||||||
def __init__(self, name) -> None:
|
|
||||||
super().__init__(name)
|
|
||||||
|
|
||||||
class SincPulse:
|
|
||||||
def __init__(self, name) -> None:
|
|
||||||
super().__init__(name)
|
|
||||||
|
|
||||||
class GaussianPulse:
|
|
||||||
def __init__(self, name) -> None:
|
|
||||||
super().__init__(name)
|
|
||||||
|
|
||||||
|
|
||||||
class RXReadout(BaseSpectrometerModel.PulseParameter):
|
class RXReadout(BaseSpectrometerModel.PulseParameter):
|
||||||
def __init__(self, name) -> None:
|
def __init__(self, name) -> None:
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
|
|
Loading…
Reference in a new issue