Skip to content

Commit

Permalink
Add measure capabilities to MW FEM
Browse files Browse the repository at this point in the history
  • Loading branch information
nulinspiratie committed Sep 2, 2024
1 parent 2a9d4ea commit deaeabb
Showing 1 changed file with 106 additions and 103 deletions.
209 changes: 106 additions & 103 deletions quam/components/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -1086,100 +1086,7 @@ def apply_to_config(self, config: dict):


@quam_dataclass
class InIQChannel(Channel):
"""QuAM component for an IQ input channel
Args:
operations (Dict[str, Pulse]): A dictionary of pulses to be played on this
channel. The key is the pulse label (e.g. "readout") and value is a
ReadoutPulse.
id (str, int): The id of the channel, used to generate the name.
Can be a string, or an integer in which case it will add
`Channel._default_label`.
opx_input_I (Tuple[str, int]): Channel I input port from the OPX perspective,
a tuple of (controller_name, port).
opx_input_Q (Tuple[str, int]): Channel Q input port from the OPX perspective,
a tuple of (controller_name, port).
opx_input_offset_I float: The offset of the I channel. Default is 0.
opx_input_offset_Q float: The offset of the Q channel. Default is 0.
frequency_converter_down (Optional[FrequencyConverter]): Frequency converter
QuAM component for the IQ input port. Only needed for the old Octave.
time_of_flight (int): Round-trip signal duration in nanoseconds.
smearing (int): Additional window of ADC integration in nanoseconds.
Used to account for signal smearing.
input_gain (float): The gain of the input channel. Default is None.
"""

opx_input_I: Union[Tuple[str, int], Tuple[str, int, int], LFAnalogInputPort]
opx_input_Q: Union[Tuple[str, int], Tuple[str, int, int], LFAnalogInputPort]

time_of_flight: int = 24
smearing: int = 0

opx_input_offset_I: float = None
opx_input_offset_Q: float = None

input_gain: Optional[int] = None

frequency_converter_down: BaseFrequencyConverter = None

_default_label: ClassVar[str] = "IQ"

def apply_to_config(self, config: dict):
"""Adds this InOutIQChannel to the QUA configuration.
See [`QuamComponent.apply_to_config`][quam.core.quam_classes.QuamComponent.apply_to_config]
for details.
"""
super().apply_to_config(config)

# Note outputs instead of inputs because it's w.r.t. the QPU
element_config = config["elements"][self.name]
element_config["smearing"] = self.smearing
element_config["time_of_flight"] = self.time_of_flight

from quam.components.octave import OctaveDownConverter

if isinstance(self.frequency_converter_down, OctaveDownConverter):
octave = self.frequency_converter_down.octave
if octave is None:
raise ValueError(
f"Error generating config: channel {self.name} has an "
f"OctaveDownConverter (id={self.frequency_converter_down.id}) "
"without an attached Octave"
)
element_config["RF_outputs"] = {
"port": (octave.name, self.frequency_converter_down.id)
}
elif str_ref.is_reference(self.frequency_converter_down):
raise ValueError(
f"Error generating config: channel {self.name} could not determine "
f'"frequency_converter_down", it seems to point to a non-existent '
f"reference: {self.frequency_converter_down}"
)
else:
# To be filled in next section
element_config["outputs"] = {}

opx_inputs = [self.opx_input_I, self.opx_input_Q]
offsets = [self.opx_input_offset_I, self.opx_input_offset_Q]
input_gain = int(self.input_gain if self.input_gain is not None else 0)
for k, (opx_input, offset) in enumerate(zip(opx_inputs, offsets), start=1):
if isinstance(opx_input, LFAnalogInputPort):
opx_port = opx_input
elif len(opx_input) == 2:
opx_port = OPXPlusAnalogInputPort(
*opx_input, offset=offset, gain_db=input_gain
)
opx_port.apply_to_config(config)
else:
opx_port = LFFEMAnalogInputPort(
*opx_input, offset=offset, gain_db=input_gain
)
opx_port.apply_to_config(config)
if not isinstance(self.frequency_converter_down, OctaveDownConverter):
element_config["outputs"][f"out{k}"] = opx_port.port_tuple

