Continuous Resonator Spectroscopy

This tutorial is applicable to all SHFQC Instruments.

Goals and Requirements

The goal of this tutorial is to demonstrate how to use the SHFQC to perform continuous resonator spectroscopy measurement using the Quantum Analyzer readout channel and Zurich Instrument Toolkit API.

Users can download all zhinst-toolkit example files introduced in this tutorial from GitHub, It is possible that some tutorials are written for the SHFQA or SHFSG. This is not a concern as they can be run also on the SHFQC.


The tutorial starts with the Instrument in the default configuration (e.g., after a power cycle). For an optimal tutorial experience, please follow these preparation steps:

  • ensure that the version of the LabOne Python API, LabOne and the Firmware of the SHFQC, zhinst-toolkit (pip install zhinst-toolkit) and Python (3.7 or newer) are updated and compatible,

  • make sure that the Instrument is powered on and connected by Ethernet to your local area network (LAN) where the host computer resides or by USB (Maintenance port) to the host computer,

  • start LabOne and open the LabOne Graphical User Interface using the default web browser,

  • connect the SHFQC Quantum Analyzer channel output (input) to the readout input (output) line, see SHFQC connection.

shfqc tutorial loopback
Figure 1. SHFQC connection.


In specific, we demonstrate how to run a frequency sweep, obtain the transmission data and plot it. For this, the Sweeper is configured to enable a continuous output signal that first probes the device under test and is then correlated with the generated signal. As a result, we obtain the amplitude and phase response in transmission of our device under test.

Users can use the Python code below to connect the instrument, perform continuous wave resonator spectroscopy, and each step is explained as the following.

  1. Connect the Instrument

    Create a toolkit session to the data server and connect the Instrument with the device ID, e.g. 'DEV12001', see Using the Python API.

    # Load the LabOne API and other necessary packages
    from zhinst.toolkit import Session
    SERVER_HOST = 'localhost'
    session = Session(SERVER_HOST)              ## connect to data server
    device = session.connect_device(DEVICE_ID)  ## connect to device
  2. Create a Sweeper and configure it

    Python API SHFSweeper class is the core of the spectroscopy measurements. It defines all relevant parameters for frequency sweeping and sequencing. Toolkit wraps around the SHFSweeper and exposes an interface that is similar to the LabOne modules, meaning the parameters are exposed in a node tree like structure.

The data class RfConfig includes the channel-specific settings, such as the center frequency in units of Hz, and the power ranges of the Input and Output channel.

The data class SweepConfig allows to configure the sweeps start and stop frequency as the in-band offset frequency. In addition, it contains the number of measured points in the sweep 'num_points', and the mapping of the points ('linear' or 'logarithmic'). The gain of the oscillator 'oscillator_gain' changes the output amplitude of the probe signal relative to the channels power ranges. Hence, the output power of the probe signal hence changes quadratically with this number.

Please note that in this example, the configuration option use_sequencer is not explicitly set to its default value True. In this mode, a SeqC program is automatically generated, uploaded and compiled in the SHFQC Sequencer, and the frequency of the digital oscillator is controlled by the Sequencer. This mode allows a fast resonator spectroscopy with estimated running time of \(\leq (t_{\mathrm{int}} + 200 ns)n_{\mathrm{averages}}n_{\mathrm{points}}\), where \(n_{\mathrm{averages}}\) is the number of averages, and \(n_{\mathrm{points}}\) is the number of sweep points. If set_sequencer is False, the sweeper is controlled by the host computer, and the frequency sweep is slower.

The data class AvgConfig contains all settings related to averaging and integration. The 'integration_time' defines how long the signal is integrated in the qubit_measurement_unit, which can be up to ~16.7 ms. The mode 'sequential' or 'cyclic' define, whether each point is first averaged 'num_averages' times before the frequency is changed, or whether every sweep is averaged 'num_averages' times.


sweeper = session.modules.shfqa_sweeper
CHANNEL_INDEX = 0 # physical Channel 1

sweeper.sweep.start_freq(-1000e6) # in units of Hz
sweeper.sweep.stop_freq(1000e6) # in units of Hz
sweeper.sweep.oscillator_gain(0.8) # amplitude scaling factor, 0 to 1
sweeper.sweep.use_sequencer = True # True (recommended): sequencer-based sweep; False: host-driven sweep

sweeper.average.integration_time(100e-6) # in units of second
sweeper.average.mode("sequential") # "sequential" or "cyclic" averaging
sweeper.rf.center_freq(5e9) # in units of Hz
sweeper.rf.input_range(0) # in units of dBm
sweeper.rf.output_range(0) # in units of dBm

with device.set_transaction():
  1. Run the measurement and plot the data

    After executing, all above parameters are updated, and a SeqC program is automatically generated, uploaded and compiled based on the sweep parameters, see in Figure 2. In the program, ConfigFreqSweep sets the start frequency and the frequency increment in units of Hz for a chosen oscillator, and setSweepStep sets the oscillator frequency. The oscillator phase is reset by resetOscPhase before each measurement. The trigger generated in the sequencer is used to start the integration, and the playZero sets the cycle duration. The measurement is repeated using the nested for loop according to the averaging mode. The measurement starts after enabling the sequencer.

    The result returned from is complex data \(E_{\mathrm{sweeper}}\) in units of Vrms. It is averaged and normalized by the integration length. The power \(P\) and phase \(\phi\) can be calculated as,

    \[\begin{equation}\tag{1} \begin{aligned} P & = 10\lg(\frac{|E_{\mathrm{sweeper}}|^2}{R}1000),\\ \phi & = \arctan\frac{\Im(E_{\mathrm{sweeper}})}{\Re(E_{\mathrm{sweeper}})}, \end{aligned} \end{equation}\]

    where \(R\) is 50 \(\Omega\), 1000 is the conversion factor from W to mW. With sweeper.plot(), the power and phase are calculated and plotted, see Figure 3.

    result =
    num_points_result = len(result["vector"])
    print(f"Measured at {num_points_result} frequency points.")
    tutorial cw spectroscopy seqc
    Figure 2. Continuous spectroscopy measurement seqc program in the Readout Pulse Generator Tab.
    tutorial cw spectroscopy
    Figure 3. Continuous spectroscopy measurement results in loopback configuration.