Hello, I have a script that is at its core a frequency scan using two AD9910 urukul boards. I am initialising 3 channels in total, as shown in the 'hardware_init' function. Two channels are on one urukul board, and the channels are labelled 'mw_channel_1' and 'mw_channel_2'. A third channel is on another urukul board, and is labelled 'rf_channel_1'.
I then have multiple functions that set up the profile settings on each of these channels. I use 'set()' and then 'cpld.set_profile' in order to set up the profile on one urukul channel, and then a separate 'pulse' function to turn the channel on with the hopefully the profile settings I would like.
I was getting multiple underflows when running the following experiment, and put in delays in the places where the underflow happened, however I now get an underflow in the middle of my pulse sequence where I cant put a delay in because I need precise timing.
I have previously tried to set all the profiles beforehand at the beginning of a scan like this:
@kernel
def preset_all_profiles(self):
delay_mu(int(250e3))
self.rf_channel.set(frequency=0e6, phase=0.0, amplitude=self.rf_amplitude, profile=0, ref_time_mu=now_mu())
delay_mu(int(250e3))
self.mw_channel_1.set(frequency=self.plus_transition, phase=0.0, amplitude=self.mw_amplitude_plus, profile=0, ref_time_mu=now_mu())
delay_mu(int(250e3))
self.mw_channel_1.set(frequency=self.clock_transition, phase=0.0, amplitude=self.clock_amplitude, profile=1, ref_time_mu=now_mu())
delay_mu(int(250e3))
self.mw_channel_1.set(frequency=self.doppler_transition, phase=0.0, amplitude=1.0, profile=3, ref_time_mu=now_mu())
delay_mu(int(250e3))
self.mw_channel_2.set(frequency=self.minus_transition, phase=0.0, amplitude=self.mw_amplitude_minus, profile=0, ref_time_mu=now_mu())
I would then use
self.channel.cpld.set_profile(0)
self.channel.sw.on()
delay_mu(t)
self.channel.sw.off()
to quickly switch in and out of profiles.
However, I found that I had the wrong settings at certain points in the scan when I checked on the oscilloscope. I now use the functions as shown in the code block below, to immediately set the profiles before pulsing the channel.
@kernel
def hardware_init(self):
self.core.reset()
self.rf_channel.cpld.init()
self.mw_channel_1.cpld.init()
self.mw_channel_2.cpld.init()
delay(300 * ms)
self.rf_channel.init()
delay(300 * ms)
self.mw_channel_1.init()
delay(30 * ms)
self.mw_channel_2.init()
delay(2 * ms)
self.pmt.setup_PMT()
delay(1 * ms)
self.eom.setup_EOM()
delay(1 * ms)
self.aom.doppler_on()
@kernel
def set_attenuation(self):
self.core.reset()
delay(1 * ms)
self.rf_channel.set_att(att1)
delay(2 * ms)
self.mw_channel_1.set_att(att2)
delay(2 * ms)
self.mw_channel_2.set_att(att3)
@kernel
def urukul_doppler_on(self,f):
self.mw_channel_1.set(frequency=f, phase=0.0, amplitude=1.0, profile=3, ref_time_mu=now_mu())
self.mw_channel_1.cpld.set_profile(3)
self.mw_channel_1.sw.on()
delay(3000*us)
self.mw_channel_1.sw.off()
@kernel
def pulse(self,f,a,t):
self.rf_channel.set(frequency=f, phase=0.0, amplitude=a, profile=0, ref_time_mu=now_mu())
self.rf_channel.cpld.set_profile(0)
self.rf_channel.sw.on()
delay_mu(t)
self.rf_channel.sw.off()
@kernel
def sideband_pulse(self, f,a,t):
self.rf_channel.set(frequency=f phase=0.0, amplitude=a, profile=1, ref_time_mu=now_mu())
self.rf_channel.cpld.set_profile(1)
self.rf_channel.sw.on()
delay_mu(t)
self.rf_channel.sw.off()
@kernel
def clock_pulse(self,f,a):
self.mw_channel_1.set(frequency=f, phase=0.0, amplitude=a, profile=1, ref_time_mu=now_mu())
self.mw_channel_1.cpld.set_profile(1)
self.mw_channel_1.sw.on()
delay_mu(self.clock_pi_time_mu)
self.mw_channel_1.sw.off()
@kernel
def dressing_fields_on(self,f1,f2,a1,a2):
self.mw_channel_1.set(frequency=f1, phase=0.0, amplitude=a1, profile=0, ref_time_mu=now_mu())
self.mw_channel_2.set(frequency=f2, phase=0.0, amplitude=a2, profile=0, ref_time_mu=now_mu())
self.mw_channel_1.cpld.set_profile(0)
self.mw_channel_2.cpld.set_profile(0)
with parallel:
self.mw_channel_1.sw.on()
self.mw_channel_2.sw.on()
@kernel
def dressing_fields_off(self):
with parallel:
self.mw_channel_1.sw.off()
self.mw_channel_2.sw.off()
The main run sequence then looks like this:
for i in range(len(self.scanTimes_mu)):
for j in range(self.averages):
self.pulsed_sbc()
self.set_scan_profile(f1)
self.dark_state()
self.clock_pulse() # set profile 1 microwave channel 1
self.dressing_fields_on() # set profile 0 on both channels
self.pulse(self.scanTimes_mu[i]) # set profile 0 rf channel
self.dressing_fields_off() # set profile 0 on both channels
self.clock_pulse() # set profile 1 microwave channel 1
self.timects[i][j] = self.detect()
self.doppler() # set profile 0 microwave channel 1
Interleaved in the above is the following function that I've written for sideband cooling, and the underflow would appear after 'self.dressing_field_on()'
@kernel
def pulsed_sbc(self):
self.core.break_realtime()
self.set_scan_profile(f2)
for t in self.sb_time_array_mu:
self.dark_state() # prep in dark state
self.clock_pulse()
self.dressing_fields_on()
self.rf_channel.sw.on()
self.rf_channel.cpld.set_profile(1)
delay_mu(t)
self.rf_channel.sw.off()
self.dressing_fields_off()
self.clock_pulse()
My question is: am I using the urukuls and their profiles correctly?
Please let me know if there's something that's not clear or if I've missed something, thanks for reading,