3131from PyDAQmx .DAQmxCallBack import *
3232
3333import numpy as np
34+ from numpy .lib .recfunctions import structured_to_unstructured
3435import labscript_utils .h5_lock
3536import h5py
3637from zprocess import Event
@@ -196,32 +197,32 @@ def program_buffered_DO(self, DO_table):
196197 line_final_value = bool ((1 << line ) & port_final_value )
197198 final_values ['%s/line%d' % (port_str , line )] = int (line_final_value )
198199
200+ # Convert DO table to a regular array and ensure it is C continguous:
201+ DO_table = np .ascontiguousarray (
202+ structured_to_unstructured (DO_table , dtype = np .uint32 )
203+ )
204+
199205 # Check if DOs are all zero for the whole shot. If they are this triggers a
200206 # bug in NI-DAQmx that throws a cryptic error for buffered output. In this
201207 # case, run it as a non-buffered task.
202- self .DO_all_zero = all (
203- DO_table [port ].sum () == 0 for port in DO_table .dtype .names
204- )
208+ self .DO_all_zero = not np .any (DO_table )
205209 if self .DO_all_zero :
206210 DO_table = DO_table [0 :1 ]
207211
208212 if self .static_DO or self .DO_all_zero :
209213 # Static DO. Start the task and write data, no timing configuration.
210214 self .DO_task .StartTask ()
211- # Write data for each port:
212- for port_str in ports :
213- # See the comment in self.program_manual as to why we are using uint32
214- # instead of the native size of the port
215- data = DO_table [port_str ].astype (np .uint32 , order = 'C' )
216- self .DO_task .WriteDigitalU32 (
217- 1 , # npts
218- False , # autostart
219- 10.0 , # timeout
220- DAQmx_Val_GroupByChannel ,
221- data ,
222- written ,
223- None ,
224- )
215+ # Write data. See the comment in self.program_manual as to why we are using
216+ # uint32 instead of the native size of each port
217+ self .DO_task .WriteDigitalU32 (
218+ 1 , # npts
219+ False , # autostart
220+ 10.0 , # timeout
221+ DAQmx_Val_GroupByScanNumber ,
222+ DO_table ,
223+ written ,
224+ None ,
225+ )
225226 else :
226227 # We use all but the last sample (which is identical to the second last
227228 # sample) in order to ensure there is one more clock tick than there are
@@ -237,23 +238,18 @@ def program_buffered_DO(self, DO_table):
237238 DAQmx_Val_FiniteSamps ,
238239 npts ,
239240 )
240- self .DO_task .CfgOutputBuffer (npts )
241-
242- # Write data for each port:
243- for port_str in ports :
244- # Use all but the last sample as mentioned above. See the comment in
245- # self.program_manual as to why we are using uint32 instead of the native
246- # size of the port.
247- data = DO_table [port_str ][:- 1 ].astype (np .uint32 , order = 'C' )
248- self .DO_task .WriteDigitalU32 (
249- npts ,
250- False , # autostart
251- 10.0 , # timeout
252- DAQmx_Val_GroupByChannel ,
253- data ,
254- written ,
255- None ,
256- )
241+
242+ # Write data. See the comment in self.program_manual as to why we are using
243+ # uint32 instead of the native size of each port.
244+ self .DO_task .WriteDigitalU32 (
245+ npts ,
246+ False , # autostart
247+ 10.0 , # timeout
248+ DAQmx_Val_GroupByScanNumber ,
249+ DO_table [:- 1 ], # All but the last sample as mentioned above
250+ written ,
251+ None ,
252+ )
257253
258254 # Go!
259255 self .DO_task .StartTask ()
@@ -273,15 +269,15 @@ def program_buffered_AO(self, AO_table):
273269 # Collect the final values of the analog outs:
274270 final_values = dict (zip (AO_table .dtype .names , AO_table [- 1 ]))
275271
276- # Obtain a view that is a regular array:
277- AO_table = AO_table . view (( AO_table . dtype [ 0 ], len ( AO_table . dtype . names )))
278- # And convert to 64 bit floats:
279- AO_table = AO_table . astype ( np . float64 )
272+ # Convert AO table to a regular array and ensure it is C continguous :
273+ AO_table = np . ascontiguousarray (
274+ structured_to_unstructured ( AO_table , dtype = np . float64 )
275+ )
280276
281277 # Check if AOs are all zero for the whole shot. If they are this triggers a
282278 # bug in NI-DAQmx that throws a cryptic error for buffered output. In this
283279 # case, run it as a non-buffered task.
284- self .AO_all_zero = all ( AO_table . flatten () == 0 )
280+ self .AO_all_zero = not np . any ( AO_table )
285281 if self .AO_all_zero :
286282 AO_table = AO_table [0 :1 ]
287283
@@ -763,13 +759,20 @@ def wait_monitor(self):
763759 semiperiods = self .read_edges (2 , timeout )
764760 # Did the wait finish of its own accord, or time out?
765761 if semiperiods is None :
766- # It timed out. Better trigger the clock to resume!
767- msg = """Wait timed out; retriggering clock with {:.3e} s pulse
768- ({} edge)"""
769- msg = dedent (msg ).format (pulse_width , self .timeout_trigger_type )
770- self .logger .debug (msg )
771- self .send_resume_trigger (pulse_width )
772- # Wait for it to respond to that:
762+ # It timed out. If there is a timeout device, send a trigger to
763+ # resume the clock!
764+ if self .DO_task is not None :
765+ msg = """Wait timed out; retriggering clock with {:.3e} s
766+ pulse ({} edge)"""
767+ msg = msg .format (pulse_width , self .timeout_trigger_type )
768+ self .logger .debug (dedent (msg ))
769+ self .send_resume_trigger (pulse_width )
770+ else :
771+ msg = """Specified wait timeout exceeded, but there is no
772+ timeout device with which to resume the experiment.
773+ Continuing to wait."""
774+ self .logger .warning (dedent (msg ))
775+ # Keep waiting for the clock to resume:
773776 self .logger .debug ('Waiting for pulse indicating end of wait' )
774777 semiperiods = self .read_edges (2 , timeout = None )
775778 # Alright, now we're at the end of the wait.
@@ -819,7 +822,8 @@ def stop_tasks(self, abort):
819822 if not abort :
820823 # Don't want errors about incomplete task to be raised if we are aborting:
821824 self .CI_task .StopTask ()
822- self .DO_task .StopTask ()
825+ if self .DO_task is not None :
826+ self .DO_task .StopTask ()
823827 if self .CI_task is not None :
824828 self .CI_task .ClearTask ()
825829 self .CI_task = None
@@ -853,15 +857,16 @@ def start_tasks(self):
853857 self .CI_task .StartTask ()
854858
855859 # The timeout task:
856- self .DO_task = Task ()
857- DO_chan = self .MAX_name + '/' + self .wait_timeout_connection
858- self .DO_task .CreateDOChan (DO_chan , "" , DAQmx_Val_ChanForAllLines )
859- # Ensure timeout trigger is armed:
860- written = int32 ()
861- # Writing autostarts the task:
862- self .DO_task .WriteDigitalLines (
863- 1 , True , 1 , DAQmx_Val_GroupByChannel , self .timeout_rearm , written , None
864- )
860+ if self .wait_timeout_MAX_name is not None :
861+ self .DO_task = Task ()
862+ DO_chan = self .wait_timeout_MAX_name + '/' + self .wait_timeout_connection
863+ self .DO_task .CreateDOChan (DO_chan , "" , DAQmx_Val_ChanForAllLines )
864+ # Ensure timeout trigger is armed:
865+ written = int32 ()
866+ # Writing autostarts the task:
867+ self .DO_task .WriteDigitalLines (
868+ 1 , True , 1 , DAQmx_Val_GroupByChannel , self .timeout_rearm , written , None
869+ )
865870
866871 def transition_to_buffered (self , device_name , h5file , initial_values , fresh ):
867872 self .logger .debug ('transition_to_buffered' )
0 commit comments