05 - Calibrating the QICK for phase coherent readout (obsolete)
Objective: Calibrate the QICK clocks to have the same phase.
Before you measure a resonance with your QICK this is the first calibration you should do. It is a calibration for the two synthesizers which belong to the QICK signal generator and the QICK readout, respectively. The two synthesizers are running at the same frequency, but there is initially a constant phase difference \(\phi\) between these two synthesizers. Doing this calibration results in you finding that phase difference \(\phi\). In your subsequent measurements, you can specify this initial phase difference \(\phi\) to compensate for it. From then on, the signal generator can synthesize any frequency and then if you read in data (doing a digital down conversion in the process), the readout will still be phase coherent with respect to the signal generator.
The angular frequency \(\omega = 2 \pi f\) .
Also, \(\phi = (\omega t) + \phi_0\).
So, $:nbsphinx-math:phi `= (2 :nbsphinx-math:pi f)*t + :nbsphinx-math:phi`_0 $.
If \(f\) goes up linearly, the phase difference will also change linearly (it will either increase or decrease, depending on whether the readout is ahead or behind of the signal generator- this is randomly determined each time the board clocks are initialized). Once the phase hits 360 degrees it cycles back to 0 again. For a readout frequency of interest \(f_i\) there is a corresponding phase difference \(\phi_i\). In this demonstration we assume \(f_i \approx 180\) MHz. You can plot \(\phi(f)\) and evaluate \(\phi(f_i)=\phi_i\).
[1]:
# Import the QICK drivers and auxiliary libraries
from qick import *
%pylab inline
Populating the interactive namespace from numpy and matplotlib
[2]:
# Load bitstream with custom overlay
soc = QickSoc()
soccfg = soc
Hardware Configuration
tProc channel 7 : DAC 229 CH3 <-> Readout channel 0 : ADC 224 CH0
[3]:
class LoopbackProgram(AveragerProgram):
def initialize(self):
cfg=self.cfg
# set the nyquist zone
self.declare_gen(ch=cfg["res_ch"], nqz=1)
self.r_rp=self.ch_page(self.cfg["res_ch"]) # get register page for res_ch
self.r_gain=self.sreg(cfg["res_ch"], "gain") #Get gain register for res_ch
#configure the readout lengths and downconversion frequencies
self.declare_readout(ch=cfg["ro_ch"], length=self.cfg["readout_length"],
freq=self.cfg["pulse_freq"], gen_ch=cfg["res_ch"])
freq=self.freq2reg(cfg["pulse_freq"], gen_ch=cfg["res_ch"], ro_ch=cfg["ro_ch"]) # convert frequency to dac frequency (ensuring it is an available adc frequency)
self.set_pulse_registers(ch=cfg["res_ch"], style="const", freq=freq, phase=0, gain=cfg["pulse_gain"],
length=cfg["length"])
self.synci(200) # give processor some time to configure pulses
def body(self):
self.measure(pulse_ch=self.cfg["res_ch"],
adcs=[self.cfg["ro_ch"]],
adc_trig_offset=self.cfg["adc_trig_offset"],
t=0,
wait=True,
syncdelay=self.us2cycles(self.cfg["relax_delay"]))
First, sanity check that we can see the pulse we want to calibrate
[4]:
config={"res_ch":6, # --Fixed
"ro_ch":0, # --Fixed
"relax_delay":1.0, # --Fixed
"res_phase":0, # --Fixed
"length":400, # [Clock ticks]
"readout_length":200, # [Clock ticks]
"pulse_gain":10000, # [DAC units]
"pulse_freq": 100, # [MHz]
"adc_trig_offset": 200, # [Clock ticks]
"reps":1,
"soft_avgs":1,
}
prog =LoopbackProgram(soccfg, config)
(iq0,) = prog.acquire_decimated(soc,progress=False)
[5]:
# Plot results.
plt.figure(1)
plt.plot(iq0[0], label="I value; ADC 0")
plt.plot(iq0[1], label="Q value; ADC 0")
plt.ylabel("a.u.")
plt.xlabel("Clock ticks")
plt.title("Averages = " + str(config["soft_avgs"]))
plt.legend()
[5]:
<matplotlib.legend.Legend at 0x7f5bca5080>
Now we perform the calibration: Params 1 (spacing between points is too large)
[6]:
sweep_cfg={"start":100, "step":0.0005, "expts":40}
gpts=sweep_cfg["start"] + sweep_cfg["step"]*np.arange(sweep_cfg["expts"])
resultsi=[]
resultsq=[]
for g in gpts:
time.sleep(0.1)
config["pulse_freq"]=g
prog =LoopbackProgram(soccfg, config)
(iq0,) = prog.acquire_decimated(soc,progress=False)
di0 = np.sum(iq0[0])/config["readout_length"]
dq0 = np.sum(iq0[1])/config["readout_length"]
resultsi.append(di0)
resultsq.append(dq0)
resultsi=np.array(resultsi)
resultsq=np.array(resultsq)
[7]:
# Plot results.
sig = resultsi + 1j * resultsq
amp_array = np.abs(sig)
phase_array = np.angle(sig,deg=True)
for x in range(0,len(phase_array)):
if phase_array[x] <0:
phase_array[x] = phase_array[x] +360
plt.figure(1)
# plt.plot(gpts, resultsi,label="I value; ADC 0")
# plt.plot(gpts, resultsq,label="Q value; ADC 0")
# plt.plot(gpts, amp_array,label="Amplitude (DAC units); ADC 0")
plt.plot(gpts, phase_array, label="Phase (degrees); ADC 0")
plt.plot(gpts,phase_array, marker='.', linestyle="None",color="Red")
plt.xticks(rotation=90)
plt.title(r"$\phi$ vs $f$")
plt.ylabel(r"$\phi$ (degrees)")
plt.xlabel(r"$f$ (MHz)")
plt.legend()
plt.savefig("images/Phase_sweep.pdf", dpi=350)
Params 2 (We try again with finer spacing and now there is enough data for us to calibrate phase)
[8]:
sweep_cfg={"start":100, "step":0.000125, "expts":160}
gpts=sweep_cfg["start"] + sweep_cfg["step"]*np.arange(sweep_cfg["expts"])
resultsi=[]
resultsq=[]
for g in gpts:
time.sleep(0.1)
config["pulse_freq"]=g
prog =LoopbackProgram(soccfg, config)
(iq0,) = prog.acquire_decimated(soc,progress=False)
di0 = np.sum(iq0[0])/config["readout_length"]
dq0 = np.sum(iq0[1])/config["readout_length"]
resultsi.append(di0)
resultsq.append(dq0)
resultsi=np.array(resultsi)
resultsq=np.array(resultsq)
[9]:
# Plot results.
sig = resultsi + 1j * resultsq
amp_array = np.abs(sig)
phase_array = np.angle(sig,deg=True)
for x in range(0,len(phase_array)):
if phase_array[x] <0:
phase_array[x] = phase_array[x] +360
plt.figure(1)
# plt.plot(gpts, resultsi,label="I value; ADC 0")
# plt.plot(gpts, resultsq,label="Q value; ADC 0")
# plt.plot(gpts, amp_array,label="Amplitude (DAC units); ADC 0")
plt.plot(gpts, phase_array, label="Phase (degrees); ADC 0")
plt.plot(gpts,phase_array, marker='.', linestyle="None",color="Red")
plt.xticks(rotation=90)
plt.title(r"$\phi$ vs $f$")
plt.ylabel(r"$\phi$ (degrees)")
plt.xlabel(r"$f$ (MHz)")
plt.legend()
plt.savefig("images/Phase_sweep.pdf", dpi=350)
Params 3 (We zoom in on the frequency area of interest and then print out the associated phase of interest)
[10]:
sweep_cfg={"start":100.0075, "step":0.000125, "expts":40}
gpts=sweep_cfg["start"] + sweep_cfg["step"]*np.arange(sweep_cfg["expts"])
resultsi=[]
resultsq=[]
for g in gpts:
time.sleep(0.1)
config["pulse_freq"]=g
prog =LoopbackProgram(soccfg, config)
(iq0,) = prog.acquire_decimated(soc,progress=False)
di0 = np.sum(iq0[0])/config["readout_length"]
dq0 = np.sum(iq0[1])/config["readout_length"]
resultsi.append(di0)
resultsq.append(dq0)
resultsi=np.array(resultsi)
resultsq=np.array(resultsq)
[11]:
# Plot results.
sig = resultsi + 1j * resultsq
amp_array = np.abs(sig)
phase_array = np.angle(sig,deg=True)
for x in range(0,len(phase_array)):
if phase_array[x] <0:
phase_array[x] = phase_array[x] +360
print("Iteration i = %d, freq_i = %f MHz, phi_i = %f degrees" %(x,gpts[x], phase_array[x]))
plt.figure(1)
# plt.plot(gpts, resultsi,label="I value; ADC 0")
# plt.plot(gpts, resultsq,label="Q value; ADC 0")
# plt.plot(gpts, amp_array,label="Amplitude (DAC units); ADC 0")
plt.plot(gpts, phase_array, label="Phase (degrees); ADC 0")
plt.plot(gpts,phase_array, marker='.', linestyle="None",color="Red")
plt.xticks(rotation=90)
plt.title(r"$\phi$ vs $f$")
plt.ylabel(r"$\phi$ (degrees)")
plt.xlabel(r"$f$ (MHz)")
plt.legend()
plt.savefig("images/Phase_sweep.pdf", dpi=350)
Iteration i = 0, freq_i = 100.007500 MHz, phi_i = 355.168452 degrees
Iteration i = 1, freq_i = 100.007625 MHz, phi_i = 344.942773 degrees
Iteration i = 2, freq_i = 100.007750 MHz, phi_i = 334.561054 degrees
Iteration i = 3, freq_i = 100.007875 MHz, phi_i = 324.265874 degrees
Iteration i = 4, freq_i = 100.008000 MHz, phi_i = 313.901978 degrees
Iteration i = 5, freq_i = 100.008125 MHz, phi_i = 303.607619 degrees
Iteration i = 6, freq_i = 100.008250 MHz, phi_i = 293.346924 degrees
Iteration i = 7, freq_i = 100.008375 MHz, phi_i = 282.962804 degrees
Iteration i = 8, freq_i = 100.008500 MHz, phi_i = 272.687116 degrees
Iteration i = 9, freq_i = 100.008625 MHz, phi_i = 262.384146 degrees
Iteration i = 10, freq_i = 100.008750 MHz, phi_i = 252.026269 degrees
Iteration i = 11, freq_i = 100.008875 MHz, phi_i = 241.732993 degrees
Iteration i = 12, freq_i = 100.009000 MHz, phi_i = 231.350591 degrees
Iteration i = 13, freq_i = 100.009125 MHz, phi_i = 221.087037 degrees
Iteration i = 14, freq_i = 100.009250 MHz, phi_i = 210.793508 degrees
Iteration i = 15, freq_i = 100.009375 MHz, phi_i = 200.399291 degrees
Iteration i = 16, freq_i = 100.009500 MHz, phi_i = 190.139522 degrees
Iteration i = 17, freq_i = 100.009625 MHz, phi_i = 179.734384 degrees
Iteration i = 18, freq_i = 100.009750 MHz, phi_i = 169.496957 degrees
Iteration i = 19, freq_i = 100.009875 MHz, phi_i = 159.184603 degrees
Iteration i = 20, freq_i = 100.010000 MHz, phi_i = 148.810858 degrees
Iteration i = 21, freq_i = 100.010125 MHz, phi_i = 138.574445 degrees
Iteration i = 22, freq_i = 100.010250 MHz, phi_i = 128.281286 degrees
Iteration i = 23, freq_i = 100.010375 MHz, phi_i = 117.892335 degrees
Iteration i = 24, freq_i = 100.010500 MHz, phi_i = 107.620575 degrees
Iteration i = 25, freq_i = 100.010625 MHz, phi_i = 97.225735 degrees
Iteration i = 26, freq_i = 100.010750 MHz, phi_i = 86.949832 degrees
Iteration i = 27, freq_i = 100.010875 MHz, phi_i = 76.650230 degrees
Iteration i = 28, freq_i = 100.011000 MHz, phi_i = 66.272948 degrees
Iteration i = 29, freq_i = 100.011125 MHz, phi_i = 56.004722 degrees
Iteration i = 30, freq_i = 100.011250 MHz, phi_i = 45.684729 degrees
Iteration i = 31, freq_i = 100.011375 MHz, phi_i = 35.369000 degrees
Iteration i = 32, freq_i = 100.011500 MHz, phi_i = 25.065924 degrees
Iteration i = 33, freq_i = 100.011625 MHz, phi_i = 14.665463 degrees
Iteration i = 34, freq_i = 100.011750 MHz, phi_i = 4.390865 degrees
Iteration i = 35, freq_i = 100.011875 MHz, phi_i = 354.139750 degrees
Iteration i = 36, freq_i = 100.012000 MHz, phi_i = 343.761122 degrees
Iteration i = 37, freq_i = 100.012125 MHz, phi_i = 333.467373 degrees
Iteration i = 38, freq_i = 100.012250 MHz, phi_i = 323.097440 degrees
Iteration i = 39, freq_i = 100.012375 MHz, phi_i = 312.817796 degrees
For example, if your cavity tone was generated with an IF of 420.9 MHz, you would look at the above table and see that freq_i = 420.9 MHz corresponds to phi_i = 308.7 degrees (that’s specific to this board session, for your QICK it will be different). That value of phi_i will be used as the value of the config file parameter associated with the offset phase of the cavity pulse (the parameter is called res_phase in the demo 00_Send_recieve_pulse).
[ ]: