Skip to content

Commit 3139ff7

Browse files
committed
Implement ability to specify an external sample clock timebase signal
for analog inputs on an NI-DAQ. This mitigates issues where the AI clock and the timing pseudoclock can get out of sync with each other, particularly for long shots.
1 parent f23aca2 commit 3139ff7

File tree

4 files changed

+47
-1
lines changed

4 files changed

+47
-1
lines changed

docs/source/devices/ni_daqs.rst

+20
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ Exact numbers of channels, performance, and configuration depend on the model of
7979
AnalogOut('daq_ao0',daq,'ao0')
8080
AnalogIn('daq_ai1',daq,'ai1')
8181
82+
WaitMonitors
83+
------------
84+
8285
NI DAQs are also used within labscript to provide a :class:`WaitMonitor <labscript:labscript.labscript.waitmonitor>`.
8386
When configured, the `WaitMonitor` allows for arbitrary-length pauses in experiment execution, waiting for some trigger to restart.
8487
The monitor provides a measurement of the duration of the wait for use in interpreting the resulting data from the experiment.
@@ -105,6 +108,23 @@ Note that the counter connection is specified using the logical label `'ctr0'`.
105108
The physical wiring for this configuration would have port0/line0 wired directly to PFI9, with PFI1 being sent to the master pseudoclock retriggering system in case of timeout.
106109
If timeouts are not expected/represent experiment failure, this physical connection can be omitted.
107110

111+
AI timing skew
112+
--------------
113+
114+
Given how the NI-DAQmx driver currently works,
115+
all of the outputs (and generally other hardware) are hardware-timed via direct outputs from the parent pseudoclocks.
116+
Under default usage, this is not true for the analog inputs of the DAQs,
117+
which are timed via the internal reference oscillator of the DAQ.
118+
Synchronization between the two is handled at the end by correlating start times and slicing the AI traces at the appropriate times.
119+
This works fine if the reference clocks for the pseudoclock and the DAQ don't drift relative to each other,
120+
but that is generally not the case for a longer shot (on the order of 1 second) since the standard clocks for a pulseblaster and a DAQ both have accuracy on the order of 50 ppm.
121+
122+
With version 1.2.0 of the NI-DAQmx driver, this issue can be mitigated by suppling an external sample timebase that is phase synchronous with the DAQ's pseudoclock device.
123+
This is done using the DAQmx `SampleClkTimebase` synchronization method.
124+
Simply provide an external clocking signal that is faster than the analog input sampling rate,
125+
and the DAQ will use an internall PLL to derive the AI sample clock from the provided timebase.
126+
Specifying an externally provided sample timebase is done using the `AI_timebase_terminal` and `AI_timebase_rate` arguments,
127+
which specify the input terminal (generally a PFI line) and the clock frequency.
108128

109129
Detailed Documentation
110130
~~~~~~~~~~~~~~~~~~~~~~

labscript_devices/NI_DAQmx/blacs_tabs.py

+2
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ def initialise_GUI(self):
204204
'AI_range': properties['AI_range'],
205205
'AI_start_delay': properties['AI_start_delay'],
206206
'AI_start_delay_ticks': properties['AI_start_delay_ticks'],
207+
'AI_timebase_terminal': properties.get('AI_timebase_terminal',None),
208+
'AI_timebase_rate': properties.get('AI_timebase_rate',None),
207209
'clock_terminal': clock_terminal,
208210
},
209211
)

labscript_devices/NI_DAQmx/blacs_workers.py

+7
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,13 @@ def start_task(self, chans, rate):
491491
None,
492492
)
493493

494+
if self.AI_timebase_terminal is None:
495+
# use internal default
496+
pass
497+
else:
498+
self.task.SetSampClkTimebaseSrc(self.AI_timebase_terminal)
499+
self.task.SetSampClkTimebaseRate(self.AI_timebase_rate)
500+
494501
self.task.CfgSampClkTiming(
495502
"", rate, DAQmx_Val_Rising, DAQmx_Val_ContSamps, num_samples
496503
)

labscript_devices/NI_DAQmx/labscript_devices.py

+18-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
# #
1212
#####################################################################
1313

14-
__version__ = '1.1.0'
14+
__version__ = '1.2.0'
1515

1616

1717
from labscript import (
@@ -63,6 +63,8 @@ class NI_DAQmx(IntermediateDevice):
6363
"AI_start_delay_ticks",
6464
"AI_term",
6565
"AI_chans",
66+
"AI_timebase_terminal",
67+
"AI_timebase_rate",
6668
"AO_range",
6769
"max_AI_multi_chan_rate",
6870
"max_AI_single_chan_rate",
@@ -100,6 +102,8 @@ def __init__(
100102
AI_start_delay_ticks=None,
101103
AI_term='RSE',
102104
AI_term_cfg=None,
105+
AI_timebase_terminal=None,
106+
AI_timebase_rate=None,
103107
AO_range=None,
104108
max_AI_multi_chan_rate=None,
105109
max_AI_single_chan_rate=None,
@@ -149,6 +153,12 @@ def __init__(
149153
AI_term_cfg (dict, optional): Dictionary of analog input channels and their
150154
supported terminations. Best to use `get_capabilities.py` to introspect
151155
these.
156+
AI_timebase_terminal (str, optional): Channel string that specifies what
157+
channel to use for the Sample Clock Timebase signal.
158+
If None, use default internal clocks.
159+
Must also specify the rate when not using the internal sources.
160+
AI_timebase_rate (float, optional): Supplied clock frequency for the AI timebase.
161+
Only specify if using an external clock source.
152162
AO_range (iterable, optional): A `[Vmin, Vmax]` pair that sets the analog
153163
output voltage range for all analog outputs.
154164
max_AI_multi_chan_rate (float, optional): Max supported analog input
@@ -245,6 +255,13 @@ def __init__(
245255
# no analog inputs
246256
self.AI_chans = []
247257
self.start_delay_ticks = None
258+
# special AI timebase handling
259+
if num_AI > 0:
260+
if (AI_timebase_rate is None) ^ (AI_timebase_terminal is None):
261+
raise LabscriptError("You must specify terminal and rate when using an external AI timebase")
262+
self.AI_timebase_terminal = AI_timebase_terminal
263+
self.AI_timebease_rate = AI_timebase_rate
264+
248265
self.num_AO = num_AO
249266
self.num_CI = num_CI
250267
self.ports = ports if ports is not None else {}

0 commit comments

Comments
 (0)