Skip to content

Fixed-timeline tutorial

This tutorial builds a simple readout measurement on a FIXED_TIMELINE instrument, in three steps: deploy an instrument, program a pulse with the sequencer, and execute it. It assumes you have finished Getting started.

Deploying an instrument is easier once you know what one is — see Instruments for the concept.

The imports and constants used throughout:

import asyncio

import numpy as np
from quelware_core.entities import directives
from quelware_core.entities.instrument import (
    FixedTimelineProfile,
    InstrumentDefinition,
    InstrumentMode,
    InstrumentRole,
)

from quelware_client import create_quelware_client
from quelware_client.client.helpers.sequencer import Sequencer
from quelware_client.core.instrument_driver import (
    create_instrument_driver_fixed_timeline,
)

UNIT = "quel3-01-028"        # your unit label
PORT = f"{UNIT}:trx_p00p01"  # a transceiver port on that unit

Step 1: Deploy an instrument

An instrument is a logical device you place on a port. Open a session over the port and deploy a transceiver into it:

async def deploy(qc):
    async with qc.create_session([PORT]) as session:
        definition = InstrumentDefinition(
            alias="readout",
            mode=InstrumentMode.FIXED_TIMELINE,
            role=InstrumentRole.TRANSCEIVER_LOOPBACK,
            profile=FixedTimelineProfile(
                frequency_range_min=5.9975e9,
                frequency_range_max=6.0025e9,
            ),
        )
        inst_infos = await session.deploy_instruments(PORT, definitions=[definition])
    return inst_infos[0]

deploy_instruments() returns an InstrumentInfo describing the deployed instrument — its id and its hardware timing (config), both of which the next steps need.

Step 2: Program a pulse with the sequencer

The Sequencer builds a fixed timeline: a schedule of waveform events and capture windows, expressed in nanoseconds. Bind it to the instrument's timing, register a waveform, then place an event and a capture window:

def build_timeline(inst_info):
    alias = inst_info.definition.alias
    sampling_period_ns = inst_info.config.sampling_period_fs * 1e-6

    seq = Sequencer(default_sampling_period_ns=sampling_period_ns)
    seq.bind(
        alias,
        sampling_period_fs=inst_info.config.sampling_period_fs,
        step_samples=inst_info.config.timeline_step_samples,
    )

    n = int(200.0 / sampling_period_ns)  # a 200 ns rectangular pulse
    seq.register_waveform("pulse", np.ones(n, dtype=complex))
    seq.add_event(alias, "pulse", start_offset_ns=0.0)
    seq.add_capture_window(alias, "capture", start_offset_ns=0.0, length_ns=1000.0)
    seq.extend_length_ns(100_000.0)  # gap before the next shot
    seq.set_iterations(1000)         # average over 1000 shots
    return seq

Step 3: Execute the instrument

Create an instrument driver, apply the frequency, capture mode, and timeline, then trigger and fetch the result:

async def run(qc, inst_info):
    async with qc.create_session([inst_info.id], ttl_ms=10_000) as session:
        driver = create_instrument_driver_fixed_timeline(session, inst_info)
        seq = build_timeline(inst_info)

        await driver.initialize()
        await driver.apply(
            [
                directives.SetFrequency(hz=6.0e9),
                directives.SetCaptureMode(mode=directives.CaptureMode.AVERAGED_WAVEFORM),
                seq.export_set_fixed_timeline_directive(inst_info.definition.alias),
            ]
        )
        await session.trigger([inst_info.id])
        result = await driver.fetch_result()

    return result.iq_waveform_result["capture"][0].iq_array

Putting it together

async def main():
    qc = create_quelware_client("192.0.2.1", 50051)  # your server address
    async with qc:
        inst_info = await deploy(qc)
        iq = await run(qc, inst_info)
    print(f"captured {len(iq)} samples")


asyncio.run(main())

Next steps