Skip to content

Commit 31709a0

Browse files
author
RbChip
committed
NI-DAQmx-wait-changes: Bugfixes. Error checking for DAQmx analog input was in the wrong function
instead denying analog *output*. Fixed that. PulseBlasters were unable to detect the end of a shot without a wait monitor. Now they can, but only if they have programming_scheme='pb_stop_programming/STOP'. Otherwise the WAIT instruction at the end of a shot(used when programming_scheme='pb_start()/BRANCH') cannot be distinguished from a intra-shot wait, and so there is no way to detect end-of-shot.
1 parent c067e56 commit 31709a0

File tree

3 files changed

+65
-18
lines changed

3 files changed

+65
-18
lines changed

NI_DAQmx/labscript_devices.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -320,12 +320,6 @@ def _make_analog_out_table(self, analogs, times):
320320
"""Collect analog output data and create the output array"""
321321
if not analogs:
322322
return None
323-
if compiler.wait_table and compiler.wait_monitor is None:
324-
msg = """Cannot do analog input on an NI DAQmx device in an experiment that
325-
uses waits without a wait monitor. This is because input data cannot be
326-
'chunked' into requested segments without knowledge of the durations of
327-
the waits. See labscript.WaitMonitor for details."""
328-
raise LabscriptError(dedent(msg))
329323
n_timepoints = 1 if self.static_AO else len(times)
330324
connections = sorted(analogs, key=split_conn_AO)
331325
dtypes = [(c, np.float32) for c in connections]
@@ -384,6 +378,13 @@ def _make_analog_input_table(self, inputs):
384378
acq['units'],
385379
)
386380
)
381+
if acquisitions and compiler.wait_table and compiler.wait_monitor is None:
382+
msg = """Cannot do analog input on an NI DAQmx device in an experiment that
383+
uses waits without a wait monitor. This is because input data cannot be
384+
'chunked' into requested segments without knowledge of the durations of
385+
the waits. See labscript.WaitMonitor for details."""
386+
raise LabscriptError(dedent(msg))
387+
387388
# The 'a256' dtype below limits the string fields to 256
388389
# characters. Can't imagine this would be an issue, but to not
389390
# specify the string length (using dtype=str) causes the strings

PulseBlaster.py

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,23 @@
1616
str = unicode
1717

1818
from labscript_devices import BLACS_tab, runviewer_parser
19+
from labscript_utils import dedent
1920

20-
from labscript import Device, PseudoclockDevice, Pseudoclock, ClockLine, IntermediateDevice, DigitalQuantity, DigitalOut, DDS, DDSQuantity, config, LabscriptError, set_passed_properties
21+
from labscript import (
22+
Device,
23+
PseudoclockDevice,
24+
Pseudoclock,
25+
ClockLine,
26+
IntermediateDevice,
27+
DigitalQuantity,
28+
DigitalOut,
29+
DDS,
30+
DDSQuantity,
31+
config,
32+
LabscriptError,
33+
set_passed_properties,
34+
compiler,
35+
)
2136

2237
import numpy as np
2338

@@ -603,14 +618,30 @@ def write_pb_inst_to_h5(self, pb_inst, hdf5_file):
603618
group.create_dataset('PULSE_PROGRAM', compression=config.compression,data = pb_inst_table)
604619
self.set_property('stop_time', self.stop_time, location='device_properties')
605620

606-
621+
622+
def _check_wait_monitor_ok(self):
623+
if (
624+
compiler.master_pseudoclock is self
625+
and compiler.wait_table
626+
and compiler.wait_monitor is None
627+
and self.programming_scheme != 'pb_stop_programming/STOP'
628+
):
629+
msg = """If using waits without a wait monitor, the PulseBlaster used as a
630+
master pseudoclock must have
631+
programming_scheme='pb_stop_programming/STOP'. Otherwise there is no way
632+
for BLACS to distinguish between a wait, and the end of a shot. Either
633+
use a wait monitor (see labscript.WaitMonitor for details) or set
634+
programming_scheme='pb_stop_programming/STOP for %s."""
635+
raise LabscriptError(dedent(msg) % self.name)
636+
607637
def generate_code(self, hdf5_file):
608638
# Generate the hardware instructions
609-
hdf5_file.create_group('/devices/'+self.name)
639+
hdf5_file.create_group('/devices/' + self.name)
610640
PseudoclockDevice.generate_code(self, hdf5_file)
611641
dig_outputs, dds_outputs = self.get_direct_outputs()
612642
freqs, amps, phases = self.generate_registers(hdf5_file, dds_outputs)
613643
pb_inst = self.convert_to_pb_inst(dig_outputs, dds_outputs, freqs, amps, phases)
644+
self._check_wait_monitor_ok()
614645
self.write_pb_inst_to_h5(pb_inst, hdf5_file)
615646

