mirror of
https://github.com/nqrduck/quackseq.git
synced 2024-11-24 14:52:26 +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
|
||||
from collections import OrderedDict
|
||||
|
||||
from quackseq.pulsesequence import PulseSequence
|
||||
from quackseq.pulseparameters import Option
|
||||
from quackseq.helpers import UnitConverter
|
||||
|
||||
|
@ -13,26 +14,37 @@ class Event:
|
|||
Args:
|
||||
name (str): The name of the event
|
||||
duration (str): The duration of the event
|
||||
pulse_sequence (PulseSequence): The pulse sequence the event belongs to
|
||||
|
||||
Attributes:
|
||||
name (str): The name 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."""
|
||||
self.parameters = OrderedDict()
|
||||
self.name = name
|
||||
self.duration = duration
|
||||
self.pulse_sequence = pulse_sequence
|
||||
self.parameters = OrderedDict()
|
||||
self.init_pulse_parameters()
|
||||
|
||||
def add_parameter(self, parameter) -> None:
|
||||
"""Adds a parameter to the event.
|
||||
|
||||
Args:
|
||||
parameter: The parameter to add
|
||||
"""
|
||||
self.parameters.append(parameter)
|
||||
def init_pulse_parameters(self) -> None:
|
||||
"""Initializes the pulse parameters of the event."""
|
||||
# Create a default instance of the pulse parameter options and add it to the event
|
||||
pulse_parameters = self.pulse_sequence.pulse_parameter_options
|
||||
for name, pulse_parameter_class in pulse_parameters.items():
|
||||
logger.debug("Adding pulse parameter %s to event %s", name, self.name)
|
||||
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:
|
||||
"""This method is called when the duration of the event is changed.
|
||||
|
|
|
@ -32,6 +32,8 @@ class UnitConverter:
|
|||
return decimal.Decimal(value)
|
||||
except TypeError:
|
||||
return decimal.Decimal(value)
|
||||
except decimal.InvalidOperation:
|
||||
raise ValueError(f"Invalid value: {value}, suffixes are n, u, m")
|
||||
|
||||
@classmethod
|
||||
def to_float(cls, value: str) -> float:
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
import logging
|
||||
import importlib.metadata
|
||||
from collections import OrderedDict
|
||||
|
||||
from quackseq.pulseparameters import PulseParameter, TXPulse, RXReadout
|
||||
from quackseq.functions import Function
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -11,21 +15,36 @@ class PulseSequence:
|
|||
|
||||
Args:
|
||||
name (str): The name of the pulse sequence
|
||||
version (str): The version of the pulse sequence
|
||||
|
||||
Attributes:
|
||||
name (str): The name 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."""
|
||||
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:
|
||||
self.version = version
|
||||
else:
|
||||
self.version = importlib.metadata.version("nqrduck_spectrometer")
|
||||
self.version = importlib.metadata.version("quackseq")
|
||||
|
||||
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:
|
||||
"""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
|
||||
"""
|
||||
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.
|
||||
|
||||
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:
|
||||
event_name (str): The name 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:
|
||||
"""Deletes an event from the pulse sequence.
|
||||
|
@ -56,6 +87,8 @@ class PulseSequence:
|
|||
self.events.remove(event)
|
||||
break
|
||||
|
||||
# Loading and saving of pulse sequences
|
||||
|
||||
def to_json(self):
|
||||
"""Returns a dict with all the data in the pulse sequence.
|
||||
|
||||
|
@ -63,7 +96,7 @@ class PulseSequence:
|
|||
dict: The dict with the sequence data
|
||||
"""
|
||||
# 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:
|
||||
event_data = {
|
||||
"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
|
||||
"""
|
||||
try:
|
||||
obj = cls(sequence["name"], version = sequence["version"])
|
||||
obj = cls(sequence["name"], version=sequence["version"])
|
||||
except KeyError:
|
||||
logger.error("Pulse sequence version not found")
|
||||
raise KeyError("Pulse sequence version not found")
|
||||
|
||||
|
||||
for event_data in sequence["events"]:
|
||||
obj.events.append(cls.Event.load_event(event_data, pulse_parameter_options))
|
||||
|
||||
return obj
|
||||
|
||||
# Automation of pulse sequences
|
||||
class Variable:
|
||||
"""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):
|
||||
raise TypeError("Variables needs to be a list")
|
||||
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:
|
||||
settings (OrderedDict) : The settings of the spectrometer
|
||||
pulse_parameter_options (OrderedDict) : The pulse parameter options of the spectrometer
|
||||
"""
|
||||
|
||||
settings: OrderedDict
|
||||
pulse_parameter_options: OrderedDict
|
||||
|
||||
def __init__(self, module):
|
||||
"""Initializes the spectrometer model.
|
||||
|
@ -31,7 +29,6 @@ class SpectrometerModel():
|
|||
"""
|
||||
super().__init__(module)
|
||||
self.settings = OrderedDict()
|
||||
self.pulse_parameter_options = OrderedDict()
|
||||
|
||||
def add_setting(self, setting: Setting, category: str) -> None:
|
||||
"""Adds a setting to the spectrometer.
|
||||
|
@ -62,17 +59,6 @@ class SpectrometerModel():
|
|||
return setting
|
||||
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
|
||||
def target_frequency(self):
|
||||
"""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
|
||||
from quackseq.pulsesequence import PulseSequence
|
||||
from quackseq.pulsesequence import QuackSequence
|
||||
from quackseq.event import Event
|
||||
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)
|
||||
#tx.set_tx_phase(0)
|
||||
#tx.set_shape(RectFunction())
|
||||
seq.add_event(tx)
|
||||
|
||||
#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(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()
|
||||
|
||||
|
|
Loading…
Reference in a new issue