class mrpt::hwdrivers::CNationalInstrumentsDAQ

An interface to read from data acquisition boards compatible with National Instruments “DAQmx Base” or “DAQmx”.

Refer to DAQmx Base C API reference online to learn more on the concepts of “channels”, “tasks” (which in this MRPT class are mapped to independent grabbing threads), etc. If both DAQmx and DAQmxBase are installed in the system, DAQmx will be used. This class API isolate the user from the usage of one or another specific library.

This class can be used as a sensor from the application “rawlog-grabber”, or directly as a C++ class from a user program. Refer to the example: [MRPT]/samples/NIDAQ_test

Samples will be returned inside mrpt::obs::CObservationRawDAQ in “packets” of a predefined number of samples, which can be changed by the user through the “samplesPerChannelToRead” parameter of each task.

For multichannels tasks, samples will be interleaved. For example, the readings from successive timesteps for 4 ADC channels will be available in the ADC vector inside mrpt::obs::CObservationRawDAQ in this order:

  • A0[0] A1[0] A2[0] A3[0] A0[1] A1[1] A2[1] A3[1] A0[2] A1[2] A2[2] A3[2] …

The sensor label (field “m_sensorLabel”) of each grabbed observation will be the concatenation of this class sensor label, a dot (“.”) and the task label (default=”task###”, with ### the task index).

 PARAMETERS IN THE ".INI"-LIKE CONFIGURATION STRINGS:
-------------------------------------------------------
  [supplied_section_name]
; Number of tasks (each will run in a thread). Task indices are 0-based.
; (Parameters below follow NIs DAQmx API notation)
num_tasks  = 1

; Channels, separated by commas if more than one.
;  - "ai": Analog inputs
;  - "ao": Analog outputs
;  - "di": Digital inputs
;  - "do": Digital inputs
;  - "ci_period",
;    "ci_count_edges", "ci_pulse_width",
;    "ci_lin_encoder", "ci_ang_encoder" : Counters & encoders (WARNING: NI
says "a task can include only one counter input channel")
;  - "co_pulses": Output digital pulses (WARNING: NI says "a task can include
only one counter output channel")
;
task0.channels = ai  //, ao, di, do, ci_ang_encoder
;task0.taskLabel= MY_LABEL     // Optional textual label to build the
mrpt::obs::CObservation sensor label (default: task number)
task0.samplesPerSecond = 1000 // Samples per second. Continuous (infinite)
sampling is assumed.
task0.samplesPerChannelToRead = 1000  // The number of samples to grab at
once from each channel. ;task0.bufferSamplesPerChannel = 200000 // Increase
if you have errors about " Onboard device memory overflow.(...)"

; Analog input channel params.
task0.ai.physicalChannel = Dev1/ai0:3, Dev1/ai6
task0.ai.physicalChannelCount = 5  // *IMPORTANT* This must be the total
number of channels listed in "physicalChannel" (e.g. 4 for "Dev1/ai0:3")
task0.ai.terminalConfig  = DAQmx_Val_Cfg_Default | DAQmx_Val_RSE |
DAQmx_Val_NRSE | DAQmx_Val_Diff   // One of these strings
task0.ai.minVal          = -10.0    // Volts
task0.ai.maxVal          =  10.0    // Volts

; Analog output channel params.
task0.ao.physicalChannel = Dev1/ao0, Dev1/ao2:4
task0.ao.physicalChannelCount = 4  // *IMPORTANT* This must be the total
number of channels listed in "physicalChannel" (e.g. 1 for "Dev1/ao0")
task0.ao.minVal          = -10.0    // Volts
task0.ao.maxVal          =  10.0    // Volts

; Digital input channel params.
task0.di.line           = Dev1/port1/line0

; Digital input channel params.
task0.do.line           = Dev1/port1/line2

; Counter: period of a digital signal
task0.ci_period.counter  = Dev1/ctr0
task0.ci_period.minVal   = 0   // The minimum value, in units, that you
expect to measure. task0.ci_period.maxVal   = 0   // The minimum value, in
units, that you expect to measure. task0.ci_period.units    =
DAQmx_Val_Seconds | DAQmx_Val_Ticks  // One of these strings
task0.ci_period.edge     = DAQmx_Val_Rising | DAQmx_Val_Falling // One of
these strings
task0.ci_period.measTime = 0   // NI says: "Always pass 0 for this
parameter." task0.ci_period.divisor  = 1   // NI says: "Always pass 1 for
this parameter."

; Counter: count the number of rising or falling edges of a digital signal
task0.ci_count_edges.counter        = Dev1/ctr0
task0.ci_count_edges.edge           = DAQmx_Val_Rising | DAQmx_Val_Falling //
One of these strings
task0.ci_count_edges.initialCount   = 0    // The value from which to start
counting
task0.ci_count_edges.countDirection = DAQmx_Val_CountUp | DAQmx_Val_CountDown
| DAQmx_Val_ExtControlled  // One of these strings

; Counter:  measure the width of a digital pulse
task0.ci_pulse_width.counter      = Dev1/ctr0
task0.ci_pulse_width.minVal       = 0   // The minimum value, in units, that
you expect to measure.
task0.ci_pulse_width.maxVal       = 0   // The minimum value, in units, that
you expect to measure.
task0.ci_pulse_width.units        = DAQmx_Val_Seconds | DAQmx_Val_Ticks  //
One of these strings
task0.ci_pulse_width.startingEdge = DAQmx_Val_Rising | DAQmx_Val_Falling //
One of these strings

; Counter:  uses a linear encoder to measure linear position
task0.ci_lin_encoder.counter      = Dev1/ctr0
task0.ci_lin_encoder.decodingType = DAQmx_Val_X1 | DAQmx_Val_X2 |
DAQmx_Val_X4 | DAQmx_Val_TwoPulseCounting // One of these strings
task0.ci_lin_encoder.ZidxEnable   = false | true | 0 | 1    //  enable z
indexing?
task0.ci_lin_encoder.ZidxVal      = 0 // The value, in units, to which to
reset the measurement when signal Z is high and signal A and signal B are at
the states you specify with ZidxPhase.
task0.ci_lin_encoder.ZidxPhase    = DAQmx_Val_AHighBHigh |
DAQmx_Val_AHighBLow | DAQmx_Val_ALowBHigh | DAQmx_Val_ALowBLow  // One of
these strings task0.ci_lin_encoder.units        = DAQmx_Val_Meters |
DAQmx_Val_Inches | DAQmx_Val_Ticks  // One of these strings
task0.ci_lin_encoder.distPerPulse = 0.1  // The distance measured for each
pulse the encoder generates. Specify this value in units.
task0.ci_lin_encoder.initialPos   = 0.0 // The position of the encoder when
the measurement begins. This value is in units.

; Counter:  uses an angular encoder to measure angular position
task0.ci_ang_encoder.counter      = Dev1/ctr0
task0.ci_ang_encoder.decodingType = DAQmx_Val_X1 | DAQmx_Val_X2 |
DAQmx_Val_X4 | DAQmx_Val_TwoPulseCounting // One of these strings
task0.ci_ang_encoder.ZidxEnable   = 0 | 1 | false | true  //  enable z
indexing
task0.ci_ang_encoder.ZidxVal      = 0 // The value, in units, to which to
reset the measurement when signal Z is high and signal A and signal B are at
the states you specify with ZidxPhase.
task0.ci_ang_encoder.ZidxPhase    = DAQmx_Val_AHighBHigh |
DAQmx_Val_AHighBLow | DAQmx_Val_ALowBHigh | DAQmx_Val_ALowBLow  // One of
these strings task0.ci_ang_encoder.units        = DAQmx_Val_Degrees |
DAQmx_Val_Radians | DAQmx_Val_Ticks  // One of these strings
task0.ci_ang_encoder.pulsesPerRev = 512  // The number of pulses the encoder
generates per revolution.
task0.ci_ang_encoder.initialAngle = 0.0 // The position of the encoder when
the measurement begins. This value is in units.
task0.ci_ang_encoder.decimate     = 1   // Grab 1 out of N readings

; Output digital pulses:
task0.co_pulses.counter           = Dev1/ctr1
task0.co_pulses.idleState         = DAQmx_Val_High | DAQmx_Val_Low
task0.co_pulses.initialDelay      = 0  // The amount of time in seconds to
wait before generating the first pulse.
task0.co_pulses.freq              = 100 // The frequency of the pulses to
generate (Hertz)
task0.co_pulses.dutyCycle         = 0.5  // The width of the pulse divided by
the pulse period.

See also:

Go to http://ni.com and download the “DAQmx Base” package for your OS. Install following NI’s instructions. As of 2013, the latest version is 3.7.

This class requires compiling MRPT with support for “NI DAQmx” or “NI DAQmx Base”. While compiling MRPT, check the “MRPT_HAS_NI_DAQmx”/”MRPT_HAS_NI_DAQmxBASE” option and correctly set the new variables to the library include directory and library file.

As of 2013, NI seems not to support compiling 64bit programs, so you can must build MRPT for 32bits if you need this class.

#include <mrpt/hwdrivers/CNationalInstrumentsDAQ.h>

class CNationalInstrumentsDAQ:
    public mrpt::system::COutputLogger,
    public mrpt::hwdrivers::CGenericSensor
{
public:
    // structs

    struct TInfoPerTask;
    struct TaskDescription;

    //
fields

    std::vector<TaskDescription> task_definitions;

    //
methods

    virtual void initialize();
    void stop();
    virtual void doProcess();
    void readFromDAQ(std::vector<mrpt::obs::CObservationRawDAQ::Ptr>& outObservations, bool& hardwareError);

    void writeAnalogOutputTask(
        size_t task_index,
        size_t nSamplesPerChannel,
        const double* volt_values,
        double timeout,
        bool groupedByChannel
        );

    void writeDigitalOutputTask(size_t task_index, bool line_value, double timeout);
    bool checkDAQIsWorking() const;
};

Inherited Members

public:
    // structs

    struct TMsg;

    //
methods

    CGenericSensor& operator = (const CGenericSensor&);
    virtual void doProcess() = 0;

Fields

std::vector<TaskDescription> task_definitions

Publicly accessible vector with the list of tasks to be launched upon call to CNationalInstrumentsDAQ::initialize().

Changing these while running will have no effects.

Methods

virtual void initialize()

Setup and launch the DAQ tasks, in parallel threads.

Access to grabbed data with CNationalInstrumentsDAQ::readFromDAQ() or the standard CGenericSensor::doProcess()

void stop()

Stop the grabbing threads for DAQ tasks.

It is automatically called at destruction.

virtual void doProcess()

This method will be invoked at a minimum rate of “process_rate” (Hz)

Parameters:

This

method must throw an exception with a descriptive message if some critical error is found.

void readFromDAQ(std::vector<mrpt::obs::CObservationRawDAQ::Ptr>& outObservations, bool& hardwareError)

Receives data from the DAQ thread(s).

It returns a maximum number of one observation object per running grabber threads, that is, per each DAQmx “task”. This method MUST BE CALLED in a timely fashion by the user to allow the proccessing of incoming data. It can be run in a different thread safely. This is internally called when using the alternative CGenericSensor::doProcess() interface. No observations may be returned if there are not samples enough yet from any task.

void writeAnalogOutputTask(
    size_t task_index,
    size_t nSamplesPerChannel,
    const double* volt_values,
    double timeout,
    bool groupedByChannel
    )

Set voltage outputs to all the outputs in an AOUT task For the meaning of parameters, refere to NI DAQmx docs for DAQmxBaseWriteAnalogF64()

The number of samples in volt_values must match the number of channels in the task when it was initiated.

void writeDigitalOutputTask(size_t task_index, bool line_value, double timeout)

Changes the boolean state of one digital output line.

For the meaning of parameters, refere to NI DAQmx docs for DAQmxBaseWriteAnalogF64() The number of samples in volt_values must match the number of channels in the task when it was initiated.

bool checkDAQIsWorking() const

Returns true if initialize() was called and at least one task is running.

Returns true if initialize() was called successfully.