class _InComplexChannel(Channel, ABC):
def measure(
self,
pulse_name: str,
Expand Down Expand Up @@ -1246,10 +1153,10 @@ def measure(
def measure_accumulated(
self,
pulse_name: str,
amplitude_scale: Union[float, AmpValuesType] = None,
num_segments: int = None,
segment_length: int = None,
qua_vars: Tuple[QuaVariableType, ...] = None,
amplitude_scale: Optional[Union[float, AmpValuesType]] = None,
num_segments: Optional[int] = None,
segment_length: Optional[int] = None,
qua_vars: Optional[Tuple[QuaVariableType, ...]] = None,
stream=None,
) -> Tuple[QuaVariableType, QuaVariableType, QuaVariableType, QuaVariableType]:
"""Perform an accumulated dual demodulation measurement on this channel.
Expand Down Expand Up @@ -1332,10 +1239,10 @@ def measure_accumulated(
def measure_sliced(
self,
pulse_name: str,
amplitude_scale: Union[float, AmpValuesType] = None,
num_segments: int = None,
segment_length: int = None,
qua_vars: Tuple[QuaVariableType, ...] = None,
amplitude_scale: Optional[Union[float, AmpValuesType]] = None,
num_segments: Optional[int] = None,
segment_length: Optional[int] = None,
qua_vars: Optional[Tuple[QuaVariableType, ...]] = None,
stream=None,
) -> Tuple[QuaVariableType, QuaVariableType, QuaVariableType, QuaVariableType]:
"""Perform a sliced dual demodulation measurement on this channel.
Expand Down Expand Up @@ -1416,6 +1323,102 @@ def measure_sliced(
return tuple(qua_vars)


@quam_dataclass
class InIQChannel(_InComplexChannel):
"""QuAM component for an IQ input channel
Args:
operations (Dict[str, Pulse]): A dictionary of pulses to be played on this
channel. The key is the pulse label (e.g. "readout") and value is a
ReadoutPulse.
id (str, int): The id of the channel, used to generate the name.
Can be a string, or an integer in which case it will add
`Channel._default_label`.
opx_input_I (Tuple[str, int]): Channel I input port from the OPX perspective,
a tuple of (controller_name, port).
opx_input_Q (Tuple[str, int]): Channel Q input port from the OPX perspective,
a tuple of (controller_name, port).
opx_input_offset_I float: The offset of the I channel. Default is 0.
opx_input_offset_Q float: The offset of the Q channel. Default is 0.
frequency_converter_down (Optional[FrequencyConverter]): Frequency converter
QuAM component for the IQ input port. Only needed for the old Octave.
time_of_flight (int): Round-trip signal duration in nanoseconds.
smearing (int): Additional window of ADC integration in nanoseconds.
Used to account for signal smearing.
input_gain (float): The gain of the input channel. Default is None.
"""

opx_input_I: Union[Tuple[str, int], Tuple[str, int, int], LFAnalogInputPort]
opx_input_Q: Union[Tuple[str, int], Tuple[str, int, int], LFAnalogInputPort]

time_of_flight: int = 24
smearing: int = 0

opx_input_offset_I: float = None
opx_input_offset_Q: float = None

input_gain: Optional[int] = None

frequency_converter_down: BaseFrequencyConverter = None

_default_label: ClassVar[str] = "IQ"

def apply_to_config(self, config: dict):
"""Adds this InOutIQChannel to the QUA configuration.
See [`QuamComponent.apply_to_config`][quam.core.quam_classes.QuamComponent.apply_to_config]
for details.
"""
super().apply_to_config(config)

# Note outputs instead of inputs because it's w.r.t. the QPU
element_config = config["elements"][self.name]
element_config["smearing"] = self.smearing
element_config["time_of_flight"] = self.time_of_flight

from quam.components.octave import OctaveDownConverter

if isinstance(self.frequency_converter_down, OctaveDownConverter):
octave = self.frequency_converter_down.octave
if octave is None:
raise ValueError(
f"Error generating config: channel {self.name} has an "
f"OctaveDownConverter (id={self.frequency_converter_down.id}) "
"without an attached Octave"
)
element_config["RF_outputs"] = {
"port": (octave.name, self.frequency_converter_down.id)
}
elif str_ref.is_reference(self.frequency_converter_down):
raise ValueError(
f"Error generating config: channel {self.name} could not determine "
f'"frequency_converter_down", it seems to point to a non-existent '
f"reference: {self.frequency_converter_down}"
)
else:
# To be filled in next section
element_config["outputs"] = {}

opx_inputs = [self.opx_input_I, self.opx_input_Q]
offsets = [self.opx_input_offset_I, self.opx_input_offset_Q]
input_gain = int(self.input_gain if self.input_gain is not None else 0)
for k, (opx_input, offset) in enumerate(zip(opx_inputs, offsets), start=1):
if isinstance(opx_input, LFAnalogInputPort):
opx_port = opx_input
elif len(opx_input) == 2:
opx_port = OPXPlusAnalogInputPort(
*opx_input, offset=offset, gain_db=input_gain
)
opx_port.apply_to_config(config)
else:
opx_port = LFFEMAnalogInputPort(
*opx_input, offset=offset, gain_db=input_gain
)
opx_port.apply_to_config(config)
if not isinstance(self.frequency_converter_down, OctaveDownConverter):
element_config["outputs"][f"out{k}"] = opx_port.port_tuple


@quam_dataclass
class InOutSingleChannel(SingleChannel, InSingleChannel):
"""QuAM component for a single (not IQ) input + output channel.
Expand Down Expand Up @@ -1587,7 +1590,7 @@ def apply_to_config(self, config: Dict) -> None:


@quam_dataclass
class InMWChannel(Channel):
class InMWChannel(_InComplexChannel):
"""QuAM component for a MW FEM input channel
Args:
Expand Down

0 comments on commit deaeabb

Please sign in to comment.