mirror of
https://github.com/nqrduck/quackseq.git
synced 2024-11-21 13:32:25 +00:00
Made sequence parameters editable.
This commit is contained in:
parent
e13ea949bc
commit
70266387e2
5 changed files with 154 additions and 47 deletions
|
@ -1,6 +1,7 @@
|
||||||
import logging
|
import logging
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from quackseq.pulsesequence import PulseSequence
|
||||||
from quackseq.pulseparameters import Option
|
from quackseq.pulseparameters import Option
|
||||||
from quackseq.helpers import UnitConverter
|
from quackseq.helpers import UnitConverter
|
||||||
|
|
||||||
|
@ -13,26 +14,37 @@ class Event:
|
||||||
Args:
|
Args:
|
||||||
name (str): The name of the event
|
name (str): The name of the event
|
||||||
duration (str): The duration of the event
|
duration (str): The duration of the event
|
||||||
|
pulse_sequence (PulseSequence): The pulse sequence the event belongs to
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
name (str): The name of the event
|
name (str): The name of the event
|
||||||
duration (str): The duration of the event
|
duration (str): The duration of the event
|
||||||
parameters (OrderedDict): The parameters of the event
|
pulse_sequence (PulseSequence): The pulse sequence the event belongs to
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name: str, duration: str) -> None:
|
def __init__(self, name: str, duration: str, pulse_sequence : PulseSequence) -> None:
|
||||||
"""Initializes the event."""
|
"""Initializes the event."""
|
||||||
self.parameters = OrderedDict()
|
self.parameters = OrderedDict()
|
||||||
self.name = name
|
self.name = name
|
||||||
self.duration = duration
|
self.duration = duration
|
||||||
|
self.pulse_sequence = pulse_sequence
|
||||||
|
self.parameters = OrderedDict()
|
||||||
|
self.init_pulse_parameters()
|
||||||
|
|
||||||
def add_parameter(self, parameter) -> None:
|
def init_pulse_parameters(self) -> None:
|
||||||
"""Adds a parameter to the event.
|
"""Initializes the pulse parameters of the event."""
|
||||||
|
# Create a default instance of the pulse parameter options and add it to the event
|
||||||
Args:
|
pulse_parameters = self.pulse_sequence.pulse_parameter_options
|
||||||
parameter: The parameter to add
|
for name, pulse_parameter_class in pulse_parameters.items():
|
||||||
"""
|
logger.debug("Adding pulse parameter %s to event %s", name, self.name)
|
||||||
self.parameters.append(parameter)
|
self.parameters[name] = pulse_parameter_class(
|
||||||
|
name
|
||||||
|
)
|
||||||
|
logger.debug(
|
||||||
|
"Created pulse parameter %s with object id %s",
|
||||||
|
name,
|
||||||
|
id(self.parameters[name]),
|
||||||
|
)
|
||||||
|
|
||||||
def on_duration_changed(self, duration: str) -> None:
|
def on_duration_changed(self, duration: str) -> None:
|
||||||
"""This method is called when the duration of the event is changed.
|
"""This method is called when the duration of the event is changed.
|
||||||
|
|
|
@ -32,6 +32,8 @@ class UnitConverter:
|
||||||
return decimal.Decimal(value)
|
return decimal.Decimal(value)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
return decimal.Decimal(value)
|
return decimal.Decimal(value)
|
||||||
|
except decimal.InvalidOperation:
|
||||||
|
raise ValueError(f"Invalid value: {value}, suffixes are n, u, m")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def to_float(cls, value: str) -> float:
|
def to_float(cls, value: str) -> float:
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import importlib.metadata
|
import importlib.metadata
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from quackseq.pulseparameters import PulseParameter, TXPulse, RXReadout
|
||||||
|
from quackseq.functions import Function
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -11,21 +15,36 @@ class PulseSequence:
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
name (str): The name of the pulse sequence
|
name (str): The name of the pulse sequence
|
||||||
|
version (str): The version of the pulse sequence
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
name (str): The name of the pulse sequence
|
name (str): The name of the pulse sequence
|
||||||
events (list): The events of the pulse sequence
|
events (list): The events of the pulse sequence
|
||||||
|
pulse_parameter_options (dict): The pulse parameter options
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, version = None) -> None:
|
def __init__(self, name: str, version: str = None) -> None:
|
||||||
"""Initializes the pulse sequence."""
|
"""Initializes the pulse sequence."""
|
||||||
self.name = name
|
self.name = name
|
||||||
# Saving version to check for compatability of saved sequence
|
# Saving version to check for compatibility of saved sequence
|
||||||
if version is not None:
|
if version is not None:
|
||||||
self.version = version
|
self.version = version
|
||||||
else:
|
else:
|
||||||
self.version = importlib.metadata.version("nqrduck_spectrometer")
|
self.version = importlib.metadata.version("quackseq")
|
||||||
|
|
||||||
self.events = list()
|
self.events = list()
|
||||||
|
self.pulse_parameter_options = OrderedDict()
|
||||||
|
|
||||||
|
def add_pulse_parameter_option(
|
||||||
|
self, name: str, pulse_parameter_class: "PulseParameter"
|
||||||
|
) -> None:
|
||||||
|
"""Adds a pulse parameter option to the spectrometer.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str) : The name of the pulse parameter
|
||||||
|
pulse_parameter_class (PulseParameter) : The pulse parameter class
|
||||||
|
"""
|
||||||
|
self.pulse_parameter_options[name] = pulse_parameter_class
|
||||||
|
|
||||||
def get_event_names(self) -> list:
|
def get_event_names(self) -> list:
|
||||||
"""Returns a list of the names of the events in the pulse sequence.
|
"""Returns a list of the names of the events in the pulse sequence.
|
||||||
|
@ -34,16 +53,28 @@ class PulseSequence:
|
||||||
list: The names of the events
|
list: The names of the events
|
||||||
"""
|
"""
|
||||||
return [event.name for event in self.events]
|
return [event.name for event in self.events]
|
||||||
|
|
||||||
def add_event(self, event_name: str, duration: float) -> None:
|
def add_event(self, event: "Event") -> None:
|
||||||
"""Add a new event to the pulse sequence.
|
"""Add a new event to the pulse sequence.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event (Event): The event to add
|
||||||
|
"""
|
||||||
|
self.events.append(event)
|
||||||
|
|
||||||
|
def create_event(self, event_name: str, duration: float) -> "Event":
|
||||||
|
"""Create a new event and return it.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
event_name (str): The name of the event
|
event_name (str): The name of the event
|
||||||
duration (float): The duration of the event
|
duration (float): The duration of the event
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Event: The created event
|
||||||
"""
|
"""
|
||||||
self.events.append(self.Event(event_name, f"{float(duration):.16g}u"))
|
event = self.Event(event_name, f"{float(duration):.16g}u")
|
||||||
|
self.events.append(event)
|
||||||
|
return event
|
||||||
|
|
||||||
def delete_event(self, event_name: str) -> None:
|
def delete_event(self, event_name: str) -> None:
|
||||||
"""Deletes an event from the pulse sequence.
|
"""Deletes an event from the pulse sequence.
|
||||||
|
@ -56,6 +87,8 @@ class PulseSequence:
|
||||||
self.events.remove(event)
|
self.events.remove(event)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# Loading and saving of pulse sequences
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
@ -63,7 +96,7 @@ class PulseSequence:
|
||||||
dict: The dict with the sequence data
|
dict: The dict with the sequence data
|
||||||
"""
|
"""
|
||||||
# Get the versions of this package
|
# Get the versions of this package
|
||||||
data = {"name": self.name, "version" : self.version, "events": []}
|
data = {"name": self.name, "version": self.version, "events": []}
|
||||||
for event in self.events:
|
for event in self.events:
|
||||||
event_data = {
|
event_data = {
|
||||||
"name": event.name,
|
"name": event.name,
|
||||||
|
@ -95,16 +128,17 @@ class PulseSequence:
|
||||||
KeyError: If the pulse parameter options are not the same as the ones in the pulse sequence
|
KeyError: If the pulse parameter options are not the same as the ones in the pulse sequence
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
obj = cls(sequence["name"], version = sequence["version"])
|
obj = cls(sequence["name"], version=sequence["version"])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
logger.error("Pulse sequence version not found")
|
logger.error("Pulse sequence version not found")
|
||||||
raise KeyError("Pulse sequence version not found")
|
raise KeyError("Pulse sequence version not found")
|
||||||
|
|
||||||
for event_data in sequence["events"]:
|
for event_data in sequence["events"]:
|
||||||
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
|
||||||
|
|
||||||
|
# Automation of pulse sequences
|
||||||
class Variable:
|
class Variable:
|
||||||
"""A variable is a parameter that can be used within a pulsesequence as a placeholder.
|
"""A variable is a parameter that can be used within a pulsesequence as a placeholder.
|
||||||
|
|
||||||
|
@ -163,3 +197,68 @@ class PulseSequence:
|
||||||
if not isinstance(variables, list):
|
if not isinstance(variables, list):
|
||||||
raise TypeError("Variables needs to be a list")
|
raise TypeError("Variables needs to be a list")
|
||||||
self._variables = variables
|
self._variables = variables
|
||||||
|
|
||||||
|
|
||||||
|
class QuackSequence(PulseSequence):
|
||||||
|
"""This is the Pulse Sequence that is compatible with all types of spectrometers.
|
||||||
|
|
||||||
|
If you want to implement your own spectrometer specific pulse sequence, you can inherit from the PulseSequence class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
TX_PULSE = "TXPulse"
|
||||||
|
RX_READOUT = "RXParameters"
|
||||||
|
|
||||||
|
def __init__(self, name: str, version: str = None) -> None:
|
||||||
|
"""Initializes the pulse sequence."""
|
||||||
|
super().__init__(name, version)
|
||||||
|
|
||||||
|
self.add_pulse_parameter_option(self.TX_PULSE, TXPulse)
|
||||||
|
self.add_pulse_parameter_option(self.RX_READOUT, RXReadout)
|
||||||
|
|
||||||
|
# TX Specific functions
|
||||||
|
|
||||||
|
def set_tx_amplitude(self, event, amplitude: float) -> None:
|
||||||
|
"""Sets the amplitude of the transmitter.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event (Event): The event to set the amplitude for
|
||||||
|
amplitude (float): The amplitude of the transmitter
|
||||||
|
"""
|
||||||
|
event.parameters[self.TX_PULSE].get_option_by_name(
|
||||||
|
TXPulse.RELATIVE_AMPLITUDE
|
||||||
|
).value = amplitude
|
||||||
|
|
||||||
|
def set_tx_phase(self, event, phase: float) -> None:
|
||||||
|
"""Sets the phase of the transmitter.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event (Event): The event to set the phase for
|
||||||
|
phase (float): The phase of the transmitter
|
||||||
|
"""
|
||||||
|
event.parameters[self.TX_PULSE].get_option_by_name(
|
||||||
|
TXPulse.TX_PHASE
|
||||||
|
).value = phase
|
||||||
|
|
||||||
|
def set_tx_shape(self, event, shape: Function) -> None:
|
||||||
|
"""Sets the shape of the transmitter.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event (Event): The event to set the shape for
|
||||||
|
shape (Any): The shape of the transmitter
|
||||||
|
"""
|
||||||
|
event.parameters[self.TX_PULSE].get_option_by_name(
|
||||||
|
TXPulse.TX_PULSE_SHAPE
|
||||||
|
).value = shape
|
||||||
|
|
||||||
|
# RX Specific functions
|
||||||
|
|
||||||
|
def set_rx(self, event, rx: bool) -> None:
|
||||||
|
"""Sets the receiver on or off.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event (Event): The event to set the receiver for
|
||||||
|
rx (bool): The receiver state
|
||||||
|
"""
|
||||||
|
event.parameters[self.RX_READOUT].get_option_by_name(
|
||||||
|
RXReadout.RX
|
||||||
|
).value = rx
|
||||||
|
|
|
@ -17,11 +17,9 @@ class SpectrometerModel():
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
settings (OrderedDict) : The settings of the spectrometer
|
settings (OrderedDict) : The settings of the spectrometer
|
||||||
pulse_parameter_options (OrderedDict) : The pulse parameter options of the spectrometer
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
settings: OrderedDict
|
settings: OrderedDict
|
||||||
pulse_parameter_options: OrderedDict
|
|
||||||
|
|
||||||
def __init__(self, module):
|
def __init__(self, module):
|
||||||
"""Initializes the spectrometer model.
|
"""Initializes the spectrometer model.
|
||||||
|
@ -31,7 +29,6 @@ class SpectrometerModel():
|
||||||
"""
|
"""
|
||||||
super().__init__(module)
|
super().__init__(module)
|
||||||
self.settings = OrderedDict()
|
self.settings = OrderedDict()
|
||||||
self.pulse_parameter_options = OrderedDict()
|
|
||||||
|
|
||||||
def add_setting(self, setting: Setting, category: str) -> None:
|
def add_setting(self, setting: Setting, category: str) -> None:
|
||||||
"""Adds a setting to the spectrometer.
|
"""Adds a setting to the spectrometer.
|
||||||
|
@ -62,17 +59,6 @@ class SpectrometerModel():
|
||||||
return setting
|
return setting
|
||||||
raise ValueError(f"Setting with name {name} not found")
|
raise ValueError(f"Setting with name {name} not found")
|
||||||
|
|
||||||
def add_pulse_parameter_option(
|
|
||||||
self, name: str, pulse_parameter_class: "PulseParameter"
|
|
||||||
) -> None:
|
|
||||||
"""Adds a pulse parameter option to the spectrometer.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
name (str) : The name of the pulse parameter
|
|
||||||
pulse_parameter_class (PulseParameter) : The pulse parameter class
|
|
||||||
"""
|
|
||||||
self.pulse_parameter_options[name] = pulse_parameter_class
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def target_frequency(self):
|
def target_frequency(self):
|
||||||
"""The target frequency of the spectrometer in Hz. This is the frequency where the magnetic resonance experiment is performed."""
|
"""The target frequency of the spectrometer in Hz. This is the frequency where the magnetic resonance experiment is performed."""
|
||||||
|
|
|
@ -1,32 +1,40 @@
|
||||||
# Dummy test to communicate the structure
|
# Dummy test to communicate the structure
|
||||||
from quackseq.pulsesequence import PulseSequence
|
from quackseq.pulsesequence import QuackSequence
|
||||||
from quackseq.event import Event
|
from quackseq.event import Event
|
||||||
from quackseq.functions import RectFunction
|
from quackseq.functions import RectFunction
|
||||||
|
|
||||||
|
|
||||||
seq = PulseSequence("test")
|
seq = QuackSequence("test")
|
||||||
|
|
||||||
tx = Event("tx", "10u")
|
tx = Event("tx", "10u", seq)
|
||||||
|
|
||||||
# tx.set_tx_amplitude(1)
|
seq.add_event(tx)
|
||||||
#tx.set_tx_phase(0)
|
|
||||||
#tx.set_shape(RectFunction())
|
|
||||||
|
|
||||||
#seq.add_event(tx)
|
seq.set_tx_amplitude(tx, 1)
|
||||||
|
seq.set_tx_phase(tx, 0)
|
||||||
|
|
||||||
#blank = Event("blank", "10u")
|
rect = RectFunction()
|
||||||
|
|
||||||
#seq.add_event(blank)
|
seq.set_tx_shape(tx, rect)
|
||||||
|
|
||||||
#rx = Event("rx", "10u")
|
blank = Event("blank", "10u", seq)
|
||||||
|
|
||||||
|
seq.add_event(blank)
|
||||||
|
|
||||||
|
rx = Event("rx", "10u", seq)
|
||||||
#rx.set_rx_phase(0)
|
#rx.set_rx_phase(0)
|
||||||
#rx.set_rx(True)
|
|
||||||
|
|
||||||
#seq.add_event(rx)
|
seq.set_rx(rx, True)
|
||||||
|
|
||||||
#TR = Event("TR", "1ms")
|
seq.add_event(rx)
|
||||||
|
|
||||||
#seq.add_event(TR)
|
TR = Event("TR", "1m", seq)
|
||||||
|
|
||||||
|
seq.add_event(TR)
|
||||||
|
|
||||||
|
json = seq.to_json()
|
||||||
|
|
||||||
|
print(json)
|
||||||
|
|
||||||
#sim = Simulator()
|
#sim = Simulator()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue