Skip to content

Support native 2 GS/s sampling on OPX1000 LF-FEM without interpolation#333

Open
Copilot wants to merge 3 commits intomainfrom
copilot/update-bakery-interpolation-logic
Open

Support native 2 GS/s sampling on OPX1000 LF-FEM without interpolation#333
Copilot wants to merge 3 commits intomainfrom
copilot/update-bakery-interpolation-logic

Conversation

Copy link
Copy Markdown

Copilot AI commented Feb 24, 2026

OPX1000 LF-FEM ports configured at 2 GS/s were being unnecessarily interpolated down to 1 GS/s during baking. This fix skips interpolation when the baking sampling_rate matches the hardware port's native rate.

Changes

Core logic:

  • _get_port_sampling_rate(qe): Lookup hardware sampling rate from config (supports FEM 3-tuple and OPX+ 2-tuple ports)
  • _needs_interpolation(qe): Returns true only when sampling_rate > 1e9 AND port doesn't natively support that rate
  • __exit__: Skip interpolation when _needs_interpolation(qe) is false
  • _get_samples: Check _needs_interpolation(qe) before upsampling existing pulses
  • _update_config: Write sampling_rate tag when != 1e9 AND interpolation was not performed

Behavior matrix:

  • Baking at 2 GS/s on 2 GS/s port → pass-through, write sampling_rate: 2e9
  • Baking at 2 GS/s on 1 GS/s port → interpolate to 1 GS/s, no tag (default)
  • Baking at < 1 GS/s → pass-through, write tag (OPX real-time upsampling)

Example:

config = {
    "controllers": {
        "con1": {
            "fems": {
                3: {
                    "analog_outputs": {
                        8: {"sampling_rate": 2e9, "output_mode": "direct"},
                    }
                }
            }
        }
    },
    "elements": {
        "qe": {"singleInput": {"port": ("con1", 3, 8)}, ...}
    }
}

# Samples played natively at 2 GS/s without interpolation loss
with baking(config, sampling_rate=2e9) as b:
    b.add_op("pulse", "qe", [0.1*i for i in range(40)])  # 40 samples = 20 ns @ 2 GS/s
    b.play("pulse", "qe")

Remember to:

  • Update the CHANGELOG.md
  • Added tests for the feature or fix
Original prompt

Problem

The bakery tool in qualang_tools/bakery/bakery.py currently treats any sampling_rate > 1e9 as requiring cubic interpolation back down to 1 GS/s. However, the OPX1000 LF-FEM hardware natively supports 2 GS/s on certain output ports (configured via "sampling_rate": 2e9 in the controller's analog_outputs). When a user sets sampling_rate=2e9 in the baking context manager and the element's output port is also configured at 2 GS/s, the interpolation is unnecessary and lossy — the samples should be passed through as-is, with the sampling_rate tag written into the waveform config so the OPX plays them at the correct rate.

Current behavior

When sampling_rate > 1e9:

  1. In __exit__: cubic interpolation is performed to resample waveforms to 1 GS/s
  2. In _get_samples: cubic interpolation is performed when retrieving existing pulse samples
  3. In _update_config: sampling_rate is only written to waveform config when < 1e9

Desired behavior

When the element's output port is configured at a sampling rate that matches the baking's sampling_rate (specifically 2e9, the only natively-supported rate above 1e9), the bakery should:

  1. Skip interpolation in __exit__ — pass samples through unchanged
  2. Skip interpolation in _get_samples — return samples as-is
  3. Write sampling_rate to the waveform config in _update_config — so the OPX knows to play at that rate

Implementation approach

Add a helper method _get_port_sampling_rate(qe) to look up the hardware sampling rate of an element's output port from the config. The config structure varies:

  • OPX1000 (FEM-based): Port is a 3-tuple (controller, fem, port_number), config path is controllers[con]["fems"][fem]["analog_outputs"][port_number]["sampling_rate"]
  • OPX+ (flat): Port is a 2-tuple (controller, port_number), config path is controllers[con]["analog_outputs"][port_number]["sampling_rate"]

The method should handle:

  • singleInput elements: look up element["singleInput"]["port"]
  • mixInputs elements: look up element["mixInputs"]["I"] (or "Q" — they should match)
  • Missing sampling_rate key in port config defaults to 1e9
  • Graceful fallback to 1e9 if anything is missing or malformed

Then use a property like _needs_interpolation(qe) that returns True when:

  • self.sampling_rate > 1e9 AND
  • self.sampling_rate != port_sampling_rate (i.e., hardware doesn't natively support the baking rate)

Modify the three locations:

  1. __exit__: Change if self.sampling_rate > int(1e9): to if self._needs_interpolation(qe):
  2. _get_samples: Change if self.sampling_rate > int(1e9) and pulse in self._config["pulses"]: to additionally check _needs_interpolation (needs the element context, so look up the element from the pulse)
  3. _update_config: Change if self.sampling_rate < int(1e9): to if self.sampling_rate != int(1e9): (which covers both < 1e9 for OPX real-time interpolation AND > 1e9 for hardware-native rates like 2 GS/s)

Also update the docstring for the sampling_rate parameter in the baking() function to document this new behavior.

Important notes

  • RF_inputs and MWInput element types should also be handled (they use different port structures, but the same principle applies)
  • For RF_inputs / MWInput, there may not be a straightforward per-port sampling rate lookup — default to 1e9 if not found
  • The existing tests should continue to pass since the default sampling rate is 1e9
  • If the port sampling rate cannot be determined, fall back to the existing interpolation behavior (safe default)

Reference: Example OPX1000 config showing 2 GS/s port

From examples/Qcodes_drivers/basic-driver/hello_qcodes_opx1000.py:

"controllers": {
    "con1": {
        "type": "opx1000",
        "fems": {
            fem: {
                "type": "LF",
                "analog_outputs": {
                    8: {"offset": 0.0, "sampling_rate": 2e9, "output_mode": "direct", "delay": 0},
                },
            },
        },
    },
},
"elements": {
    "readout_element": {
        "singleInput": {
            "port": ("con1", fem, 8),
        },
        ...
    },
}

This pull request was created from Copilot chat.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Co-authored-by: yomach <53370976+yomach@users.noreply.github.com>
Copilot AI changed the title [WIP] Update bakery tool to handle 2 GS/s without interpolation Support native 2 GS/s on OPX1000 LF-FEM without interpolation Feb 24, 2026
Copilot AI requested a review from yomach February 24, 2026 07:04
@yomach yomach marked this pull request as ready for review February 24, 2026 07:41
@github-actions
Copy link
Copy Markdown

github-actions bot commented Feb 24, 2026

Unit Test Results

0 tests   0 ✔️  0s ⏱️
0 suites  0 💤
0 files    0

Results for commit 8c452d8.

♻️ This comment has been updated with latest results.

Co-authored-by: yomach <53370976+yomach@users.noreply.github.com>
Copilot AI changed the title Support native 2 GS/s on OPX1000 LF-FEM without interpolation Support native 2 GS/s sampling on OPX1000 LF-FEM without interpolation Feb 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants