-
Notifications
You must be signed in to change notification settings - Fork 41
/
Copy path03_time_of_flight.py
137 lines (123 loc) · 5.61 KB
/
03_time_of_flight.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
"""
TIME OF FLIGHT
This sequence involves sending a readout pulse and capturing the raw ADC traces.
The data undergoes post-processing to calibrate three distinct parameters:
- Time of Flight: This represents the internal processing time and the propagation delay of the readout pulse.
Its value can be adjusted in the configuration under "time_of_flight".
This value is utilized to offset the acquisition window relative to when the readout pulse is dispatched.
- Analog Inputs Offset: Due to minor impedance mismatches, the signals captured by the OPX might exhibit slight offsets.
These can be rectified in the configuration at: config/controllers/"con1"/analog_inputs, enhancing the demodulation process.
- Analog Inputs Gain: If a signal is constrained by digitization or if it saturates the ADC,
the variable gain of the OPX analog input can be modified to fit the signal within the ADC range of +/-0.5V.
This gain, ranging from -12 dB to 20 dB, can also be adjusted in the configuration at: config/controllers/"con1"/analog_inputs.
"""
from qm.qua import *
from qm import QuantumMachinesManager
from qm import SimulationConfig
from configuration import *
import matplotlib.pyplot as plt
from scipy.signal import savgol_filter
###################
# The QUA program #
###################
n_avg = 100 # Number of averaging loops
with program() as raw_trace_prog:
n = declare(int) # QUA variable for the averaging loop
adc_st = declare_stream(adc_trace=True) # The stream to store the raw ADC trace
with for_(n, 0, n < n_avg, n + 1):
# Reset the phase of the digital oscillator associated to the resonator element. Needed to average the cosine signal.
reset_phase("resonator")
# Sends the readout pulse and stores the raw ADC traces in the stream called "adc_st"
measure("readout", "resonator", adc_st)
# Wait for the resonator to deplete
wait(depletion_time * u.ns, "resonator")
with stream_processing():
# Will save average:
adc_st.input1().average().save("adc1")
adc_st.input2().average().save("adc2")
# Will save only last run:
adc_st.input1().save("adc1_single_run")
adc_st.input2().save("adc2_single_run")
#####################################
# Open Communication with the QOP #
#####################################
qmm = QuantumMachinesManager(host=qop_ip, port=qop_port, cluster_name=cluster_name, octave=octave_config)
#######################
# Simulate or execute #
#######################
simulate = False
if simulate:
# Simulates the QUA program for the specified duration
simulation_config = SimulationConfig(duration=10_000) # In clock cycles = 4ns
# Simulate blocks python until the simulation is done
job = qmm.simulate(config, raw_trace_prog, simulation_config)
# Get the simulated samples
samples = job.get_simulated_samples()
# Plot the simulated samples
samples.con1.plot()
# Get the waveform report object
waveform_report = job.get_simulated_waveform_report()
# Cast the waveform report to a python dictionary
waveform_dict = waveform_report.to_dict()
# Visualize and save the waveform report
waveform_report.create_plot(samples, plot=True, save_path="./")
else:
# Open the quantum machine
qm = qmm.open_qm(config)
# Send the QUA program to the OPX, which compiles and executes it
job = qm.execute(raw_trace_prog)
# Creates a result handle to fetch data from the OPX
res_handles = job.result_handles
# Waits (blocks the Python console) until all results have been acquired
res_handles.wait_for_all_values()
# Fetch the raw ADC traces and convert them into Volts
adc1 = u.raw2volts(res_handles.get("adc1").fetch_all())
adc2 = u.raw2volts(res_handles.get("adc2").fetch_all())
adc1_single_run = u.raw2volts(res_handles.get("adc1_single_run").fetch_all())
adc2_single_run = u.raw2volts(res_handles.get("adc2_single_run").fetch_all())
# Derive the average values
adc1_mean = np.mean(adc1)
adc2_mean = np.mean(adc2)
# Remove the average values
adc1_unbiased = adc1 - np.mean(adc1)
adc2_unbiased = adc2 - np.mean(adc2)
# Filter the data to get the pulse arrival time
signal = savgol_filter(np.abs(adc1_unbiased + 1j * adc2_unbiased), 11, 3)
# Detect the arrival of the readout signal
th = (np.mean(signal[:100]) + np.mean(signal[:-100])) / 2
delay = np.where(signal > th)[0][0]
delay = np.round(delay / 4) * 4 # Find the closest multiple integer of 4ns
# Plot data
fig = plt.figure()
plt.subplot(121)
plt.title("Single run")
plt.plot(adc1_single_run, "b", label="Input 1")
plt.plot(adc2_single_run, "r", label="Input 2")
xl = plt.xlim()
yl = plt.ylim()
plt.axhline(y=0.5)
plt.axhline(y=-0.5)
plt.plot(xl, adc1_mean * np.ones(2), "k--")
plt.plot(xl, adc2_mean * np.ones(2), "k--")
plt.plot(delay * np.ones(2), yl, "k--")
plt.xlabel("Time [ns]")
plt.ylabel("Signal amplitude [V]")
plt.legend()
plt.subplot(122)
plt.title("Averaged run")
plt.plot(adc1, "b", label="Input 1")
plt.plot(adc2, "r", label="Input 2")
xl = plt.xlim()
yl = plt.ylim()
plt.plot(xl, adc1_mean * np.ones(2), "k--")
plt.plot(xl, adc2_mean * np.ones(2), "k--")
plt.plot(delay * np.ones(2), yl, "k--")
plt.xlabel("Time [ns]")
plt.legend()
plt.grid("all")
plt.tight_layout()
plt.show()
# Update the config
print(f"DC offset to add to I in the config: {-adc1_mean:.6f} V")
print(f"DC offset to add to Q in the config: {-adc2_mean:.6f} V")
print(f"Time Of Flight to add in the config: {delay} ns")