Skip to content

Commit 5193aa9

Browse files
committed
Modified NI_DAQmx to handle differentially terminated analog inputs.
Behavior is to either have all the inputs as single-ended or differential. Also modified `get_capabilities.py` a little more so that it will correctly set the default AI termination for devices that do not support RSE. Tested with a NI USB 6366. Was able to simultaneously sample at 2MS/s on two channels (the max spec for this device).
1 parent 4d38383 commit 5193aa9

File tree

6 files changed

+172
-11
lines changed

6 files changed

+172
-11
lines changed

labscript_devices/NI_DAQmx/blacs_tabs.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def initialise_GUI(self):
4141

4242
num_AO = properties['num_AO']
4343
num_AI = properties['num_AI']
44+
AI_chans = properties['AI_chans']
4445
ports = properties['ports']
4546
num_CI = properties['num_CI']
4647

@@ -187,6 +188,8 @@ def initialise_GUI(self):
187188
{
188189
'MAX_name': self.MAX_name,
189190
'num_AI': num_AI,
191+
'AI_chans': AI_chans,
192+
'AI_term': properties['AI_term'],
190193
'AI_range': properties['AI_range'],
191194
'AI_start_delay': properties['AI_start_delay'],
192195
'clock_terminal': clock_terminal,

labscript_devices/NI_DAQmx/blacs_workers.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ def init(self):
408408

409409
# Hard coded for now. Perhaps we will add functionality to enable
410410
# and disable inputs in manual mode, and adjust the rate:
411-
self.manual_mode_chans = ['ai%d' % i for i in range(self.num_AI)]
411+
self.manual_mode_chans = self.AI_chans
412412
self.manual_mode_rate = 1000
413413

414414
# An event for knowing when the wait durations are known, so that we may use
@@ -471,11 +471,16 @@ def start_task(self, chans, rate):
471471
self.read_array = np.zeros((num_samples, len(chans)), dtype=np.float64)
472472
self.task = Task()
473473

474+
if self.AI_term == 'RSE':
475+
term = DAQmx_Val_RSE
476+
elif self.AI_term == 'Diff':
477+
term = DAQmx_Val_Diff
478+
474479
for chan in chans:
475480
self.task.CreateAIVoltageChan(
476481
self.MAX_name + '/' + chan,
477482
"",
478-
DAQmx_Val_RSE,
483+
term,
479484
self.AI_range[0],
480485
self.AI_range[1],
481486
DAQmx_Val_Volts,

labscript_devices/NI_DAQmx/labscript_devices.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ class NI_DAQmx(IntermediateDevice):
5959
"clock_mirror_terminal",
6060
"AI_range",
6161
"AI_start_delay",
62+
"AI_term",
63+
"AI_chans",
6264
"AO_range",
6365
"max_AI_multi_chan_rate",
6466
"max_AI_single_chan_rate",
@@ -72,6 +74,7 @@ class NI_DAQmx(IntermediateDevice):
7274
"supports_buffered_AO",
7375
"supports_buffered_DO",
7476
"supports_semiperiod_measurement",
77+
"supports_simultaneous_AI_sampling",
7578
"clock_limit",
7679
"wait_monitor_minimum_pulse_width",
7780
"wait_monitor_supports_wait_completed_events",
@@ -90,7 +93,10 @@ def __init__(
9093
clock_mirror_terminal=None,
9194
acquisition_rate=None,
9295
AI_range=None,
96+
AI_range_Diff=None,
9397
AI_start_delay=0,
98+
AI_term='RSE',
99+
AI_term_cfg=None,
94100
AO_range=None,
95101
max_AI_multi_chan_rate=None,
96102
max_AI_single_chan_rate=None,
@@ -104,6 +110,7 @@ def __init__(
104110
supports_buffered_AO=False,
105111
supports_buffered_DO=False,
106112
supports_semiperiod_measurement=False,
113+
supports_simultaneous_AI_sampling=False,
107114
**kwargs
108115
):
109116
"""Generic class for NI_DAQmx devices.
@@ -192,12 +199,20 @@ def __init__(
192199
self.max_DO_sample_rate = max_DO_sample_rate
193200
self.min_semiperiod_measurement = min_semiperiod_measurement
194201
self.num_AI = num_AI
202+
self.AI_term = AI_term
203+
self.AI_chans = [key for key,val in AI_term_cfg.items() if self.AI_term in val]
204+
if not len(self.AI_chans):
205+
msg = """AI termination {0} not supported by this device."""
206+
raise LabscriptError(dedent(msg.format(AI_term)))
207+
if AI_term == 'Diff':
208+
self.AI_range = AI_range_Diff
195209
self.num_AO = num_AO
196210
self.num_CI = num_CI
197211
self.ports = ports if ports is not None else {}
198212
self.supports_buffered_AO = supports_buffered_AO
199213
self.supports_buffered_DO = supports_buffered_DO
200214
self.supports_semiperiod_measurement = supports_semiperiod_measurement
215+
self.supports_simultaneous_AI_sampling = supports_simultaneous_AI_sampling
201216

202217
if self.supports_buffered_DO and self.supports_buffered_AO:
203218
self.clock_limit = min(self.max_DO_sample_rate, self.max_AO_sample_rate)
@@ -291,8 +306,7 @@ def add_device(self, device):
291306
buffered output"""
292307
raise ValueError(dedent(msg) % port_str)
293308
elif isinstance(device, AnalogIn):
294-
ai_num = split_conn_AI(device.connection)
295-
if ai_num >= self.num_AI:
309+
if device.connection not in self.AI_chans:
296310
msg = """Cannot add analog input with connection string '%s' to device
297311
with num_AI=%d"""
298312
raise ValueError(dedent(msg) % (device.connection, self.num_AI))
@@ -352,7 +366,9 @@ def _check_AI_not_too_fast(self, AI_table):
352366
# Either no AI in use, or already checked against single channel rate in
353367
# __init__.
354368
return
355-
if self.acquisition_rate <= self.max_AI_multi_chan_rate / n:
369+
if self.supports_simultaneous_AI_sampling and self.acquisition_rate <= self.max_AI_multi_chan_rate:
370+
return
371+
elif self.acquisition_rate <= self.max_AI_multi_chan_rate / n:
356372
return
357373
msg = """Requested acqusition_rate %f for device %s with %d analog input
358374
channels in use is too fast. Device supports a rate of %f per channel when
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#####################################################################
2+
# #
3+
# /NI_DAQmx/models/_subclass_template.py #
4+
# #
5+
# Copyright 2018, Christopher Billington #
6+
# #
7+
# This file is part of the module labscript_devices, in the #
8+
# labscript suite (see http://labscriptsuite.org), and is #
9+
# licensed under the Simplified BSD License. See the license.txt #
10+
# file in the root of the project for the full license. #
11+
# #
12+
#####################################################################
13+
14+
#####################################################################
15+
# WARNING #
16+
# #
17+
# This file is auto-generated, any modifications may be #
18+
# overwritten. See README.txt in this folder for details #
19+
# #
20+
#####################################################################
21+
22+
23+
from labscript_devices.NI_DAQmx.labscript_devices import NI_DAQmx
24+
25+
CAPABILITIES = {
26+
'AI_range': [-10.0, 10.0],
27+
'AI_range_Diff': [-10.0, 10.0],
28+
'AI_start_delay': 4e-08,
29+
'AI_term': 'Diff',
30+
'AI_term_cfg': {
31+
'ai0': ['Diff'],
32+
'ai1': ['Diff'],
33+
'ai2': ['Diff'],
34+
'ai3': ['Diff'],
35+
'ai4': ['Diff'],
36+
'ai5': ['Diff'],
37+
'ai6': ['Diff'],
38+
'ai7': ['Diff'],
39+
},
40+
'AO_range': [-10.0, 10.0],
41+
'max_AI_multi_chan_rate': 2000000.0,
42+
'max_AI_single_chan_rate': 2000000.0,
43+
'max_AO_sample_rate': 3333333.3333333335,
44+
'max_DO_sample_rate': 10000000.0,
45+
'min_semiperiod_measurement': 1e-07,
46+
'num_AI': 8,
47+
'num_AO': 2,
48+
'num_CI': 4,
49+
'ports': {
50+
'port0': {'num_lines': 8, 'supports_buffered': True},
51+
'port1': {'num_lines': 8, 'supports_buffered': False},
52+
'port2': {'num_lines': 8, 'supports_buffered': False},
53+
},
54+
'supports_buffered_AO': True,
55+
'supports_buffered_DO': True,
56+
'supports_semiperiod_measurement': True,
57+
'supports_simultaneous_AI_sampling': True,
58+
}
59+
60+
61+
class NI_USB_6366(NI_DAQmx):
62+
description = 'NI-USB-6366'
63+
64+
def __init__(self, *args, **kwargs):
65+
# Any provided kwargs take precedent over capabilities
66+
combined_kwargs = CAPABILITIES.copy()
67+
combined_kwargs.update(kwargs)
68+
NI_DAQmx.__init__(self, *args, **combined_kwargs)

labscript_devices/NI_DAQmx/models/capabilities.json

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,5 +465,73 @@
465465
"supports_buffered_AO": true,
466466
"supports_buffered_DO": true,
467467
"supports_semiperiod_measurement": true
468+
},
469+
"USB-6366": {
470+
"AI_range": [
471+
-10.0,
472+
10.0
473+
],
474+
"AI_range_Diff": [
475+
-10.0,
476+
10.0
477+
],
478+
"AI_start_delay": 4e-08,
479+
"AI_term": "Diff",
480+
"AI_term_cfg": {
481+
"ai0": [
482+
"Diff"
483+
],
484+
"ai1": [
485+
"Diff"
486+
],
487+
"ai2": [
488+
"Diff"
489+
],
490+
"ai3": [
491+
"Diff"
492+
],
493+
"ai4": [
494+
"Diff"
495+
],
496+
"ai5": [
497+
"Diff"
498+
],
499+
"ai6": [
500+
"Diff"
501+
],
502+
"ai7": [
503+
"Diff"
504+
]
505+
},
506+
"AO_range": [
507+
-10.0,
508+
10.0
509+
],
510+
"max_AI_multi_chan_rate": 2000000.0,
511+
"max_AI_single_chan_rate": 2000000.0,
512+
"max_AO_sample_rate": 3333333.3333333335,
513+
"max_DO_sample_rate": 10000000.0,
514+
"min_semiperiod_measurement": 1e-07,
515+
"num_AI": 8,
516+
"num_AO": 2,
517+
"num_CI": 4,
518+
"ports": {
519+
"port0": {
520+
"num_lines": 8,
521+
"supports_buffered": true
522+
},
523+
"port1": {
524+
"num_lines": 8,
525+
"supports_buffered": false
526+
},
527+
"port2": {
528+
"num_lines": 8,
529+
"supports_buffered": false
530+
}
531+
},
532+
"supports_buffered_AO": true,
533+
"supports_buffered_DO": true,
534+
"supports_semiperiod_measurement": true,
535+
"supports_simultaneous_AI_sampling": true
468536
}
469537
}

labscript_devices/NI_DAQmx/models/get_capabilities.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ def wrapped2(name):
179179
DAQmxGetDevAOVoltageRngs = float64_array_prop(PyDAQmx.DAQmxGetDevAOVoltageRngs)
180180
DAQmxGetDevAIVoltageRngs = float64_array_prop(PyDAQmx.DAQmxGetDevAIVoltageRngs)
181181
DAQmxGetPhysicalChanAITermCfgs = int32_prop(PyDAQmx.DAQmxGetPhysicalChanAITermCfgs)
182+
DAQmxGetDevAISimultaneousSamplingSupported = bool_prop(PyDAQmx.DAQmxGetDevAISimultaneousSamplingSupported)
182183

183184

184185
def port_supports_buffered(device_name, port, clock_terminal=None):
@@ -569,12 +570,12 @@ def get_min_semiperiod_measurement(device_name):
569570
capabilities[model]["max_AI_multi_chan_rate"] = multi_rate
570571
if capabilities[model]["num_AI"] > 0:
571572
capabilities[model]["AI_term_cfg"] = supported_AI_terminal_configurations(name)
572-
cfgs = [item for sublist in capabilities[model]["AI_term_cfg"].values()
573-
for item in sublist]
574-
capabilities[model]["num_AI_Diff"] = cfgs.count('Diff')
575-
capabilities[model]["num_AI_RSE"] = cfgs.count('RSE')
576-
else:
577-
capabilities[model]["AI_term_cfg"] = None
573+
cfgs = [item for sublist in capabilities[model]["AI_term_cfg"].values() for item in sublist]
574+
if cfgs.count('RSE'):
575+
capabilities[model]["AI_term"] = 'RSE'
576+
elif cfgs.count('Diff'):
577+
capabilities[model]["AI_term"] = 'Diff'
578+
capabilities[model]["supports_simultaneous_AI_sampling"] = DAQmxGetDevAISimultaneousSamplingSupported(name)
578579

579580
capabilities[model]["ports"] = {}
580581
ports = DAQmxGetDevDOPorts(name)

0 commit comments

Comments
 (0)