Skip to content

Commit 7f70188

Browse files
committed
Adds PulseBlasterT1 class.
Makes a few modifications to PulserBlasterArb to separate resets of the channels and clock
1 parent 4678626 commit 7f70188

File tree

1 file changed

+92
-7
lines changed

1 file changed

+92
-7
lines changed

src/qt3utils/pulsers/pulseblaster.py

Lines changed: 92 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313

1414
class PulseBlaster(ExperimentPulser):
1515

16-
def __init__(self, instruction_set_resolution_in_ns = 50):
16+
def __init__(self, pb_board_number=0, instruction_set_resolution_in_ns=50):
1717
self.instruction_set_resolution_in_ns = instruction_set_resolution_in_ns
18+
self.pb_board_number = pb_board_number
1819

1920
def start(self):
2021
self.open()
@@ -65,16 +66,17 @@ def PBInd(self, *args, **kwargs):
6566

6667
class PulseBlasterArb(PulseBlaster):
6768

68-
def __init__(self, pb_board_number = 1, *args, **kwargs):
69+
def __init__(self, *args, **kwargs):
6970
super().__init__(*args, **kwargs)
70-
self.pb_board_number = pb_board_number
71-
self.reset()
71+
self.clear_channel_settings()
72+
self.clear_clock_channels()
73+
self.set_full_cycle_length(0)
7274

73-
def reset(self):
75+
def clear_clock_channels(self):
7476
self.clock_channels = []
7577
self.clock_period = None
78+
def clear_channel_settings(self):
7679
self.channel_settings = []
77-
self.full_cycle_width = 0
7880

7981
def set_clock_channels(self, pulse_blaster_channels, clock_period):
8082
'''
@@ -177,9 +179,92 @@ def __init__(self, pb_board_number = 1,
177179
aom_channel output controls the AOM by holding a positive voltage
178180
cycle_width - the length of the programmed pulse. Since aom channel is held on, this value is arbitrary
179181
"""
180-
super().__init__(pb_board_number, *args, **kwargs)
182+
super().__init__(pb_board_number=pb_board_number, *args, **kwargs)
181183
self.add_channels(aom_channel, 0, cycle_width)
182184

185+
class PulseBlasterT1(PulseBlasterArb):
186+
"""
187+
Sets up the pulse blaster to emit TTL signals for measuring the T1 relaxation time of
188+
a quantum state that can be optically initialized and read out.
189+
190+
pulse sequence
191+
aom on, aom off for time = ~full_cycle_width/2 - tau, aom on, aom off for time = tau, aom on, aom off for time = ~full_cycle_width/2
192+
193+
Note that since this cycle repeats (often for many thousands of times), the first aom on pulse is
194+
our background signal because it occurs ~full_cycle_width/2 after the third aom pulse.
195+
The second aom pulse occurs a time tau before the third aom pulse.
196+
Thus, the third aom pulse acts as a readout that occurs time tau after initialization.
197+
It also acts as the initialization for the first aom pulse, which is why we use the first aom pulse for background measuremnt
198+
This all assumes, of course, that the ensemble of states can be fully initialized within the aom pulse duration.
199+
Default is set to 10 microseconds, which should be sufficient with ~1mW of optical power.
200+
201+
Also to note: our current reliance on zeeshawn/pulseblaster hinders our ability to create long
202+
pulse blaster sequences and simultaneously short clock periods. There seems to be a limitation that
203+
full_cycle_width / clock_period <= 2000. This is probably because 2000 clock ticks requires 4000
204+
programming instructions, based upon zeeshawn/pulseblaster code. The pulse blaster has a limit
205+
of 4k memory for pulse instructions.
206+
"""
207+
def __init__(self, aom_channels=0,
208+
clock_channels=2,
209+
trigger_channels=3,
210+
aom_pulse_duration_time=10e-6,
211+
aom_response_time = 800e-9,
212+
clock_period=0.25e-6,
213+
trigger_pulse_duration=1e-6,
214+
tau_readout_delay_default=10e-6,
215+
full_cycle_width=0.5e-3, *args, **kwargs):
216+
"""
217+
pb_board_number - the board number (0, 1, ...)
218+
aom_channel output controls the AOM by holding a positive voltage
219+
full_cycle_width - the length of the programmed pulse. Since aom channel is held on, this value is arbitrary
220+
"""
221+
super().__init__(*args, **kwargs)
222+
self.aom_channels = aom_channels
223+
self.clock_channels = clock_channels
224+
self.trigger_channels = trigger_channels
225+
self.aom_pulse_duration_time = aom_pulse_duration_time
226+
self.aom_response_time = aom_response_time
227+
self.trigger_pulse_duration = trigger_pulse_duration
228+
self.clock_period = clock_period
229+
self.tau_readout_delay = tau_readout_delay_default
230+
self.set_full_cycle_length(full_cycle_width)
231+
self.clock_delay = 0 #artificial delay to handle issue where NIDAQ doesn't start acquireing data until a full clock cycle after the trigger
232+
# this normally isn't an issue when the AOM response time is greater than a clock cycle.
233+
234+
def program_pulser_state(self, tau_readout_delay=None, *args, **kwargs):
235+
"""
236+
tau_readout_delay
237+
"""
238+
239+
if tau_readout_delay is not None:
240+
self.tau_readout_delay = np.round(tau_readout_delay,8)
241+
242+
self.clear_channel_settings()
243+
self.set_clock_channels(self.clock_channels, self.clock_period)
244+
self.add_channels(self.trigger_channels, 0, self.trigger_pulse_duration)
245+
246+
self.clock_delay = 2*self.clock_period # we have to delay signals such that the pulses arrive after subsequent clock signals to our DAQ, otherwise the readout appears phase shifted
247+
# # first aom pulse
248+
self.add_channels(self.aom_channels, self.clock_delay, self.aom_pulse_duration_time)
249+
# delay
250+
read_out_start_time = self.full_cycle_width / 2
251+
read_out_start_time -= self.tau_readout_delay + self.aom_pulse_duration_time
252+
# second aom pulse
253+
self.add_channels(self.aom_channels, self.clock_delay + read_out_start_time, self.aom_pulse_duration_time)
254+
# third aom pulse
255+
self.add_channels(self.aom_channels, self.clock_delay + self.full_cycle_width / 2, self.aom_pulse_duration_time)
256+
257+
258+
self.raise_for_pulse_width(self.tau_readout_delay)
259+
return super().program_pulser_state(*args, **kwargs)
260+
261+
def raise_for_pulse_width(self, tau_readout_delay, *args, **kwargs):
262+
min_required_length = 2 * (self.aom_pulse_duration_time + tau_readout_delay + self.aom_pulse_duration_time)
263+
min_required_length += self.aom_response_time
264+
265+
if self.full_cycle_width < min_required_length:
266+
raise PulseTrainWidthError(f'Readout delay is too large: {tau_readout_delay:.2e}. Increase self.full_cycle_width to > {min_required_length:.2e}')
267+
183268
class PulseBlasterCWODMR(PulseBlaster):
184269
'''
185270
Programs the pulse sequences needed for CWODMR.

0 commit comments

Comments
 (0)