Pulsed Resonator Spectroscopy
This tutorial is applicable to all SHFQC Instruments. |
Goals and Requirements
The goal of this tutorial is to demonstrate how to use SHFQC to perform pulsed spectroscopy measurement with a customized pulse envelope using Zurich Instruments Toolkit API.
To run the tutorial, 1 SHFQC and loopback connection are required.
Users can download all zhinst-toolkit example files introduced in this tutorial from GitHub, https://github.com/zhinst/zhinst-toolkit/tree/main/examples. 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. |
Preparation
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 device, 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.
Tutorial
The tutorial starts with configuring sweep parameters is followed by generating and uploading pulse envelope and run the measurement, is finished with verification of the spectroscopy pulse. `` Users can use the Python code below to perform pulsed spectroscopy measurement, and each step is explained as the following.
-
Connect the instrument
Create a toolkit session to the data server and connect the device 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 from scipy.signal import gaussian import numpy as np DEVICE_ID = 'DEVXXXXX' SERVER_HOST = 'localhost' session = Session(SERVER_HOST) ## connect to data server device = session.connect_device(DEVICE_ID) ## connect to device
-
Create a Sweeper and configure it
Create a Sweeper and configure sweep parameters, see tutorial Continuous Resonator Spectroscopy.
sweeper = session.modules.shfqa_sweeper sweeper.device(device) 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.num_points(1001) 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(1e-6) # in units of second sweeper.average.num_averages(200) sweeper.average.mode("sequential") # "sequential" or "cyclic" sweeper.rf.channel(CHANNEL_INDEX) sweeper.rf.center_freq(7e9) # 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(): device.qachannels[CHANNEL_INDEX].input.on(1) device.qachannels[CHANNEL_INDEX].output.on(1)
-
Generate and upload pulse envelope
Create a complex flat-top Gaussian envelope with 1 μs duration and 50 ns rise and fall time. Enable the pulsed mode by sweeper.envelope.enable(True) and upload the envelope to the waveform memory. This envelope can be displayed on the Waveform Viewer of the Readout Pulse Generator.
SAMPLING_FREQUENCY = 2e9 # in units of Hz ENVELOPE_DURATION = 1.0e-6 # in units of second ENVELOPE_RISE_FALL_TIME = 0.05e-6 # in units of second rise_fall_len = int(ENVELOPE_RISE_FALL_TIME * SAMPLING_FREQUENCY) std_dev = rise_fall_len // 10 gauss = gaussian(2 * rise_fall_len, std_dev) complex_amplitude = (1 - 1j)/np.sqrt(2) flat_top_gaussian = np.ones(int(ENVELOPE_DURATION * SAMPLING_FREQUENCY)) * complex_amplitude flat_top_gaussian[0:rise_fall_len] = gauss[0:rise_fall_len] * complex_amplitude flat_top_gaussian[-rise_fall_len:] = gauss[-rise_fall_len:] * complex_amplitude sweeper.average.integration_delay(220e-9) # in units of second sweeper.envelope.enable(True) # True: Pulsed mode; False: Continuous mode sweeper.envelope.waveform(flat_top_gaussian) # upload envelope waveform
Please note that default values of Sweeper parameters depend on the zhinst version, see tutorial Continuous Resonator Spectroscopy. Descriptions of other parameters can be found using the following code.
list(sweeper)
-
Run the measurement and plot the data
After executing sweeper.run(), all above parameters are updated, a SeqC program is automatically generated, uploaded and compiled based on the sweep parameters, see Continuous Resonator Spectroscopy, and the result is downloaded after the measurement is done. The power and phase are calculated (see Continuous Resonator Spectroscopy.) and plotted, see in Figure 3.
result = sweeper.run() num_points_result = len(result["vector"]) print(f"Measured at {num_points_result} frequency points.") sweeper.plot()
-
Verify the spectroscopy pulse with the Scope
To achieve the best SNR, integration should start when the pulse reaches the integration units. Here, the scope is used to verify the spectroscopy pulse and measure the integration delay.
The Scope is configured in a single mode triggered by the Sequencer 1 Trigger Output without any trigger delay, the recorded channel is physical Input Channel 1, and the recording duration is 1.5 μs. The trigger generated from the Sequencer 1 is also used to trigger Spectroscopy measurement to send the spectroscopy pulse. The pulse recorded by the Scope is shown in Figure 2. The measured phase of the input signal depends on the delay between signal generation at the Readout Pulse Generator and signal arrival at the Integration Units. The integration delay can be simply obtained by checking the delay of the pulse from the Scope. In the loopback mode the delay is about 220 ns.
SCOPE_CHANNEL = 0 RECORD_DURATION = 1.5e-6 with device.set_transaction(): device.scopes[0].trigger.enable(1) device.scopes[0].trigger.channel(32 + CHANNEL_INDEX) # Sequencer 1 Trigger Output device.scopes[0].trigger.delay(0) # in units of second device.scopes[0].length(int(RECORD_DURATION * SAMPLING_FREQUENCY/16)*16) device.scopes[0].channels[SCOPE_CHANNEL].inputselect(CHANNEL_INDEX) # physical Channel 1 device.scopes[0].channels[SCOPE_CHANNEL].enable(1) device.qachannels[CHANNEL_INDEX].oscs[0].freq(0) # set oscillator frequency to 0 Hz device.qachannels[CHANNEL_INDEX].spectroscopy.trigger.channel(32 + CHANNEL_INDEX) # Sequencer 1 Trigger Output seqc_program = """\ repeat(1) { resetOscPhase(); // reset the digital oscillator phase of Channel 1 setTrigger(1); setTrigger(0); // send out Sequencer 1 Trigger } """ device.scopes[0].run(single = True) # run the scope device.qachannels[CHANNEL_INDEX].generator.load_sequencer_program(seqc_program) # load SeqC program to the Sequencer device.qachannels[CHANNEL_INDEX].generator.single(1) # using single mode device.qachannels[CHANNEL_INDEX].generator.enable(1) # run the Sequencer scope_data, *_ = device.scopes[0].read() # readout the data from the Scope
Figure 2. Pulse envelope recorded by the ScopeFigure 3. Pulsed spectroscopy measurement results in loopback configuration.