diff --git a/src/quackseq/measurement.py b/src/quackseq/measurement.py index 6d92ba4..666419d 100644 --- a/src/quackseq/measurement.py +++ b/src/quackseq/measurement.py @@ -3,6 +3,7 @@ import logging import numpy as np from scipy.optimize import curve_fit +from numpy.fft import ifft, fft from quackseq.functions import Function from quackseq.signalprocessing import SignalProcessing as sp @@ -18,8 +19,8 @@ class Measurement: Args: name (str): Name of the measurement. - tdx (np.array): Time axis for the x axis of the measurement data. - tdy (np.array): Time axis for the y axis of the measurement data. + tdx (np.array): Time axis for the x axis of the measurement data. This can be multi-dimensional. + tdy (np.array): Time axis for the y axis of the measurement data. This can be multi-dimensional. target_frequency (float): Target frequency of the measurement. frequency_shift (float, optional): Frequency shift of the measurement. Defaults to 0. IF_frequency (float, optional): Intermediate frequency of the measurement. Defaults to 0. @@ -77,6 +78,18 @@ class Measurement: IF_frequency=self.IF_frequency, ) return apodized_measurement + + def phase_shift(self, phase: float, axis = 0) -> np.array: + """Applies a phase shift to the measurement data. + + Args: + phase (float): Phase shift in degrees. + axis (int): Axis to apply the phase shift to. Defaults to 0. + """ + spec = fft(self.tdy) + shifted_signal = np.exp(1j * np.deg2rad(phase)) * spec + + self.tdy = ifft(shifted_signal, n=len(self.tdy)) def add_fit(self, fit: "Fit") -> None: """Adds a fit to the measurement. diff --git a/src/quackseq/pulseparameters.py b/src/quackseq/pulseparameters.py index 9d43e02..12c3b8d 100644 --- a/src/quackseq/pulseparameters.py +++ b/src/quackseq/pulseparameters.py @@ -149,7 +149,8 @@ class RXReadout(PulseParameter): RX (str): The RX Readout state. """ - RX = "RX" + RX = "Enable RX Readout" + PHASE = "RX Phase (°)" def __init__(self, name) -> None: """Initializes the RX Readout PulseParameter. @@ -159,6 +160,11 @@ class RXReadout(PulseParameter): super().__init__(name) self.add_option(BooleanOption(self.RX, False)) + # Receiver Phase + self.add_option( + NumericOption(self.PHASE, 0, min_value=0, max_value=360, is_float=True) + ) + class Gate(PulseParameter): """Basic PulseParameter for the Gate. It includes an option for the Gate state. diff --git a/src/quackseq/spectrometer/spectrometer_controller.py b/src/quackseq/spectrometer/spectrometer_controller.py index 941ac59..007e158 100644 --- a/src/quackseq/spectrometer/spectrometer_controller.py +++ b/src/quackseq/spectrometer/spectrometer_controller.py @@ -30,7 +30,7 @@ class SpectrometerController: """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 + tuple: A tuple containing the start and stop time of the RX event in µs and the phase of the RX event. """ # 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 = sequence.events @@ -55,14 +55,15 @@ class SpectrometerController: [event.duration for event in previous_events] ) rx_duration = event.duration + phase = parameter.get_option_by_name(RXReadout.PHASE).value rx_begin = float(previous_events_duration) if rx_duration: rx_stop = rx_begin + float(rx_duration) - return rx_begin * 1e6, rx_stop * 1e6 + return rx_begin * 1e6, rx_stop * 1e6, phase else: - return None, None + return None, None, None def calculate_simulation_length(self, sequence: QuackSequence) -> float: """This method calculates the simulation length based on the settings and the pulse sequence.