Microwave Spin echo (IQ modulation w/ ADF4351 & ADL5375)¶
In this example we connect a Red Pitaya 125-14 to an EVAL-CN0285-E1BZ containing a microwave synthesizer (ADF4351) & IQ modulator (ADL5375). We use DigitalIOs for the SPI configuration of the carrier and the RF outputs for fast quadrature modulation.
Required hardware connection (see README for IO Names and Pin Mapping):
digital_io_2[0] –> CE
digital_io_2[1] –> PDRF
digital_io_2[2] <– MUXOUT
digital_io_2[3] <– LD
digital_io_3[0] –> CLK
digital_io_3[1] –> LE
digital_io_3[2] –> DATA
The generated pulse sequence is a microwave spin echo (pi/2 + pi + pi/2) with variable delay in between.
NOTE:
The following resistors of the EVAL-CN0285-E1BZ need to be removed (if populated):
Connectivity to on-board controller: R46, R51, R52, R53, R54, R55 R56, R57
Pull-ups: R13, R14, R15, R16, R17, R18, R19
We use two AD8132 mounted on generic EVAL-FDA-1RZ-8 boards to perform the single-ended to differential pair conversion with common mode voltage of 0.5V.
Imports¶
import time
import numpy as np
from matplotlib import pyplot as plt
from openlabctrl.device import Rp_125_14_Z7010
from openlabctrl.sequence import IoSequence
from openlabctrl.frame import ParamIoSyncFrame
from openlabctrl.io.scope import ScopeSource
Device instances¶
rp_0 = Rp_125_14_Z7010(ip="192.168.1.143", label="rp_0")
IO Sequences & IO Frames instances¶
seq = IoSequence(device_list=[rp_0])
fr_rf_config = ParamIoSyncFrame(device_type=Rp_125_14_Z7010, trig=None)
fr_mw_config = ParamIoSyncFrame(device_type=Rp_125_14_Z7010, trig=None)
fr_pi_half = ParamIoSyncFrame(device_type=Rp_125_14_Z7010, trig=None)
fr_pi = ParamIoSyncFrame(device_type=Rp_125_14_Z7010, trig=None)
fr_wait = ParamIoSyncFrame(device_type=Rp_125_14_Z7010, trig=None)
Parameter¶
param_dict = {
"mw_config" : {
"frequency_mhz" : 106.1
},
"rf_config" : {
"freq_mhz" : 0,
"offset_i" : 0, #compensate for minor deviation of zero point (I modulation)
"offset_q" : 0 #compensate for minor deviation of zero point (Q modulation)
},
"pi_half" : {
"width_samples" : 20,
"ampl_i" : 0.5,
"ampl_q" : 0.5
},
"pi" : {
"width_samples" : 40,
"ampl_i" : 0.5,
"ampl_q" : 0.5
},
"wait" : {
"wait_time_samples" : 500
}
}
Frame functions¶
from devices import ADF4351
#MW config
def fr_func_mw_config(fr, param):
fr.reset()
mw = ADF4351(fr)
mw.io_config()
mw.rf_disable()
mw.chip_enable()
fr.delay(125000) #1ms
mw.frequency(param["frequency_mhz"] * 1e6)
fr.delay(125000) #1ms
#RF config
def fr_func_rf_config(fr, param):
fr.reset()
fr.rf_out_0.frequency(param["freq_mhz"] * 1e6)
fr.rf_out_1.frequency(param["freq_mhz"] * 1e6)
fr.rf_out_0.amplitude(param["offset_i"])
fr.rf_out_1.amplitude(param["offset_q"])
fr.rf_out_0.offset(0)
fr.rf_out_1.amplitude(0)
fr.rf_out_0.phase(0)
fr.rf_out_1.phase(90)
fr.rf_out_0.phase_reset()
fr.rf_out_1.phase_reset()
#Triangular Pulse
def fr_func_triangular_pulse(fr, param):
ampl_i_list = np.concatenate(
(np.linspace(0, param["ampl_i"], param["width_samples"]//2),
np.linspace(param["ampl_i"], 0, param["width_samples"]//2)))
ampl_q_list = np.concatenate(
(np.linspace(0, param["ampl_q"], param["width_samples"]//2),
np.linspace(param["ampl_q"], 0, param["width_samples"]//2)))
fr.reset()
mw = ADF4351(fr)
mw.rf_enable()
fr.rsync()
for ampl_i, ampl_q in zip(ampl_i_list, ampl_q_list):
fr.rf_out_0.amplitude(ampl_i)
fr.rf_out_1.amplitude(ampl_q)
fr.delay(10) #80us (compensate for RF out latency)
fr.rsync()
mw.rf_disable()
#Wait
def fr_func_wait(fr, param):
fr.reset()
fr.delay(param["wait_time_samples"])
Map frame function and parameters¶
fr_mw_config.set_frame_function(fr_func_mw_config)
fr_mw_config.set_frame_parameter(param_dict["mw_config"])
fr_rf_config.set_frame_function(fr_func_rf_config)
fr_rf_config.set_frame_parameter(param_dict["rf_config"])
fr_pi_half.set_frame_function(fr_func_triangular_pulse)
fr_pi_half.set_frame_parameter(param_dict["pi_half"])
fr_pi.set_frame_function(fr_func_triangular_pulse)
fr_pi.set_frame_parameter(param_dict["pi"])
fr_wait.set_frame_function(fr_func_wait)
fr_wait.set_frame_parameter(param_dict["wait"])
Sequence definition¶
seq.reset()
seq.add_frame(frame=fr_mw_config, device=rp_0, label="mw_config")
seq.add_frame(frame=fr_rf_config, device=rp_0, label="rf_config")
seq.add_frame(frame=fr_pi_half, device=rp_0, label="pi_half (I)")
seq.add_frame(frame=fr_wait, device=rp_0, label="wait (I)")
seq.add_frame(frame=fr_pi, device=rp_0, label="pi")
seq.add_frame(frame=fr_wait, device=rp_0, label="wait (II)")
seq.add_frame(frame=fr_pi_half, device=rp_0, label="pi_half (II)")
print(seq.sequence_description())
+--------------------+
| rp_0@192.168.1.143 |
+--------------------+
| mw_config |
| rf_config |
| pi_half (I) |
| wait (I) |
| pi |
| wait (II) |
| pi_half (II) |
+--------------------+
NOTE: Frames with (*) are triggered by external trigger source.
Upload & Run sequence¶
if not seq.is_done():
seq.stop()
repeat = 5
wait_list = np.arange(0, 50, 2)
for r in range(repeat):
for w in wait_list:
param_dict["wait"]["wait_time_samples"] = w
seq.upload()
seq.start()
seq.wait()
seq.get_status()
seq.stop()