Skip to content

Commit

Permalink
Added docstrings
Browse files Browse the repository at this point in the history
  • Loading branch information
nulinspiratie committed Mar 7, 2024
1 parent c7cdad2 commit e04fa7f
Showing 1 changed file with 155 additions and 4 deletions.
159 changes: 155 additions & 4 deletions quam/components/octave.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,30 @@

@quam_dataclass
class Octave(QuamComponent):
"""QuAM component for the QM Octave.
The QM Octave is a device that can be used to upconvert and downconvert signals. It
has 5 RF outputs and 2 RF inputs. Each RF_output has an associated
`OctaveUpConverter`, and similarly each RF_input has an `OctaveDownConverter`.
In many cases the Octave is connected to a single OPX in the default configuration,
i.e. OPX outputs are connected to the corresponding Octave I/Q input, and Octave IF
outputs are connected to the corresponding OPX input. In this case you can configure
the Octave with the correct `FrequencyConverter`s using
`Octave.initialize_default_connectivity()`.
Args:
name: The name of the Octave. Must be unique
ip: The IP address of the Octave. Used in `Octave.get_octave_config()`
port: The port number of the Octave. Used in `Octave.get_octave_config()`
RF_outputs: A dictionary of `OctaveUpConverter` objects. The keys are the
output numbers (1-5).
RF_inputs: A dictionary of `OctaveDownConverter` objects. The keys are the
input numbers (1-2).
loopbacks: A list of loopback connections, for example to connect a local
oscillator. See the QUA Octave documentation for details.
"""

name: str
ip: str
port: int
Expand All @@ -38,6 +62,18 @@ class Octave(QuamComponent):
loopbacks: List[Tuple[Tuple[str, str], str]] = field(default_factory=list)

def initialize_default_connectivity(self):
"""Initialize the Octave with default connectivity.
This method initializes the Octave with default connectivity, i.e. it connects
the Octave to a single OPX. It creates an `OctaveUpConverter` for each RF output
and an `OctaveDownConverter` for each RF input. The `OctaveUpConverter` objects
are added to `Octave.RF_outputs` and the `OctaveDownConverter` objects are added
to `Octave.RF_inputs`.
Raises:
ValueError: If the Octave already has RF_outputs or RF_inputs.
"""
if self.RF_outputs:
raise ValueError(
"Error initializing Octave with default connectivity. "
Expand Down Expand Up @@ -65,6 +101,16 @@ def get_octave_config(self) -> QmOctaveConfig:
return octave_config

def apply_to_config(self, config: Dict) -> None:
"""Add the Octave configuration to a config dictionary.
This method is called by the `QuamComponent.generate_config` method.
Args:
config: A dictionary representing a QUA config file.
Raises:
KeyError: If the Octave is already in the config.
"""
if "octaves" not in config:
config["octaves"] = {}
if self.name in config["octaves"]:
Expand All @@ -83,6 +129,15 @@ def apply_to_config(self, config: Dict) -> None:

@quam_dataclass
class OctaveFrequencyConverter(FrequencyConverter, ABC):
"""Base class for OctaveUpConverter and OctaveDownConverter.
Args:
id: The id of the converter. Must be unique within the Octave.
For OctaveUpConverter, the id is used as the RF output number.
For OctaveDownConverter, the id is used as the RF input number.
channel: The channel that the converter is connected to.
"""

id: int
channel: Channel = None

Expand All @@ -97,10 +152,22 @@ def octave(self) -> Optional[Octave]:

@property
def config_settings(self) -> Dict[str, Any]:
"""Specifies that the converter will be added to the config after the Octave."""
return {"after": [self.octave]}

def apply_to_config(self, config: Dict) -> None:
super().apply_to_config(config) # TODO is this necessary?
"""Add information about the frequency converter to the QUA config
This method is called by the `QuamComponent.generate_config` method.
Args:
config: A dictionary representing a QUA config file.
Raises:
KeyError: If the Octave is not in the config, or if config["octaves"] does
not exist.
"""
super().apply_to_config(config)

if "octaves" not in config:
raise KeyError('Error generating config: "octaves" entry not found')
Expand All @@ -120,15 +187,54 @@ def apply_to_config(self, config: Dict) -> None:

@quam_dataclass
class OctaveUpConverter(OctaveFrequencyConverter):
gain: float = 0 # range [-20:0.5:20]
"""A frequency upconverter for the QM Octave.
The OctaveUpConverter represents a frequency upconverter in the QM Octave. Usually
an IQChannel is connected `OctaveUpconverter.channel`, in which case the two OPX
outputs are connected to the I and Q inputs of the OctaveUpConverter.
The OPX outputs are specified in the `OctaveUpConverter.channel` attribute.
The channel is either an IQChannel or a SingleChannel.
Args:
id: The RF output id, must be between 1-5.
LO_frequency: The local oscillator frequency in Hz, between 2 and 18 GHz.
LO_source: The local oscillator source, "internal" (default) or "external".
gain: The gain of the output, between -20 and 20 dB in steps of 0.5.
Default is 0 dB.
output_mode: Sets the fast switch's mode of the up converter module.
Can be "always_on" / "always_off" / "triggered" / "triggered_reversed".
The default is "always_off".
- "always_on" - Output is always on
- "always_off" - Output is always off
- "triggered" - The output will play when rising edge is detected in the
octave's digital port.
- "triggered_reversed" - The output will play when falling edge is detected
in the octave's digital port.
input_attenuators: Whether the I and Q ports have a 10 dB attenuator before
entering the mixer. Off by default.
"""

LO_frequency: float
LO_source: Literal["internal", "external"] = "internal"
LO_frequency: float # Between 2 and 18 GHz
gain: float = 0
output_mode: Literal[
"always_on", "always_off", "triggered", "triggered_reersed"
] = "always_on"
] = "always_off"
input_attenuators: Literal["off", "on"] = "off"

