mirror of
https://github.com/nqrduck/nqrduck-spectrometer-simulator.git
synced 2024-11-09 19:20:03 +00:00
Updated simulator with RX event.
This commit is contained in:
parent
7f721d242a
commit
9cbe265694
2 changed files with 127 additions and 57 deletions
|
@ -9,13 +9,14 @@ from nqr_blochsimulator.classes.simulation import Simulation
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class SimulatorController(BaseSpectrometerController):
|
class SimulatorController(BaseSpectrometerController):
|
||||||
def __init__(self, module):
|
def __init__(self, module):
|
||||||
super().__init__(module)
|
super().__init__(module)
|
||||||
|
|
||||||
def start_measurement(self):
|
def start_measurement(self):
|
||||||
"""This method is called when the start_measurement signal is received from the core.
|
"""This method is called when the start_measurement signal is received from the core.
|
||||||
It will becalled if the simulator is the active spectrometer.
|
It will becalled if the simulator is the active spectrometer.
|
||||||
This will start the simulation based on the settings and the pulse sequence.
|
This will start the simulation based on the settings and the pulse sequence.
|
||||||
"""
|
"""
|
||||||
logger.debug("Starting simulation")
|
logger.debug("Starting simulation")
|
||||||
|
@ -30,11 +31,21 @@ class SimulatorController(BaseSpectrometerController):
|
||||||
simulation = self.get_simulation(sample, pulse_array)
|
simulation = self.get_simulation(sample, pulse_array)
|
||||||
|
|
||||||
result = abs(simulation.simulate())
|
result = abs(simulation.simulate())
|
||||||
tdx = np.linspace(0, float(self.calculate_simulation_length()), len(result)) * 1e6
|
|
||||||
|
tdx = (
|
||||||
|
np.linspace(0, float(self.calculate_simulation_length()), len(result)) * 1e6
|
||||||
|
)
|
||||||
|
|
||||||
|
rx_begin, rx_stop = self.translate_rx_event()
|
||||||
|
# If we have a RX event, we need to cut the result to the RX event
|
||||||
|
if rx_begin and rx_stop:
|
||||||
|
evidx = np.where((tdx > rx_begin) & (tdx < rx_stop))[0]
|
||||||
|
tdx = tdx[evidx]
|
||||||
|
result = result[evidx]
|
||||||
|
|
||||||
measurement_data = Measurement(
|
measurement_data = Measurement(
|
||||||
tdx,
|
tdx,
|
||||||
result,
|
result / self.module.model.averages,
|
||||||
sample.resonant_frequency,
|
sample.resonant_frequency,
|
||||||
# frequency_shift=self.module.model.if_frequency,
|
# frequency_shift=self.module.model.if_frequency,
|
||||||
)
|
)
|
||||||
|
@ -95,31 +106,32 @@ class SimulatorController(BaseSpectrometerController):
|
||||||
else:
|
else:
|
||||||
logger.warning("Unknown sample setting: %s", samplesetting.name)
|
logger.warning("Unknown sample setting: %s", samplesetting.name)
|
||||||
self.module.nqrduck_signal.emit(
|
self.module.nqrduck_signal.emit(
|
||||||
"notification", ["Error", "Unknown sample setting: " + samplesetting.name]
|
"notification",
|
||||||
|
["Error", "Unknown sample setting: " + samplesetting.name],
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
sample = Sample(
|
sample = Sample(
|
||||||
name = name,
|
name=name,
|
||||||
density = density,
|
density=density,
|
||||||
molar_mass = molar_mass,
|
molar_mass=molar_mass,
|
||||||
resonant_frequency = resonant_frequency,
|
resonant_frequency=resonant_frequency,
|
||||||
gamma = gamma,
|
gamma=gamma,
|
||||||
nuclear_spin = nuclear_spin,
|
nuclear_spin=nuclear_spin,
|
||||||
spin_factor = spin_factor,
|
spin_factor=spin_factor,
|
||||||
powder_factor = powder_factor,
|
powder_factor=powder_factor,
|
||||||
filling_factor = filling_factor,
|
filling_factor=filling_factor,
|
||||||
T1 = T1,
|
T1=T1,
|
||||||
T2 = T2,
|
T2=T2,
|
||||||
T2_star = T2_star,
|
T2_star=T2_star,
|
||||||
atom_density = atom_density,
|
atom_density=atom_density,
|
||||||
sample_volume = sample_volume,
|
sample_volume=sample_volume,
|
||||||
sample_length = sample_length,
|
sample_length=sample_length,
|
||||||
sample_diameter = sample_diameter
|
sample_diameter=sample_diameter,
|
||||||
)
|
)
|
||||||
return sample
|
return sample
|
||||||
|
|
||||||
def translate_pulse_sequence(self, dwell_time : float) -> PulseArray:
|
def translate_pulse_sequence(self, dwell_time: float) -> PulseArray:
|
||||||
"""This method translates the pulse sequence from the core to a PulseArray object needed for the simulation.
|
"""This method translates the pulse sequence from the core to a PulseArray object needed for the simulation.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -147,29 +159,35 @@ class SimulatorController(BaseSpectrometerController):
|
||||||
pulse_shape = parameter.get_option_by_name(
|
pulse_shape = parameter.get_option_by_name(
|
||||||
TXPulse.TX_PULSE_SHAPE
|
TXPulse.TX_PULSE_SHAPE
|
||||||
).value
|
).value
|
||||||
pulse_amplitude = abs(pulse_shape.get_pulse_amplitude(event.duration, resolution = dwell_time))
|
pulse_amplitude = abs(
|
||||||
|
pulse_shape.get_pulse_amplitude(
|
||||||
|
event.duration, resolution=dwell_time
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
amplitude_array.append(pulse_amplitude)
|
amplitude_array.append(pulse_amplitude)
|
||||||
elif (parameter.name == self.module.model.TX and parameter.get_option_by_name(TXPulse.RELATIVE_AMPLITUDE).value
|
elif (
|
||||||
== 0):
|
parameter.name == self.module.model.TX
|
||||||
|
and parameter.get_option_by_name(TXPulse.RELATIVE_AMPLITUDE).value
|
||||||
|
== 0
|
||||||
|
):
|
||||||
# If we have a wait, we need to add it to the pulse array
|
# If we have a wait, we need to add it to the pulse array
|
||||||
amplitude_array.append(np.zeros(int(event.duration / dwell_time)))
|
amplitude_array.append(np.zeros(int(event.duration / dwell_time)))
|
||||||
|
|
||||||
amplitude_array = np.concatenate(amplitude_array)
|
amplitude_array = np.concatenate(amplitude_array)
|
||||||
|
|
||||||
# This has not yet been implemented right now the phase is always 0
|
# This has not yet been implemented right now the phase is always 0
|
||||||
phase_array = np.zeros(len(amplitude_array))
|
phase_array = np.zeros(len(amplitude_array))
|
||||||
|
|
||||||
pulse_array = PulseArray(
|
pulse_array = PulseArray(
|
||||||
pulseamplitude = amplitude_array,
|
pulseamplitude=amplitude_array,
|
||||||
pulsephase = phase_array,
|
pulsephase=phase_array,
|
||||||
dwell_time = float(dwell_time)
|
dwell_time=float(dwell_time),
|
||||||
)
|
)
|
||||||
|
|
||||||
return pulse_array
|
return pulse_array
|
||||||
|
|
||||||
def get_simulation(self, sample : Sample, pulse_array : PulseArray) -> Simulation:
|
def get_simulation(self, sample: Sample, pulse_array: PulseArray) -> Simulation:
|
||||||
"""This method creates a simulation object based on the settings and the pulse sequence.
|
"""This method creates a simulation object based on the settings and the pulse sequence.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -183,24 +201,29 @@ class SimulatorController(BaseSpectrometerController):
|
||||||
|
|
||||||
noise = float(model.get_setting_by_name(model.NOISE).value)
|
noise = float(model.get_setting_by_name(model.NOISE).value)
|
||||||
simulation = Simulation(
|
simulation = Simulation(
|
||||||
sample = sample,
|
sample=sample,
|
||||||
pulse = pulse_array,
|
pulse=pulse_array,
|
||||||
number_isochromats = int(model.get_setting_by_name(model.NUMBER_ISOCHROMATS).value),
|
number_isochromats=int(
|
||||||
initial_magnetization = float(model.get_setting_by_name(model.INITIAL_MAGNETIZATION).value),
|
model.get_setting_by_name(model.NUMBER_ISOCHROMATS).value
|
||||||
gradient = float(model.get_setting_by_name(model.GRADIENT).value),
|
),
|
||||||
noise = float(model.get_setting_by_name(model.NOISE).value),
|
initial_magnetization=float(
|
||||||
length_coil = float(model.get_setting_by_name(model.LENGTH_COIL).value),
|
model.get_setting_by_name(model.INITIAL_MAGNETIZATION).value
|
||||||
diameter_coil = float(model.get_setting_by_name(model.DIAMETER_COIL).value),
|
),
|
||||||
number_turns = float(model.get_setting_by_name(model.NUMBER_TURNS).value),
|
gradient=float(model.get_setting_by_name(model.GRADIENT).value),
|
||||||
power_amplifier_power =float( model.get_setting_by_name(model.POWER_AMPLIFIER_POWER).value),
|
noise=float(model.get_setting_by_name(model.NOISE).value),
|
||||||
gain = float(model.get_setting_by_name(model.GAIN).value),
|
length_coil=float(model.get_setting_by_name(model.LENGTH_COIL).value),
|
||||||
temperature = float(model.get_setting_by_name(model.TEMPERATURE).value),
|
diameter_coil=float(model.get_setting_by_name(model.DIAMETER_COIL).value),
|
||||||
averages = int(model.averages),
|
number_turns=float(model.get_setting_by_name(model.NUMBER_TURNS).value),
|
||||||
loss_TX = float(model.get_setting_by_name(model.LOSS_TX).value),
|
power_amplifier_power=float(
|
||||||
loss_RX = float(model.get_setting_by_name(model.LOSS_RX).value)
|
model.get_setting_by_name(model.POWER_AMPLIFIER_POWER).value
|
||||||
|
),
|
||||||
|
gain=float(model.get_setting_by_name(model.GAIN).value),
|
||||||
|
temperature=float(model.get_setting_by_name(model.TEMPERATURE).value),
|
||||||
|
averages=int(model.averages),
|
||||||
|
loss_TX=float(model.get_setting_by_name(model.LOSS_TX).value),
|
||||||
|
loss_RX=float(model.get_setting_by_name(model.LOSS_RX).value),
|
||||||
)
|
)
|
||||||
return simulation
|
return simulation
|
||||||
|
|
||||||
|
|
||||||
def calculate_dwelltime(self) -> float:
|
def calculate_dwelltime(self) -> float:
|
||||||
"""This method calculates the dwell time based on the settings and the pulse sequence.
|
"""This method calculates the dwell time based on the settings and the pulse sequence.
|
||||||
|
@ -208,11 +231,13 @@ class SimulatorController(BaseSpectrometerController):
|
||||||
Returns:
|
Returns:
|
||||||
float: The dwell time in seconds.
|
float: The dwell time in seconds.
|
||||||
"""
|
"""
|
||||||
n_points = int(self.module.model.get_setting_by_name(self.module.model.NUMBER_POINTS).value)
|
n_points = int(
|
||||||
|
self.module.model.get_setting_by_name(self.module.model.NUMBER_POINTS).value
|
||||||
|
)
|
||||||
simulation_length = self.calculate_simulation_length()
|
simulation_length = self.calculate_simulation_length()
|
||||||
dwell_time = simulation_length / n_points
|
dwell_time = simulation_length / n_points
|
||||||
return dwell_time
|
return dwell_time
|
||||||
|
|
||||||
def calculate_simulation_length(self) -> float:
|
def calculate_simulation_length(self) -> float:
|
||||||
"""This method calculates the simulation length based on the settings and the pulse sequence.
|
"""This method calculates the simulation length based on the settings and the pulse sequence.
|
||||||
|
|
||||||
|
@ -225,16 +250,59 @@ class SimulatorController(BaseSpectrometerController):
|
||||||
simulation_length += event.duration
|
simulation_length += event.duration
|
||||||
return simulation_length
|
return simulation_length
|
||||||
|
|
||||||
def set_frequency(self, value : str) -> None:
|
def translate_rx_event(self) -> tuple:
|
||||||
""" This method is called when the set_frequency signal is received from the core.
|
"""This method translates the RX event of the pulse sequence to the limr object.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: A tuple containing the start and stop time of the RX event in µs"""
|
||||||
|
# This is a correction factor for the RX event. The offset of the first pulse is 2.2µs longer than from the specified samples.
|
||||||
|
events = self.module.model.pulse_programmer.model.pulse_sequence.events
|
||||||
|
|
||||||
|
previous_events_duration = 0
|
||||||
|
offset = 0
|
||||||
|
rx_duration = 0
|
||||||
|
for event in events:
|
||||||
|
logger.debug("Event %s has parameters: %s", event.name, event.parameters)
|
||||||
|
for parameter in event.parameters.values():
|
||||||
|
logger.debug(
|
||||||
|
"Parameter %s has options: %s", parameter.name, parameter.options
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
parameter.name == self.module.model.RX
|
||||||
|
and parameter.get_option_by_name(RXReadout.RX).value
|
||||||
|
):
|
||||||
|
# Get the length of all previous events
|
||||||
|
previous_events = events[: events.index(event)]
|
||||||
|
previous_events_duration = sum(
|
||||||
|
[event.duration for event in previous_events]
|
||||||
|
)
|
||||||
|
rx_duration = event.duration
|
||||||
|
|
||||||
|
rx_begin = float(previous_events_duration)
|
||||||
|
if rx_duration:
|
||||||
|
rx_stop = rx_begin + float(rx_duration)
|
||||||
|
return rx_begin * 1e6, rx_stop * 1e6
|
||||||
|
|
||||||
|
else:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
def set_frequency(self, value: str) -> None:
|
||||||
|
"""This method is called when the set_frequency signal is received from the core.
|
||||||
For the simulator this just prints a warning that the simulator is selected.
|
For the simulator this just prints a warning that the simulator is selected.
|
||||||
"""
|
"""
|
||||||
self.module.nqrduck_signal.emit(
|
self.module.nqrduck_signal.emit(
|
||||||
"notification", ["Warning", "Could not set averages to because the simulator is selected as active spectrometer "]
|
"notification",
|
||||||
|
[
|
||||||
|
"Warning",
|
||||||
|
"Could not set averages to because the simulator is selected as active spectrometer ",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_averages(self, value : str) -> None:
|
def set_averages(self, value: str) -> None:
|
||||||
""" This method is called when the set_averages signal is received from the core.
|
"""This method is called when the set_averages signal is received from the core.
|
||||||
It sets the averages in the model used for the simulation.
|
It sets the averages in the model used for the simulation.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|
|
@ -102,6 +102,8 @@ class SimulatorModel(BaseSpectrometerModel):
|
||||||
# self.add_pulse_parameter_option(self.GATE, Gate)
|
# self.add_pulse_parameter_option(self.GATE, Gate)
|
||||||
self.add_pulse_parameter_option(self.RX, RXReadout)
|
self.add_pulse_parameter_option(self.RX, RXReadout)
|
||||||
|
|
||||||
|
self.averages = 1
|
||||||
|
|
||||||
# Try to load the pulse programmer module
|
# Try to load the pulse programmer module
|
||||||
try:
|
try:
|
||||||
from nqrduck_pulseprogrammer.pulseprogrammer import pulse_programmer
|
from nqrduck_pulseprogrammer.pulseprogrammer import pulse_programmer
|
||||||
|
|
Loading…
Reference in a new issue