616647

@@ -1042,10 +1073,17 @@ def transition_to_buffered(self,device_name,h5file,initial_values,fresh):
10421073
else:
10431074
raise ValueError('invalid programming_scheme %s'%str(self.programming_scheme))
10441075

1045-
# Are there waits in use in this experiment? The monitor waiting for the end of
1046-
# the experiment will need to know:
1047-
self.waits_pending = bool(len(hdf5_file['waits']))
1048-
1076+
# Are there waits in use in this experiment? The monitor waiting for the end
1077+
# of the experiment will need to know:
1078+
wait_monitor_exists = bool(hdf5_file['waits'].attrs['wait_monitor_acquisition_device'])
1079+
waits_in_use = bool(len(hdf5_file['waits']))
1080+
self.waits_pending = wait_monitor_exists and waits_in_use
1081+
if waits_in_use and not wait_monitor_exists:
1082+
# This should be caught during labscript compilation, but just in case.
1083+
# Having waits but not a wait monitor means we can't tell when the shot
1084+
# is over unless the shot ends in a STOP instruction:
1085+
assert self.programming_scheme == 'pb_stop_programming/STOP'
1086+
10491087
# Now we build a dictionary of the final state to send back to the GUI:
10501088
return_values = {'dds 0':{'freq':finalfreq0, 'amp':finalamp0, 'phase':finalphase0, 'gate':en0},
10511089
'dds 1':{'freq':finalfreq1, 'amp':finalamp1, 'phase':finalphase1, 'gate':en1},
@@ -1077,7 +1115,7 @@ def transition_to_manual(self):
10771115
if self.programming_scheme == 'pb_start/BRANCH':
10781116
done_condition = status['waiting']
10791117
elif self.programming_scheme == 'pb_stop_programming/STOP':
1080-
done_condition = True # status['stopped']
1118+
done_condition = status['stopped']
10811119

10821120
if time_based_shot_over is not None:
10831121
done_condition = time_based_shot_over

PulseBlaster_No_DDS.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def generate_code(self, hdf5_file):
5454
PseudoclockDevice.generate_code(self, hdf5_file)
5555
dig_outputs, ignore = self.get_direct_outputs()
5656
pb_inst = self.convert_to_pb_inst(dig_outputs, [], {}, {}, {})
57+
self._check_wait_monitor_ok()
5758
self.write_pb_inst_to_h5(pb_inst, hdf5_file)
5859

5960

@@ -403,9 +404,16 @@ def transition_to_buffered(self,device_name,h5file,initial_values,fresh):
403404
else:
404405
raise ValueError('invalid programming_scheme %s'%str(self.programming_scheme))
405406

406-
# Are there waits in use in this experiment? The monitor waiting for the end of
407-
# the experiment will need to know:
408-
self.waits_pending = bool(len(hdf5_file['waits']))
407+
# Are there waits in use in this experiment? The monitor waiting for the end
408+
# of the experiment will need to know:
409+
wait_monitor_exists = bool(hdf5_file['waits'].attrs['wait_monitor_acquisition_device'])
410+
waits_in_use = bool(len(hdf5_file['waits']))
411+
self.waits_pending = wait_monitor_exists and waits_in_use
412+
if waits_in_use and not wait_monitor_exists:
413+
# This should be caught during labscript compilation, but just in case.
414+
# having waits but not a wait monitor means we can't tell when the shot
415+
# is over unless the shot ends in a STOP instruction:
416+
assert self.programming_scheme == 'pb_stop_programming/STOP'
409417

410418
# Now we build a dictionary of the final state to send back to the GUI:
411419
return_values = {}
@@ -436,7 +444,7 @@ def transition_to_manual(self):
436444
if self.programming_scheme == 'pb_start/BRANCH':
437445
done_condition = status['waiting']
438446
elif self.programming_scheme == 'pb_stop_programming/STOP':
439-
done_condition = True # status['stopped']
447+
done_condition = status['stopped']
440448

441449
if time_based_shot_over is not None:
442450
done_condition = time_based_shot_over

0 commit comments

Comments
 (0)