def apply_to_config(self, config: Dict) -> None:
"""Add information about the frequency up-converter to the QUA config
This method is called by the `QuamComponent.generate_config` method.
Args:
config: A dictionary representing a QUA config file.
Raises:
KeyError: If the Octave is not in the config, or if config["octaves"] does
not exist.
KeyError: If the Octave already has an entry for the OctaveUpConverter.
"""
super().apply_to_config(config)

if self.id in config["octaves"][self.octave.name]["RF_outputs"]:
Expand All @@ -154,6 +260,36 @@ def apply_to_config(self, config: Dict) -> None:

@quam_dataclass
class OctaveDownConverter(OctaveFrequencyConverter):
"""A frequency downconverter for the QM Octave.
The OctaveDownConverter represents a frequency downconverter in the QM Octave. The
OctaveDownConverter is usually connected to an InOutIQChannel, in which case the
two OPX inputs are connected to the IF outputs of the OctaveDownConverter. The
OPX inputs are specified in the `OctaveDownConverter.channel` attribute. The
channel is either an InOutIQChannel or an InOutSingleChannel.
Args:
id: The RF input id, must be between 1-2.
LO_frequency: The local oscillator frequency in Hz, between 2 and 18 GHz.
LO_source: The local oscillator source, "internal" or "external.
For down converter 1 "internal" is the default,
for down converter 2 "external" is the default.
IF_mode_I: Sets the mode of the I port of the IF Down Converter module as can be
seen in the octave block diagram (see Octave page in QUA documentation).
Can be "direct" / "envelope" / "mixer" / "off". The default is "direct".
- "direct" - The signal bypasses the IF module.
- "envelope" - The signal passes through an envelope detector.
- "mixer" - The signal passes through a low-frequency mixer.
- "off" - the signal doesn't pass to the output port.
IF_mode_Q: Sets the mode of the Q port of the IF Down Converter module.
IF_output_I: The output port of the IF Down Converter module for the I port.
Can be 1 or 2. The default is 1. This will be 2 if the IF outputs
are connected to the opposite OPX inputs
IF_output_Q: The output port of the IF Down Converter module for the Q port.
Can be 1 or 2. The default is 2. This will be 1 if the IF outputs
are connected to the opposite OPX inputs.
"""

LO_frequency: float # Between 2 and 18 GHz
LO_source: Literal["internal", "external"] = (
"internal" # default is internal for LO 1, external for LO 2
Expand All @@ -165,9 +301,24 @@ class OctaveDownConverter(OctaveFrequencyConverter):

@property
def config_settings(self):
"""Specifies that the converter will be added to the config after the Octave."""
return {"after": self.octave}

def apply_to_config(self, config: Dict) -> None:
"""Add information about the frequency down-converter to the QUA config
This method is called by the `QuamComponent.generate_config` method.
Args:
config: A dictionary representing a QUA config file.
Raises:
KeyError: If the Octave is not in the config, or if config["octaves"] does
not exist.
KeyError: If the Octave already has an entry for the OctaveDownConverter.
ValueError: If the IF_output_I and IF_output_Q are already assigned to
other ports.
"""
super().apply_to_config(config)

if self.id in config["octaves"][self.octave.name]["RF_inputs"]:
Expand Down

0 comments on commit e04fa7f

Please sign in to comment.