Synchronization
In this tutorial we will demonstrate how to synchronize two Qblox instruments using the SYNQ technology (see section Synchronization). For this tutorial we will use one Pulsar QCM and one Pulsar QRM and we will be acquiring waveforms sequenced by the Pulsar QCM using the Pulsar QRM. By synchronizing the two instruments using the SYNQ technology, timing the acquisition of the waveforms becomes trivial.
For this tutorial to work, we need to connect both instruments to the same network, connect the \(\text{REF}^{out}\) of the Pulsar QCM to the \(\text{REF}^{in}\) of the Pulsar QRM using a 50cm coaxial cable, connect their SYNQ ports using the SYNQ cable and finally connect \(\text{O}^{[1-2]}\) of the Pulsar QCM to \(\text{I}^{[1-2]}\) of the Pulsar QRM respectively.
Setup
First, we are going to import the required packages and connect to the instruments.
[1]:
#Set up the environment.
import os
import scipy.signal
import math
import json
import matplotlib.pyplot
import numpy
from qblox_instruments import Pulsar
#Close any existing connections to any Pulsar module
Pulsar.close_all()
#Connect to the Pulsar QCM at default IP address.
pulsar_qcm = Pulsar("qcm", "192.168.0.2")
#Connect to the Pulsar QRM at alternate IP address.
pulsar_qrm = Pulsar("qrm", "192.168.0.3")
#Reset the Pulsar QCM for good measure.
pulsar_qcm.reset()
print("QCM status:")
print(pulsar_qcm.get_system_state())
print()
#Reset the Pulsar QRM for good measure.
pulsar_qrm.reset()
print("QRM status:")
print(pulsar_qrm.get_system_state())
QCM status:
SystemState(status=<SystemStatus.OKAY: 2>, flags=[])
QRM status:
SystemState(status=<SystemStatus.OKAY: 2>, flags=[])
We also need to configure the reference clock sources of the instruments. The Pulsar QCM is used as the overal reference source and needs to be configured to use its internal reference clock (the default setting). The Pulsar QRM will use the Pulsar QCM’s reference clock and needs to be configured to use the external reference clock source.
[2]:
#Set reference clock source.
pulsar_qrm.reference_source("external")
Generate waveforms
Next, we need to create the waveforms for the sequence.
[3]:
#Waveform parameters
waveform_length = 120 #nanoseconds
#Waveform dictionary (data will hold the samples and index will be used to select the waveforms in the instrument).
waveforms = {
"gaussian": {"data": scipy.signal.gaussian(waveform_length, std=0.12 * waveform_length).tolist(), "index": 0},
"sine": {"data": [math.sin((2*math.pi/waveform_length)*i) for i in range(0, waveform_length)], "index": 1}
}
Let’s plot the waveforms to see what we have created.
[4]:
time = numpy.arange(0, max(map(lambda d: len(d["data"]), waveforms.values())), 1)
fig, ax = matplotlib.pyplot.subplots(1,1, figsize=(10, 10/1.61))
for wf, d in waveforms.items():
ax.plot(time[:len(d["data"])], d["data"], ".-", linewidth=0.5, label=wf)
ax.legend(loc=4)
ax.yaxis.grid()
ax.xaxis.grid()
ax.set_ylabel("Waveform primitive amplitude")
ax.set_xlabel("Time (ns)")
matplotlib.pyplot.draw()
matplotlib.pyplot.show()
Specify acquisitions
We also need to specify the acquisitions so that the instrument can allocate the required memory for it’s acquisition list. In this case we will create one acquisition specification that creates a single bin. However, we will not be using the bin in this turorial.
[5]:
#Acquisitions
acquisitions = {"measurement": {"num_bins": 1, "index": 0}}
Create Q1ASM programs
Now that we have the waveforms and acquisition specifications for the sequence, we need a simple Q1ASM program that sequences the waveforms in the Pulsar QCM and acquires the waveforms in the Pulsar QRM.
[6]:
#Pulsar QCM sequence program.
qcm_seq_prog = """
wait_sync 4 #Synchronize sequencers over multiple instruments.
play 0,1,16384 #Play waveforms and wait remaining duration of scope acquisition.
stop #Stop.
"""
#Pulsar QRM sequence program.
qrm_seq_prog = """
wait_sync 4 #Synchronize sequencers over multiple instruments.
acquire 0,0,16384 #Acquire waveforms and wait remaining duration of scope acquisition.
stop #Stop.
"""
Upload sequences
Now that we have the waveforms and Q1ASM programs, we can combine them in the sequences stored in JSON files.
[7]:
#Add QCM sequence to single dictionary and write to JSON file.
sequence = {"waveforms": waveforms, "weights": {}, "acquisitions": acquisitions, "program": qcm_seq_prog}
with open("qcm_sequence.json", 'w', encoding='utf-8') as file:
json.dump(sequence, file, indent=4)
file.close()
#Add QRM sequence to single dictionary and write to JSON file.
sequence = {"waveforms": waveforms, "weights": {}, "acquisitions": acquisitions, "program": qrm_seq_prog}
with open("qrm_sequence.json", 'w', encoding='utf-8') as file:
json.dump(sequence, file, indent=4)
file.close()
Let’s write the JSON file to the instruments. We will use sequencer 0 of both the Pulsar QCM and Pulsar QCM, which will drive outputs \(\text{O}^{[1-2]}\) of the Pulsar QCM and acquire on inputs \(\text{I}^{[1-2]}\) of the Pulsar QRM.
[8]:
#Upload waveforms and programs to Pulsar QCM.
pulsar_qcm.sequencer0.sequence("qcm_sequence.json")
#Upload waveforms and programs to Pulsar QRM.
pulsar_qrm.sequencer0.sequence("qrm_sequence.json")
Play sequences
The sequence has been uploaded to the instruments. Now we need to configure the sequencers of both the Pulsar QCM and Pulsar QRM to use the wait_sync
instruction to synchronize and we need to configure the sequencer of the Pulsar QRM to trigger the acquisition with the acquire
instruction. Furthermore we also need to attenuate the Pulsar QCM’s outputs to 40% to be able to capture the full range of the waveforms on the Pulsar QRM’s inputs.
\(\text{Attenuation}={Input}/{Output}={2V}/{5V}={0.4}\)
[9]:
#Configure the sequencer of the Pulsar QCM.
pulsar_qcm.sequencer0.sync_en(True)
pulsar_qcm.sequencer0.gain_awg_path0(0.35) #Adding a bit of margin to the 0.4
pulsar_qcm.sequencer0.gain_awg_path1(0.35)
#Map sequencer of the Pulsar QCM to specific outputs (but first disable all sequencer connections)
for sequencer in pulsar_qcm.sequencers:
for out in range(0, 2):
sequencer.set("channel_map_path{}_out{}_en".format(out%2, out), False)
pulsar_qcm.sequencer0.channel_map_path0_out0_en(True)
pulsar_qcm.sequencer0.channel_map_path1_out1_en(True)
#Configure the scope acquisition of the Pulsar QRM.
pulsar_qrm.scope_acq_sequencer_select(0)
pulsar_qrm.scope_acq_trigger_mode_path0("sequencer")
pulsar_qrm.scope_acq_trigger_mode_path1("sequencer")
#Configure the sequencer of the Pulsar QRM.
pulsar_qrm.sequencer0.sync_en(True)
Now let’s start the sequences.
[10]:
#Arm and start sequencer of the Pulsar QCM (only sequencer 0).
pulsar_qcm.arm_sequencer(0)
pulsar_qcm.start_sequencer()
#Print status of sequencer of the Pulsar QCM.
print("QCM status:")
print(pulsar_qcm.get_sequencer_state(0))
print()
#Arm and start sequencer of the Pulsar QRM (only sequencer 0).
pulsar_qrm.arm_sequencer(0)
pulsar_qrm.start_sequencer()
#Print status of sequencer of the Pulsar QRM.
print("QRM status:")
print(pulsar_qrm.get_sequencer_state(0))
QCM status:
SequencerState(status=<SequencerStatus.Q1_STOPPED: 4>, flags=[])
QRM status:
SequencerState(status=<SequencerStatus.STOPPED: 5>, flags=[<SequencerStatusFlags.ACQ_SCOPE_DONE_PATH_0: 10>, <SequencerStatusFlags.ACQ_SCOPE_DONE_PATH_1: 13>, <SequencerStatusFlags.ACQ_BINNING_DONE: 16>])
Retrieve acquisition
The waveforms have now been sequenced on the outputs and acquired on the inputs by both instruments. And as you might have noticed, timing these operations was simplified significantly by the SYNQ technology. Lets retrieve the resulting data, but first let’s make sure the sequencers have finished.
[11]:
#Wait for the QCM sequencer to stop with a timeout period of one minute.
pulsar_qcm.get_sequencer_state(0, 1)
#Wait for the QRM acquisition to finish with a timeout period of one minute.
pulsar_qrm.get_acquisition_state(0, 1)
#Move acquisition data from temporary memory to acquisition list.
pulsar_qrm.store_scope_acquisition(0, "measurement")
#Get acquisition list from instrument.
acq = pulsar_qrm.get_acquisitions(0)
Let’s plot the result.
[12]:
#Plot acquired signal on both inputs.
fig, ax = matplotlib.pyplot.subplots(1, 1, figsize=(15, 15/2/1.61))
ax.plot(acq["measurement"]["acquisition"]["scope"]["path0"]["data"][130:290])
ax.plot(acq["measurement"]["acquisition"]["scope"]["path1"]["data"][130:290])
ax.set_xlabel('Time (ns)')
ax.set_ylabel('Relative amplitude')
matplotlib.pyplot.show()
Stop
Finally, let’s stop the sequencers if they haven’t already and close the instrument connections.
[13]:
#Stop sequencers.
pulsar_qcm.stop_sequencer()
pulsar_qrm.stop_sequencer()
#Print status of sequencers.
print("QCM status:")
print(pulsar_qcm.get_sequencer_state(0))
print()
print("QRM status:")
print(pulsar_qrm.get_sequencer_state(0))
print()
#Print an overview of instrument parameters.
print("QCM snapshot:")
pulsar_qcm.print_readable_snapshot(update=True)
print()
print("QRM snapshot:")
pulsar_qrm.print_readable_snapshot(update=True)
#Close the instrument connections.
pulsar_qcm.close()
pulsar_qrm.close()
QCM status:
SequencerState(status=<SequencerStatus.STOPPED: 5>, flags=[<SequencerStatusFlags.FORCED_STOP: 2>])
QRM status:
SequencerState(status=<SequencerStatus.STOPPED: 5>, flags=[<SequencerStatusFlags.FORCED_STOP: 2>, <SequencerStatusFlags.ACQ_SCOPE_DONE_PATH_0: 10>, <SequencerStatusFlags.ACQ_SCOPE_DONE_PATH_1: 13>, <SequencerStatusFlags.ACQ_BINNING_DONE: 16>])
QCM snapshot:
qcm:
parameter value
--------------------------------------------------------------------------------
IDN : {'manufacturer': 'qblox', 'model': 'pulsar_qcm', 'serial_n...
out0_offset : 0 (V)
out1_offset : 0 (V)
out2_offset : 0 (V)
out3_offset : 0 (V)
reference_source : internal
qcm_sequencer0:
parameter value
--------------------------------------------------------------------------------
channel_map_path0_out0_en : True
channel_map_path0_out2_en : True
channel_map_path1_out1_en : True
channel_map_path1_out3_en : True
cont_mode_en_awg_path0 : False
cont_mode_en_awg_path1 : False
cont_mode_waveform_idx_awg_path0 : 0
cont_mode_waveform_idx_awg_path1 : 0
gain_awg_path0 : 0.34999
gain_awg_path1 : 0.34999
marker_ovr_en : False
marker_ovr_value : 0
mixer_corr_gain_ratio : 1
mixer_corr_phase_offset_degree : -0
mod_en_awg : False
nco_freq : 0 (Hz)
nco_phase_offs : 0 (Degrees)
offset_awg_path0 : 0
offset_awg_path1 : 0
sequence : qcm_sequence.json
sync_en : True
upsample_rate_awg_path0 : 0
upsample_rate_awg_path1 : 0
qcm_sequencer1:
parameter value
--------------------------------------------------------------------------------
channel_map_path0_out0_en : False
channel_map_path0_out2_en : True
channel_map_path1_out1_en : False
channel_map_path1_out3_en : True
cont_mode_en_awg_path0 : False
cont_mode_en_awg_path1 : False
cont_mode_waveform_idx_awg_path0 : 0
cont_mode_waveform_idx_awg_path1 : 0
gain_awg_path0 : 1
gain_awg_path1 : 1
marker_ovr_en : False
marker_ovr_value : 0
mixer_corr_gain_ratio : 1
mixer_corr_phase_offset_degree : -0
mod_en_awg : False
nco_freq : 0 (Hz)
nco_phase_offs : 0 (Degrees)
offset_awg_path0 : 0
offset_awg_path1 : 0
sequence : None
sync_en : False
upsample_rate_awg_path0 : 0
upsample_rate_awg_path1 : 0
qcm_sequencer2:
parameter value
--------------------------------------------------------------------------------
channel_map_path0_out0_en : False
channel_map_path0_out2_en : True
channel_map_path1_out1_en : False
channel_map_path1_out3_en : True
cont_mode_en_awg_path0 : False
cont_mode_en_awg_path1 : False
cont_mode_waveform_idx_awg_path0 : 0
cont_mode_waveform_idx_awg_path1 : 0
gain_awg_path0 : 1
gain_awg_path1 : 1
marker_ovr_en : False
marker_ovr_value : 0
mixer_corr_gain_ratio : 1
mixer_corr_phase_offset_degree : -0
mod_en_awg : False
nco_freq : 0 (Hz)
nco_phase_offs : 0 (Degrees)
offset_awg_path0 : 0
offset_awg_path1 : 0
sequence : None
sync_en : False
upsample_rate_awg_path0 : 0
upsample_rate_awg_path1 : 0
qcm_sequencer3:
parameter value
--------------------------------------------------------------------------------
channel_map_path0_out0_en : False
channel_map_path0_out2_en : True
channel_map_path1_out1_en : False
channel_map_path1_out3_en : True
cont_mode_en_awg_path0 : False
cont_mode_en_awg_path1 : False
cont_mode_waveform_idx_awg_path0 : 0
cont_mode_waveform_idx_awg_path1 : 0
gain_awg_path0 : 1
gain_awg_path1 : 1
marker_ovr_en : False
marker_ovr_value : 0
mixer_corr_gain_ratio : 1
mixer_corr_phase_offset_degree : -0
mod_en_awg : False
nco_freq : 0 (Hz)
nco_phase_offs : 0 (Degrees)
offset_awg_path0 : 0
offset_awg_path1 : 0
sequence : None
sync_en : False
upsample_rate_awg_path0 : 0
upsample_rate_awg_path1 : 0
qcm_sequencer4:
parameter value
--------------------------------------------------------------------------------
channel_map_path0_out0_en : False
channel_map_path0_out2_en : True
channel_map_path1_out1_en : False
channel_map_path1_out3_en : True
cont_mode_en_awg_path0 : False
cont_mode_en_awg_path1 : False
cont_mode_waveform_idx_awg_path0 : 0
cont_mode_waveform_idx_awg_path1 : 0
gain_awg_path0 : 1
gain_awg_path1 : 1
marker_ovr_en : False
marker_ovr_value : 0
mixer_corr_gain_ratio : 1
mixer_corr_phase_offset_degree : -0
mod_en_awg : False
nco_freq : 0 (Hz)
nco_phase_offs : 0 (Degrees)
offset_awg_path0 : 0
offset_awg_path1 : 0
sequence : None
sync_en : False
upsample_rate_awg_path0 : 0
upsample_rate_awg_path1 : 0
qcm_sequencer5:
parameter value
--------------------------------------------------------------------------------
channel_map_path0_out0_en : False
channel_map_path0_out2_en : True
channel_map_path1_out1_en : False
channel_map_path1_out3_en : True
cont_mode_en_awg_path0 : False
cont_mode_en_awg_path1 : False
cont_mode_waveform_idx_awg_path0 : 0
cont_mode_waveform_idx_awg_path1 : 0
gain_awg_path0 : 1
gain_awg_path1 : 1
marker_ovr_en : False
marker_ovr_value : 0
mixer_corr_gain_ratio : 1
mixer_corr_phase_offset_degree : -0
mod_en_awg : False
nco_freq : 0 (Hz)
nco_phase_offs : 0 (Degrees)
offset_awg_path0 : 0
offset_awg_path1 : 0
sequence : None
sync_en : False
upsample_rate_awg_path0 : 0
upsample_rate_awg_path1 : 0
QRM snapshot:
qrm:
parameter value
--------------------------------------------------------------------------------
IDN : {'manufacturer': 'qblox', 'model': 'pulsar_qr...
in0_gain : -6 (dB)
in1_gain : -6 (dB)
out0_offset : 0 (V)
out1_offset : 0 (V)
reference_source : external
scope_acq_avg_mode_en_path0 : False
scope_acq_avg_mode_en_path1 : False
scope_acq_sequencer_select : 0
scope_acq_trigger_level_path0 : 0
scope_acq_trigger_level_path1 : 0
scope_acq_trigger_mode_path0 : sequencer
scope_acq_trigger_mode_path1 : sequencer
qrm_sequencer0:
parameter value
--------------------------------------------------------------------------------
channel_map_path0_out0_en : True
channel_map_path1_out1_en : True
cont_mode_en_awg_path0 : False
cont_mode_en_awg_path1 : False
cont_mode_waveform_idx_awg_path0 : 0
cont_mode_waveform_idx_awg_path1 : 0
demod_en_acq : False
discretization_threshold_acq : 0
gain_awg_path0 : 1
gain_awg_path1 : 1
integration_length_acq : 1024
marker_ovr_en : False
marker_ovr_value : 0
mixer_corr_gain_ratio : 1
mixer_corr_phase_offset_degree : -0
mod_en_awg : False
nco_freq : 0 (Hz)
nco_phase_offs : 0 (Degrees)
offset_awg_path0 : 0
offset_awg_path1 : 0
phase_rotation_acq : 0 (Degrees)
sequence : qrm_sequence.json
sync_en : True
upsample_rate_awg_path0 : 0
upsample_rate_awg_path1 : 0
qrm_sequencer1:
parameter value
--------------------------------------------------------------------------------
channel_map_path0_out0_en : True
channel_map_path1_out1_en : True
cont_mode_en_awg_path0 : False
cont_mode_en_awg_path1 : False
cont_mode_waveform_idx_awg_path0 : 0
cont_mode_waveform_idx_awg_path1 : 0
demod_en_acq : False
discretization_threshold_acq : 0
gain_awg_path0 : 1
gain_awg_path1 : 1
integration_length_acq : 1024
marker_ovr_en : False
marker_ovr_value : 0
mixer_corr_gain_ratio : 1
mixer_corr_phase_offset_degree : -0
mod_en_awg : False
nco_freq : 0 (Hz)
nco_phase_offs : 0 (Degrees)
offset_awg_path0 : 0
offset_awg_path1 : 0
phase_rotation_acq : 0 (Degrees)
sequence : None
sync_en : False
upsample_rate_awg_path0 : 0
upsample_rate_awg_path1 : 0
qrm_sequencer2:
parameter value
--------------------------------------------------------------------------------
channel_map_path0_out0_en : True
channel_map_path1_out1_en : True
cont_mode_en_awg_path0 : False
cont_mode_en_awg_path1 : False
cont_mode_waveform_idx_awg_path0 : 0
cont_mode_waveform_idx_awg_path1 : 0
demod_en_acq : False
discretization_threshold_acq : 0
gain_awg_path0 : 1
gain_awg_path1 : 1
integration_length_acq : 1024
marker_ovr_en : False
marker_ovr_value : 0
mixer_corr_gain_ratio : 1
mixer_corr_phase_offset_degree : -0
mod_en_awg : False
nco_freq : 0 (Hz)
nco_phase_offs : 0 (Degrees)
offset_awg_path0 : 0
offset_awg_path1 : 0
phase_rotation_acq : 0 (Degrees)
sequence : None
sync_en : False
upsample_rate_awg_path0 : 0
upsample_rate_awg_path1 : 0
qrm_sequencer3:
parameter value
--------------------------------------------------------------------------------
channel_map_path0_out0_en : True
channel_map_path1_out1_en : True
cont_mode_en_awg_path0 : False
cont_mode_en_awg_path1 : False
cont_mode_waveform_idx_awg_path0 : 0
cont_mode_waveform_idx_awg_path1 : 0
demod_en_acq : False
discretization_threshold_acq : 0
gain_awg_path0 : 1
gain_awg_path1 : 1
integration_length_acq : 1024
marker_ovr_en : False
marker_ovr_value : 0
mixer_corr_gain_ratio : 1
mixer_corr_phase_offset_degree : -0
mod_en_awg : False
nco_freq : 0 (Hz)
nco_phase_offs : 0 (Degrees)
offset_awg_path0 : 0
offset_awg_path1 : 0
phase_rotation_acq : 0 (Degrees)
sequence : None
sync_en : False
upsample_rate_awg_path0 : 0
upsample_rate_awg_path1 : 0
qrm_sequencer4:
parameter value
--------------------------------------------------------------------------------
channel_map_path0_out0_en : True
channel_map_path1_out1_en : True
cont_mode_en_awg_path0 : False
cont_mode_en_awg_path1 : False
cont_mode_waveform_idx_awg_path0 : 0
cont_mode_waveform_idx_awg_path1 : 0
demod_en_acq : False
discretization_threshold_acq : 0
gain_awg_path0 : 1
gain_awg_path1 : 1
integration_length_acq : 1024
marker_ovr_en : False
marker_ovr_value : 0
mixer_corr_gain_ratio : 1
mixer_corr_phase_offset_degree : -0
mod_en_awg : False
nco_freq : 0 (Hz)
nco_phase_offs : 0 (Degrees)
offset_awg_path0 : 0
offset_awg_path1 : 0
phase_rotation_acq : 0 (Degrees)
sequence : None
sync_en : False
upsample_rate_awg_path0 : 0
upsample_rate_awg_path1 : 0
qrm_sequencer5:
parameter value
--------------------------------------------------------------------------------
channel_map_path0_out0_en : True
channel_map_path1_out1_en : True
cont_mode_en_awg_path0 : False
cont_mode_en_awg_path1 : False
cont_mode_waveform_idx_awg_path0 : 0
cont_mode_waveform_idx_awg_path1 : 0
demod_en_acq : False
discretization_threshold_acq : 0
gain_awg_path0 : 1
gain_awg_path1 : 1
integration_length_acq : 1024
marker_ovr_en : False
marker_ovr_value : 0
mixer_corr_gain_ratio : 1
mixer_corr_phase_offset_degree : -0
mod_en_awg : False
nco_freq : 0 (Hz)
nco_phase_offs : 0 (Degrees)
offset_awg_path0 : 0
offset_awg_path1 : 0
phase_rotation_acq : 0 (Degrees)
sequence : None
sync_en : False
upsample_rate_awg_path0 : 0
upsample_rate_awg_path1 : 0