See also
A Jupyter notebook version of this tutorial can be downloaded here.

Time of Flight Measurement#
Tutorial Objective#
In this tutorial, we aim to measure the time of flight (in other words, the propagation delay between the output and the input of the control system). Knowing the time of flight is essential for synchronizing control & readout pulses with the quantum system’s response, ensuring correct timing and phase alignment in experiments. Thus, the analysis of the data obtained from this application example will be useful to calibrate the acquisition delay for subsequent experiments, improving the quality of the digitized signal.

Imports#
[1]:
from dependencies.analysis_utils import TimeOfFlightAnalysis
from dependencies.simulated_data import get_tof_data
from qblox_scheduler import HardwareAgent, Schedule
from qblox_scheduler.backends.qblox import constants
from qblox_scheduler.math import closest_number_ceil
from qblox_scheduler.operations import Measure, SetClockFrequency
from qblox_scheduler.operations.expressions import DType
from qblox_scheduler.operations.loop_domains import arange
/.venv/lib/python3.14/site-packages/quantify_core/utilities/general.py:13: QCoDeSDeprecationWarning: The `qcodes.utils.helpers` module is deprecated. Please consult the api documentation at https://microsoft.github.io/Qcodes/api/index.html for alternatives.
from qcodes.utils.helpers import NumpyJSONEncoder
Hardware/Device Configuration Files#
We use configuration files in order to describe the hardware properties (e.g. cluster ip, connected modules, output ports) and quantum device properties (charge sensors, qubits, barriers and their associated properties) in one accessible location, respectively. Check the Getting Started Guide for further information.
Based on the information specified in these files, we establish the hardware and device configurations that determine the system’s connectivity.
[2]:
hw_config_path = "dependencies/configs/tuning_spin_coupled_pair_hardware_config.json"
device_path = "dependencies/configs/spin_with_psb_device_config_2q.yaml"
Experimental Setup#
In order to run this application example, you will need a quantum device that consists of a double quantum dot array (q0 and q1), with a charge sensor (cs0) connected to reflectometry readout. The DC voltages of the quantum device also need to be properly tuned. For example, reservoir gates need to be ramped up for the accumulation devices. The charge sensor can be a quantum dot, quantum point contact (QPC), or single electron transistor (SET).
Hardware Setup#
The HardwareAgent() is the main object for Qblox experiments. It provides an interface to define the quantum device, set up hardware connectivity, run experiments, and receive results. For more information about the HardwareAgent() you can check the Core concepts of qblox-scheduler page.
[3]:
hw_agent = HardwareAgent(hw_config_path, device_path)
hw_agent.connect_clusters()
# Device name string should be defined as specified in the hardware configuration file
sensor_0 = hw_agent.quantum_device.get_element("cs0")
cluster = hw_agent.get_clusters()["cluster0"]
hw_opts = hw_agent.hardware_configuration.hardware_options
/.venv/lib/python3.14/site-packages/qblox_scheduler/qblox/hardware_agent.py:499: UserWarning: cluster0: Trying to instantiate cluster with ip 'None'.Creating a dummy cluster.
warnings.warn(
As can be observed from the defined quantum devices and the hardware connectivies of these elements, the relevant modules and connections for this tutorial are:
QRM (Module 4):
\(\text{O}^{1}\) and \(\text{I}^{1}\): Charge sensor (
cs0) resonator probe and readout connection.
Time of Flight - Schedule & Measurement#
Prior to the start of quantum experiments, it is crucial to calibrate the delay between sending a measurement pulse and the beginning of the acquisition on the instrument. This delay should be equal to the time it takes for the readout signal to travel through the fridge and is determined by the length of the cables, as well as other delay inducing components. In order to perform the experiment, a square pulse is played over the output of the readout line, and a trace acquisition is done. The time at which the pulse is detected on the acquisition path is determined as the acquisition delay. This also allows us to set the NCO propagation delay, which corrects for the phase accumulated during the time of flight.
We define all variables for the experiment schedule in one location for ease of access and modification.
[4]:
sensor = sensor_0
repetitions = 500
[5]:
# Define a schedule and set the repetition rate
tof_schedule = Schedule("Trace measurement schedule")
# Set the clock frequency of the sensor readout port
with tof_schedule.loop(arange(0, repetitions, 1, DType.NUMBER)):
tof_schedule.add(
SetClockFrequency(
clock=sensor.name + ".ro", clock_freq_new=sensor.clock_freqs.readout - 100e6
) # We detune clock 100MHz from the expected resonance frequency, at which the signal will be partially absorbed.
)
# Acquire a trace of the signal acquired at the input port
tof_schedule.add(Measure(sensor.name, acq_protocol="Trace"))
/tmp/ipykernel_7625/1129173696.py:6: FutureWarning: clock_freq_new is deprecated as an argument to SetClockFrequency and will be removed in qblox-scheduler >= 2.0; use frequency instead.
SetClockFrequency(
Now, we will run the schedule. See the documentation on the QRM Module and Readout Sequencers for information on how the signal is processed upon acquisition.
[6]:
hw_opts.output_att.update({f"{sensor.name}.ro": 0}) # set attenuation to zero to have max signal
tof_data = hw_agent.run(tof_schedule)
tof_data
[6]:
<xarray.Dataset> Size: 8kB
Dimensions: (acq_index_0: 1, trace_index_0: 350)
Coordinates:
* acq_index_0 (acq_index_0) int64 8B 0
* trace_index_0 (trace_index_0) int64 3kB 0 1 2 3 4 5 ... 345 346 347 348 349
Data variables:
0 (acq_index_0, trace_index_0) complex128 6kB (nan+nanj) ......
Attributes:
tuid: 20260415-120936-349-3fa431Analysis#
[7]:
# Add simulated data in case a dummy cluster is being used. This is done in order to successfully showcase the expected results of an analysis in all cases.
if cluster.is_dummy:
tof_data[0].data = get_tof_data(sensor.measure.integration_time)[0].reshape(
1, len(tof_data[0].data[0])
)
tof_analysis = TimeOfFlightAnalysis(
dataset=tof_data,
)
tof_analysis.run().display_figs_mpl()
The analysis yields the time of flight in the system: The time difference between the beginning of the acquisition and the actual incidence of the acquired signal on the acquisition port. This result should be considered when the acquisition delay parameter is configured for subsequent experiments in order to avoid integrating over a portion of the noise floor signal before the actual signal from the experiment is acquired. Thus, we set the acquisition delay of the sensor according to the information we obtain from data analysis.
[8]:
fit_results = tof_analysis.quantities_of_interest
nco_prop_delay = fit_results["nco_prop_delay"]
measured_tof = fit_results["tof"]
sensor.measure.acq_delay = (
closest_number_ceil(
measured_tof * constants.SAMPLING_RATE, constants.MIN_TIME_BETWEEN_OPERATIONS
)
/ constants.SAMPLING_RATE
)
The quantum device settings can be saved after every experiment for allowing later reference into experiment settings.
[9]:
hw_agent.quantum_device.to_json_file("./dependencies/configs", add_timestamp=True)
[9]:
'./dependencies/configs/spin_with_psb_device_config_2026-04-15_12-09-37_UTC.json'