NI DAQs

This module has been developed using an NI USB-6221 – the code should generally work for all DAQmx boards, but I’m sure there are plenty of compatibility bugs just waiting for you wonderful users to find and fix.

First, make sure you have NI’s DAQmx software installed. Once that’s set, you’ll need PyDAQmx, a basic Python interface to DAQmx. You can get it via pip:

pip install PyDAQmx

The NIDAQ class lets you interact with your board and all its various inputs and outputs in a fairly simple way. Let’s say you’ve hooked up digital I/O P1.0 to analog input AI0, and your analog out AO1 to analog input AI1:

>>> from instrumental.drivers.daq.ni import NIDAQ, list_instruments
>>> list_instruments()
[<NIDAQ 'Dev0'>]
>>> daq = NIDAQ('Dev0')
>>> daq.ai0.read()
<Quantity(0.0154385786803, 'volt)>
>>> daq.port1[0].write(True)
>>> daq.ai0.read()
<Quantity(5.04241962841, 'volt')>
>>> daq.ao1.write('2.1V')
>>> daq.ai1.read()
<Quantity(2.10033320744, 'volt')>

Now let’s try using digital input. Assume P1.1 is attached to P1.2:

>>> daq.port1[1].write(False)
>>> daq.port1[2].read()
False
>>> daq.port1[1].write(True)
>>> daq.port1[2].read()
True

Let’s read and write more than one bit at a time. To write to multiple lines simultaneously, pass an unsigned int to write(). The line with the lowest index corresponds to the lowest bit, and so on. If you read from multiple lines, read() returns an int. Connect P1.0-3 to P1.4-7:

>>> daq.port1[0:3].write(5)  # 0101 = decimal 5
>>> daq.port1[4:7].read()  # Note that the last index IS included
5
>>> daq.port1[7:4].read()  # This flips the ordering of the bits
10                         # 1010 = decimal 10
>>> daq.port1[0].write(False)  # Zero the ones bit individually
>>> daq.port1[4:7].read()  # 0100 = decimal 4
4

You can also read and write arrays of buffered data. Use the same read() and write() methods, just include your timing info (and pass in the data as an array if writing). When writing, you must provide either freq or fsamp, and may provide either duration or reps to specify for how long the waveform is output. For example, there are many ways to output the same sinusoid:

>>> from instrumental import u
>>> from numpy import pi, sin, linspace
>>> data = sin( 2*pi * linspace(0, 1, 100, endpoint=False) )*5*u.V + 5*u.V
>>> daq.ao0.write(data, duration='1s', freq='500Hz')
>>> daq.ao0.write(data, duration='1s', fsamp='50kHz')
>>> daq.ao0.write(data, reps=500, freq='500Hz')
>>> daq.ao0.write(data, reps=500, fsamp='50kHz')

Note the use of endpoint=False in linspace. This ensures we don’t repeat the start/end point (0V) of our sine waveform when outputting more than one period.

All this stuff is great for simple tasks, but sometimes you may want to perform input and output on multiple channels simultaneously. To accomplish this we need to use Tasks.

Note

Tasks in the ni module are similar, but not the same as Tasks in DAQmx (and PyDAQmx). Our Tasks allow you to quickly and easily perform simultaneous input and output with one Task without the hassle of having to create multiple and hook their timing and triggers up.

Here’s an example of how to perform simultaneous input and output:

>>> from instrumental.drivers.daq.ni import NIDAQ, Task
>>> from instrumental import u
>>> from numpy import linspace
>>> daq = NIDAQ('Dev0')
>>> task = Task(daq.ao0, daq.ai0)
>>> task.set_timing(duration='1s', fsamp='10Hz')
>>> write_data = {'ao0': linspace(0, 9, 10) * u.V}
>>> task.run(write_data)
{u'ai0': <Quantity([  1.00000094e+01   1.89578724e-04   9.99485542e-01   2.00007917e+00
   3.00034866e+00   3.99964556e+00   4.99991698e+00   5.99954114e+00
   6.99981625e+00   7.99976941e+00], 'volt')>,
 u't': <Quantity([ 0.   0.1  0.2  0.3  0.4  0.5  0.6  0.7  0.8  0.9], 'second')>}

As you can see, we create a dict as input to the run() method. Its keys are the names of the input channels, and its values are the corresponding array Quantities that we want to write. Similarly, the run() returns a dict that contains the input that was read. This dict also contains the time data under key ‘t’. Note that the read and write happen concurrently, so each voltage read has not yet moved to its new setpoint.

Module Reference

Driver module for NI-DAQmx-supported hardware.

class instrumental.drivers.daq.ni.AnalogIn(dev, chan_name)

Methods

read([duration, fsamp, n_samples]) Read one or more analog input samples.
__init__(dev, chan_name)
read(duration=None, fsamp=None, n_samples=None)

Read one or more analog input samples.

By default, reads and returns a single sample. If two of duration, fsamp, and n_samples are given, an array of samples is read and returned.

Parameters:

duration : Quantity

How long to read from the analog input, specified as a Quantity. Use with fsamp or n_samples.

fsamp : Quantity

The sample frequency, specified as a Quantity. Use with duration or n_samples.

n_samples : int

The number of samples to read. Use with duration or fsamp.

Returns:

data : scalar or array Quantity

The data that was read from analog input.

class instrumental.drivers.daq.ni.AnalogOut(dev, chan_name)

Methods

write(data[, duration, reps, fsamp, freq, ...]) Write a value or array to the analog output.
__init__(dev, chan_name)
write(data, duration=None, reps=None, fsamp=None, freq=None, onboard=True)

Write a value or array to the analog output.

If data is a scalar value, it is written to output and the function returns immediately. If data is an array of values, a buffered write is performed, writing each value in sequence at the rate determined by duration and fsamp or freq. You must specify either fsamp or freq.

When writing an array, this function blocks until the output sequence has completed.

Parameters:

data : scalar or array Quantity

The value or values to output, passed in Volt-compatible units.

duration : Quantity, optional

Used when writing arrays of data. This is how long the entirety of the output lasts, specified as a second-compatible Quantity. If duration is longer than a single period of data, the waveform will repeat. Use either this or reps, not both. If neither is given, waveform is output once.

reps : int or float, optional

Used when writing arrays of data. This is how many times the waveform is repeated. Use either this or duration, not both. If neither is given, waveform is output once.

fsamp: Quantity, optional

Used when writing arrays of data. This is the sample frequency, specified as a Hz-compatible Quantity. Use either this or freq, not both.

freq : Quantity, optional

Used when writing arrays of data. This is the frequency of the overall waveform, specified as a Hz-compatible Quantity. Use either this or fsamp, not both.

onboard : bool, optional

Use only onboard memory. Defaults to True. If False, all data will be continually buffered from the PC memory, even if it is only repeating a small number of samples many times.

class instrumental.drivers.daq.ni.Channel
class instrumental.drivers.daq.ni.Counter(dev, chan_name)

Methods

output_pulses(freq[, duration, reps, ...]) Generate digital pulses using the counter.
__init__(dev, chan_name)
output_pulses(freq, duration=None, reps=None, idle_high=False, delay=None, duty_cycle=0.5)

Generate digital pulses using the counter.

Outputs digital pulses with a given frequency and duty cycle.

This function blocks until the output sequence has completed.

Parameters:

freq : Quantity

This is the frequency of the pulses, specified as a Hz-compatible Quantity.

duration : Quantity, optional

How long the entirety of the output lasts, specified as a second-compatible Quantity. Use either this or reps, not both. If neither is given, only one pulse is generated.

reps : int, optional

How many pulses to generate. Use either this or duration, not both. If neither is given, only one pulse is generated.

idle_high : bool, optional

Whether the resting state is considered high or low. Idles low by default.

delay : Quantity, optional

How long to wait before generating the first pulse, specified as a second-compatible Quantity. Defaults to zero.

duty_cycle : float, optional

The width of the pulse divided by the pulse period. The default is a 50% duty cycle.

class instrumental.drivers.daq.ni.NIDAQ(dev_name)

Methods

create_task()
get_AI_channels()
get_AI_max_range() Returns the min and max voltage of the widest AI range
get_AI_ranges()
get_AO_channels()
get_AO_max_range() Returns the min and max voltage of the widest AO range
get_AO_ranges()
get_CI_channels()
get_CO_channels()
get_DI_lines()
get_DI_ports()
get_DO_lines()
get_DO_ports()
get_chassis_num()
get_product_type()
get_serial()
get_slot_num()
get_terminals()
__init__(dev_name)

Constructor for an NIDAQ object. End users should not use this directly, and should instead use instrument()

create_task()
get_AI_channels()
get_AI_max_range()

Returns the min and max voltage of the widest AI range

get_AI_ranges()
get_AO_channels()
get_AO_max_range()

Returns the min and max voltage of the widest AO range

get_AO_ranges()
get_CI_channels()
get_CO_channels()
get_DI_lines()
get_DI_ports()
get_DO_lines()
get_DO_ports()
get_chassis_num()
get_product_type()
get_serial()
get_slot_num()
get_terminals()
class instrumental.drivers.daq.ni.Task(*args)

Note that true DAQmx tasks can only include one type of channel (e.g. AI). To run multiple synchronized reads/writes, we need to make one task for each type, then use the same sample clock for each.

Methods

run([write_data])
set_timing([duration, fsamp, n_samples, ...])
__init__(*args)

Creates a task that uses the given channels.

Each arg can either be a Channel or a tuple of (Channel, name_str)

run(write_data=None)
set_timing(duration=None, fsamp=None, n_samples=None, mode=u'finite', clock=u'', rising=True)
class instrumental.drivers.daq.ni.VirtualDigitalChannel(dev, line_pairs)

Methods

as_input()
as_output()
read()
write(value) Write a value to the digital output channel
write_sequence(data[, duration, reps, ...]) Write an array of samples to the digital output channel
__init__(dev, line_pairs)
as_input()
as_output()
read()
write(value)

Write a value to the digital output channel

Parameters:

value : int or bool

An int representing the digital values to write. The lowest bit of the int is written to the first digital line, the second to the second, and so forth. For a single-line DO channel, can be a bool.

write_sequence(data, duration=None, reps=None, fsamp=None, freq=None, onboard=True)

Write an array of samples to the digital output channel

Outputs a buffered digital waveform, writing each value in sequence at the rate determined by duration and fsamp or freq. You must specify either fsamp or freq.

This function blocks until the output sequence has completed.

Parameters:

data : array or list of ints or bools

The sequence of samples to output. For a single-line DO channel, samples can be bools.

duration : Quantity, optional

How long the entirety of the output lasts, specified as a second-compatible Quantity. If duration is longer than a single period of data, the waveform will repeat. Use either this or reps, not both. If neither is given, waveform is output once.

reps : int or float, optional

How many times the waveform is repeated. Use either this or duration, not both. If neither is given, waveform is output once.

fsamp: Quantity, optional

This is the sample frequency, specified as a Hz-compatible Quantity. Use either this or freq, not both.

freq : Quantity, optional

This is the frequency of the overall waveform, specified as a Hz-compatible Quantity. Use either this or fsamp, not both.

onboard : bool, optional

Use only onboard memory. Defaults to True. If False, all data will be continually buffered from the PC memory, even if it is only repeating a small number of samples many times.

instrumental.drivers.daq.ni.list_instruments()