Skip to content

Welcome to the QuAM Octave API Documentation

The Octave component in the Quantum Abstract Machine (QuAM) manages signal upconversion and downconversion through its frequency converters. This section provides an API guide for setting up and customizing the Octave, detailing its integration with quantum processors for efficient signal processing. Explore the capabilities, configuration options, and practical examples to enhance your quantum operations with Octave's advanced functionalities.

Octave

Bases: 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 FrequencyConverters using Octave.initialize_default_connectivity().

Parameters:

Name Type Description Default
name

The name of the Octave. Must be unique

required
ip

The IP address of the Octave. Used in Octave.get_octave_config()

required
port

The port number of the Octave. Used in Octave.get_octave_config()

required
calibration_db_path

The path to the calibration database. If not specified, the current working directory is used.

required
RF_outputs

A dictionary of OctaveUpConverter objects. The keys are the output numbers (1-5).

required
RF_inputs

A dictionary of OctaveDownConverter objects. The keys are the input numbers (1-2).

required
loopbacks

A list of loopback connections, for example to connect a local oscillator. See the QUA Octave documentation for details.

required
Source code in quam/components/octave.py
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
@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()`
        calibration_db_path: The path to the calibration database. If not specified, the
            current working directory is used.
        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
    calibration_db_path: str = None

    RF_outputs: Dict[int, "OctaveUpConverter"] = field(default_factory=dict)
    RF_inputs: Dict[int, "OctaveDownConverter"] = field(default_factory=dict)
    loopbacks: List[Tuple[Tuple[str, str], str]] = field(default_factory=list)

    def initialize_frequency_converters(self):
        """Initialize the Octave frequency converterswith 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. "
                "octave.RF_outputs is not empty"
            )
        if self.RF_inputs:
            raise ValueError(
                "Error initializing Octave with default connectivity. "
                "octave.IF_outputs is not empty"
            )

        for idx in range(1, 6):
            self.RF_outputs[idx] = OctaveUpConverter(
                id=idx,
                LO_frequency=None,  # TODO What should default be?
            )

        for idx in range(1, 3):
            LO_source = "internal" if idx == 1 else "external"
            self.RF_inputs[idx] = OctaveDownConverter(
                id=idx, LO_frequency=None, LO_source=LO_source
            )

    def get_octave_config(self) -> QmOctaveConfig:
        """Return a QmOctaveConfig object with the current Octave configuration."""
        octave_config = QmOctaveConfig()

        if self.calibration_db_path is not None:
            octave_config.set_calibration_db(self.calibration_db_path)
        else:
            octave_config.set_calibration_db(os.getcwd())

        octave_config.add_device_info(self.name, self.ip, self.port)
        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"]:
            raise KeyError(
                f'Error generating config: config["octaves"] already contains an entry '
                f' for Octave "{self.name}"'
            )

        config["octaves"][self.name] = {
            "RF_outputs": {},
            "IF_outputs": {},
            "RF_inputs": {},
            "loopbacks": list(self.loopbacks),
        }

apply_to_config(config)

Add the Octave configuration to a config dictionary.

This method is called by the QuamComponent.generate_config method.

Parameters:

Name Type Description Default
config Dict

A dictionary representing a QUA config file.

required

Raises:

Type Description
KeyError

If the Octave is already in the config.

Source code in quam/components/octave.py
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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"]:
        raise KeyError(
            f'Error generating config: config["octaves"] already contains an entry '
            f' for Octave "{self.name}"'
        )

    config["octaves"][self.name] = {
        "RF_outputs": {},
        "IF_outputs": {},
        "RF_inputs": {},
        "loopbacks": list(self.loopbacks),
    }

get_octave_config()

Return a QmOctaveConfig object with the current Octave configuration.

Source code in quam/components/octave.py
105
106
107
108
109
110
111
112
113
114
115
def get_octave_config(self) -> QmOctaveConfig:
    """Return a QmOctaveConfig object with the current Octave configuration."""
    octave_config = QmOctaveConfig()

    if self.calibration_db_path is not None:
        octave_config.set_calibration_db(self.calibration_db_path)
    else:
        octave_config.set_calibration_db(os.getcwd())

    octave_config.add_device_info(self.name, self.ip, self.port)
    return octave_config

initialize_frequency_converters()

Initialize the Octave frequency converterswith 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:

Type Description
ValueError

If the Octave already has RF_outputs or RF_inputs.

Source code in quam/components/octave.py
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def initialize_frequency_converters(self):
    """Initialize the Octave frequency converterswith 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. "
            "octave.RF_outputs is not empty"
        )
    if self.RF_inputs:
        raise ValueError(
            "Error initializing Octave with default connectivity. "
            "octave.IF_outputs is not empty"
        )

    for idx in range(1, 6):
        self.RF_outputs[idx] = OctaveUpConverter(
            id=idx,
            LO_frequency=None,  # TODO What should default be?
        )

    for idx in range(1, 3):
        LO_source = "internal" if idx == 1 else "external"
        self.RF_inputs[idx] = OctaveDownConverter(
            id=idx, LO_frequency=None, LO_source=LO_source
        )

OctaveFrequencyConverter

Bases: BaseFrequencyConverter, ABC

Base class for OctaveUpConverter and OctaveDownConverter.

Parameters:

Name Type Description Default
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.

required
channel

The channel that the converter is connected to.

required
Source code in quam/components/octave.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
@quam_dataclass
class OctaveFrequencyConverter(BaseFrequencyConverter, 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

    @property
    def octave(self) -> Optional[Octave]:
        if self.parent is None:
            return None
        parent_parent = getattr(self.parent, "parent")
        if not isinstance(parent_parent, Octave):
            return None
        return parent_parent

    @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:
        """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')

        if self.octave is None:
            raise KeyError(
                f"Error generating config: OctaveConverter with id {self.id} does not "
                "have an Octave parent"
            )

        if self.octave.name not in config["octaves"]:
            raise KeyError(
                'Error generating config: config["octaves"] does not have Octave'
                f' entry config["octaves"]["{self.octave.name}"]'
            )

config_settings: Dict[str, Any] property

Specifies that the converter will be added to the config after the Octave.

apply_to_config(config)

Add information about the frequency converter to the QUA config

This method is called by the QuamComponent.generate_config method.

Parameters:

Name Type Description Default
config Dict

A dictionary representing a QUA config file.

required

Raises:

Type Description
KeyError

If the Octave is not in the config, or if config["octaves"] does not exist.

Source code in quam/components/octave.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def apply_to_config(self, config: Dict) -> None:
    """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')

    if self.octave is None:
        raise KeyError(
            f"Error generating config: OctaveConverter with id {self.id} does not "
            "have an Octave parent"
        )

    if self.octave.name not in config["octaves"]:
        raise KeyError(
            'Error generating config: config["octaves"] does not have Octave'
            f' entry config["octaves"]["{self.octave.name}"]'
        )

OctaveUpConverter

Bases: OctaveFrequencyConverter

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.

Parameters:

Name Type Description Default
id

The RF output id, must be between 1-5.

required
LO_frequency

The local oscillator frequency in Hz, between 2 and 18 GHz.

required
LO_source

The local oscillator source, "internal" (default) or "external".

required
gain

The gain of the output, between -20 and 20 dB in steps of 0.5. Default is 0 dB.

required
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.

required
input_attenuators

Whether the I and Q ports have a 10 dB attenuator before entering the mixer. Off by default.

required
Source code in quam/components/octave.py
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
@quam_dataclass
class OctaveUpConverter(OctaveFrequencyConverter):
    """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 = None
    LO_source: Literal["internal", "external"] = "internal"
    gain: float = 0
    output_mode: Literal[
        "always_on", "always_off", "triggered", "triggered_reversed"
    ] = "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.

        Nothing is added to the config if the `OctaveUpConverter.channel` is not
        specified or if the `OctaveUpConverter.LO_frequency` is not specified.

        Args:
            config: A dictionary representing a QUA config file.

        Raises:
            ValueError: If the LO_frequency is not specified.
            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.
        """
        if not isinstance(self.LO_frequency, (int, float)):
            if self.channel is None:
                return
            else:
                raise ValueError(
                    f"Error generating config for Octave upconverter id={self.id}: "
                    "LO_frequency must be specified."
                )

        super().apply_to_config(config)

        if self.id in config["octaves"][self.octave.name]["RF_outputs"]:
            raise KeyError(
                f"Error generating config: "
                f'config["octaves"]["{self.octave.name}"]["RF_outputs"] '
                f'already has an entry for OctaveDownConverter with id "{self.id}"'
            )

        output_config = config["octaves"][self.octave.name]["RF_outputs"][self.id] = {
            "LO_frequency": self.LO_frequency,
            "LO_source": self.LO_source,
            "gain": self.gain,
            "output_mode": self.output_mode,
            "input_attenuators": self.input_attenuators,
        }
        if isinstance(self.channel, SingleChannel):
            if isinstance(self.channel.opx_output, LFAnalogOutputPort):
                output_config["I_connection"] = self.channel.opx_output.port_tuple
            else:
                output_config["I_connection"] = self.channel.opx_output
        elif isinstance(self.channel, IQChannel):
            if isinstance(self.channel.opx_output_I, LFAnalogOutputPort):
                output_config["I_connection"] = self.channel.opx_output_I.port_tuple
            else:
                output_config["I_connection"] = tuple(self.channel.opx_output_I)
            if isinstance(self.channel.opx_output_Q, LFAnalogOutputPort):
                output_config["Q_connection"] = self.channel.opx_output_Q.port_tuple
            else:
                output_config["Q_connection"] = tuple(self.channel.opx_output_Q)

apply_to_config(config)

Add information about the frequency up-converter to the QUA config

This method is called by the QuamComponent.generate_config method.

Nothing is added to the config if the OctaveUpConverter.channel is not specified or if the OctaveUpConverter.LO_frequency is not specified.

Parameters:

Name Type Description Default
config Dict

A dictionary representing a QUA config file.

required

Raises:

Type Description
ValueError

If the LO_frequency is not specified.

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.

Source code in quam/components/octave.py
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
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.

    Nothing is added to the config if the `OctaveUpConverter.channel` is not
    specified or if the `OctaveUpConverter.LO_frequency` is not specified.

    Args:
        config: A dictionary representing a QUA config file.

    Raises:
        ValueError: If the LO_frequency is not specified.
        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.
    """
    if not isinstance(self.LO_frequency, (int, float)):
        if self.channel is None:
            return
        else:
            raise ValueError(
                f"Error generating config for Octave upconverter id={self.id}: "
                "LO_frequency must be specified."
            )

    super().apply_to_config(config)

    if self.id in config["octaves"][self.octave.name]["RF_outputs"]:
        raise KeyError(
            f"Error generating config: "
            f'config["octaves"]["{self.octave.name}"]["RF_outputs"] '
            f'already has an entry for OctaveDownConverter with id "{self.id}"'
        )

    output_config = config["octaves"][self.octave.name]["RF_outputs"][self.id] = {
        "LO_frequency": self.LO_frequency,
        "LO_source": self.LO_source,
        "gain": self.gain,
        "output_mode": self.output_mode,
        "input_attenuators": self.input_attenuators,
    }
    if isinstance(self.channel, SingleChannel):
        if isinstance(self.channel.opx_output, LFAnalogOutputPort):
            output_config["I_connection"] = self.channel.opx_output.port_tuple
        else:
            output_config["I_connection"] = self.channel.opx_output
    elif isinstance(self.channel, IQChannel):
        if isinstance(self.channel.opx_output_I, LFAnalogOutputPort):
            output_config["I_connection"] = self.channel.opx_output_I.port_tuple
        else:
            output_config["I_connection"] = tuple(self.channel.opx_output_I)
        if isinstance(self.channel.opx_output_Q, LFAnalogOutputPort):
            output_config["Q_connection"] = self.channel.opx_output_Q.port_tuple
        else:
            output_config["Q_connection"] = tuple(self.channel.opx_output_Q)

OctaveDownConverter

Bases: 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.

Parameters:

Name Type Description Default
id

The RF input id, must be between 1-2.

required
LO_frequency

The local oscillator frequency in Hz, between 2 and 18 GHz.

required
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.

required
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.

required
IF_mode_Q

Sets the mode of the Q port of the IF Down Converter module.

required
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

required
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.

required
Source code in quam/components/octave.py
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
@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 = None
    LO_source: Literal["internal", "external"] = "internal"
    IF_mode_I: Literal["direct", "envelope", "mixer", "off"] = "direct"
    IF_mode_Q: Literal["direct", "envelope", "mixer", "off"] = "direct"
    IF_output_I: Literal[1, 2] = 1
    IF_output_Q: Literal[1, 2] = 2

    @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.

        Nothing is added to the config if the `OctaveDownConverter.channel` is not
        specified or if the `OctaveDownConverter.LO_frequency` is not specified.

        Args:
            config: A dictionary representing a QUA config file.

        Raises:
            ValueError: If the LO_frequency is not specified.
            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.
        """
        if not isinstance(self.LO_frequency, (int, float)):
            if self.channel is None:
                return
            else:
                raise ValueError(
                    f"Error generating config for Octave upconverter id={self.id}: "
                    "LO_frequency must be specified."
                )

        super().apply_to_config(config)

        if self.id in config["octaves"][self.octave.name]["RF_inputs"]:
            raise KeyError(
                f"Error generating config: "
                f'config["octaves"]["{self.octave.name}"]["RF_inputs"] '
                f'already has an entry for OctaveDownConverter with id "{self.id}"'
            )

        config["octaves"][self.octave.name]["RF_inputs"][self.id] = {
            "RF_source": "RF_in",
            "LO_frequency": self.LO_frequency,
            "LO_source": self.LO_source,
            "IF_mode_I": self.IF_mode_I,
            "IF_mode_Q": self.IF_mode_Q,
        }

        if isinstance(self.channel, InOutIQChannel):
            IF_channels = [self.IF_output_I, self.IF_output_Q]
            opx_channels = [self.channel.opx_input_I, self.channel.opx_input_Q]
        elif isinstance(self.channel, InOutSingleChannel):
            IF_channels = [self.IF_output_I]
            opx_channels = [self.channel.opx_input]
        else:
            IF_channels = []
            opx_channels = []

        opx_port_tuples = [
            p.port_tuple if isinstance(p, BasePort) else tuple(p) for p in opx_channels
        ]

        IF_config = config["octaves"][self.octave.name]["IF_outputs"]
        for k, (IF_ch, opx_port_tuples) in enumerate(
            zip(IF_channels, opx_port_tuples), start=1
        ):
            label = f"IF_out{IF_ch}"
            IF_config.setdefault(label, {"port": opx_port_tuples, "name": f"out{k}"})
            if IF_config[label]["port"] != opx_port_tuples:
                raise ValueError(
                    f"Error generating config for Octave downconverter id={self.id}: "
                    f"Unable to assign {label} to  port {opx_port_tuples} because it is already "
                    f"assigned to port {IF_config[label]['port']} "
                )

config_settings property

Specifies that the converter will be added to the config after the Octave.

apply_to_config(config)

Add information about the frequency down-converter to the QUA config

This method is called by the QuamComponent.generate_config method.

Nothing is added to the config if the OctaveDownConverter.channel is not specified or if the OctaveDownConverter.LO_frequency is not specified.

Parameters:

Name Type Description Default
config Dict

A dictionary representing a QUA config file.

required

Raises:

Type Description
ValueError

If the LO_frequency is not specified.

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.

Source code in quam/components/octave.py
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
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.

    Nothing is added to the config if the `OctaveDownConverter.channel` is not
    specified or if the `OctaveDownConverter.LO_frequency` is not specified.

    Args:
        config: A dictionary representing a QUA config file.

    Raises:
        ValueError: If the LO_frequency is not specified.
        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.
    """
    if not isinstance(self.LO_frequency, (int, float)):
        if self.channel is None:
            return
        else:
            raise ValueError(
                f"Error generating config for Octave upconverter id={self.id}: "
                "LO_frequency must be specified."
            )

    super().apply_to_config(config)

    if self.id in config["octaves"][self.octave.name]["RF_inputs"]:
        raise KeyError(
            f"Error generating config: "
            f'config["octaves"]["{self.octave.name}"]["RF_inputs"] '
            f'already has an entry for OctaveDownConverter with id "{self.id}"'
        )

    config["octaves"][self.octave.name]["RF_inputs"][self.id] = {
        "RF_source": "RF_in",
        "LO_frequency": self.LO_frequency,
        "LO_source": self.LO_source,
        "IF_mode_I": self.IF_mode_I,
        "IF_mode_Q": self.IF_mode_Q,
    }

    if isinstance(self.channel, InOutIQChannel):
        IF_channels = [self.IF_output_I, self.IF_output_Q]
        opx_channels = [self.channel.opx_input_I, self.channel.opx_input_Q]
    elif isinstance(self.channel, InOutSingleChannel):
        IF_channels = [self.IF_output_I]
        opx_channels = [self.channel.opx_input]
    else:
        IF_channels = []
        opx_channels = []

    opx_port_tuples = [
        p.port_tuple if isinstance(p, BasePort) else tuple(p) for p in opx_channels
    ]

    IF_config = config["octaves"][self.octave.name]["IF_outputs"]
    for k, (IF_ch, opx_port_tuples) in enumerate(
        zip(IF_channels, opx_port_tuples), start=1
    ):
        label = f"IF_out{IF_ch}"
        IF_config.setdefault(label, {"port": opx_port_tuples, "name": f"out{k}"})
        if IF_config[label]["port"] != opx_port_tuples:
            raise ValueError(
                f"Error generating config for Octave downconverter id={self.id}: "
                f"Unable to assign {label} to  port {opx_port_tuples} because it is already "
                f"assigned to port {IF_config[label]['port']} "
            )