Main MRPT website > C++ reference for MRPT 1.9.9
CNationalInstrumentsDAQ.h
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2018, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #ifndef CNationalInstrumentsDAQ_H
11 #define CNationalInstrumentsDAQ_H
12 
16 #include <mrpt/io/CPipe.h>
17 
18 #include <list>
19 #include <memory>
20 #include <thread>
21 #include <atomic>
22 
23 namespace mrpt
24 {
25 namespace hwdrivers
26 {
27 /** An interface to read from data acquisition boards compatible with National
28 * Instruments "DAQmx Base" or "DAQmx".
29 * Refer to DAQmx Base C API reference online to learn more on the concepts of
30 * "channels", "tasks" (which in this MRPT class
31 * are mapped to independent grabbing threads), etc.
32 * If both DAQmx and DAQmxBase are installed in the system, DAQmx will be used.
33 * This class API isolate the user from the usage of one or another specific
34 * library.
35 *
36 * This class can be used as a sensor from the application "rawlog-grabber", or
37 * directly as a C++ class from a user program.
38 * Refer to the example: [MRPT]/samples/NIDAQ_test
39 *
40 * Samples will be returned inside mrpt::obs::CObservationRawDAQ in "packets" of
41 * a predefined number of samples, which can
42 * be changed by the user through the "samplesPerChannelToRead" parameter of
43 * each task.
44 *
45 * For multichannels tasks, samples will be **interleaved**. For example, the
46 * readings from successive timesteps for 4 ADC channels
47 * will be available in the ADC vector inside mrpt::obs::CObservationRawDAQ in
48 * this order:
49 *
50 * - A0[0] A1[0] A2[0] A3[0] A0[1] A1[1] A2[1] A3[1] A0[2] A1[2] A2[2] A3[2]
51 * ...
52 *
53 * The sensor label (field "m_sensorLabel") of each grabbed observation will be
54 * the concatenation of this class sensor label,
55 * a dot (".") and the task label (default="task###", with ### the task index).
56 *
57 * \code
58 * PARAMETERS IN THE ".INI"-LIKE CONFIGURATION STRINGS:
59 * -------------------------------------------------------
60 * [supplied_section_name]
61 * ; Number of tasks (each will run in a thread). Task indices are 0-based.
62 * ; (Parameters below follow NIs DAQmx API notation)
63 * num_tasks = 1
64 *
65 * ; Channels, separated by commas if more than one.
66 * ; - "ai": Analog inputs
67 * ; - "ao": Analog outputs
68 * ; - "di": Digital inputs
69 * ; - "do": Digital inputs
70 * ; - "ci_period",
71 * ; "ci_count_edges", "ci_pulse_width",
72 * ; "ci_lin_encoder", "ci_ang_encoder" : Counters & encoders (WARNING: NI
73 * says "a task can include only one counter input channel")
74 * ; - "co_pulses": Output digital pulses (WARNING: NI says "a task can include
75 * only one counter output channel")
76 * ;
77 * task0.channels = ai //, ao, di, do, ci_ang_encoder
78 * ;task0.taskLabel= MY_LABEL // Optional textual label to build the
79 * mrpt::obs::CObservation sensor label (default: task number)
80 * task0.samplesPerSecond = 1000 // Samples per second. Continuous (infinite)
81 * sampling is assumed.
82 * task0.samplesPerChannelToRead = 1000 // The number of samples to grab at once
83 * from each channel.
84 * ;task0.bufferSamplesPerChannel = 200000 // Increase if you have errors about "
85 * Onboard device memory overflow.(...)"
86 *
87 * ; Analog input channel params.
88 * task0.ai.physicalChannel = Dev1/ai0:3, Dev1/ai6
89 * task0.ai.physicalChannelCount = 5 // *IMPORTANT* This must be the total
90 * number of channels listed in "physicalChannel" (e.g. 4 for "Dev1/ai0:3")
91 * task0.ai.terminalConfig = DAQmx_Val_Cfg_Default | DAQmx_Val_RSE |
92 * DAQmx_Val_NRSE | DAQmx_Val_Diff // One of these strings
93 * task0.ai.minVal = -10.0 // Volts
94 * task0.ai.maxVal = 10.0 // Volts
95 *
96 * ; Analog output channel params.
97 * task0.ao.physicalChannel = Dev1/ao0, Dev1/ao2:4
98 * task0.ao.physicalChannelCount = 4 // *IMPORTANT* This must be the total
99 * number of channels listed in "physicalChannel" (e.g. 1 for "Dev1/ao0")
100 * task0.ao.minVal = -10.0 // Volts
101 * task0.ao.maxVal = 10.0 // Volts
102 *
103 * ; Digital input channel params.
104 * task0.di.line = Dev1/port1/line0
105 *
106 * ; Digital input channel params.
107 * task0.do.line = Dev1/port1/line2
108 *
109 * ; Counter: period of a digital signal
110 * task0.ci_period.counter = Dev1/ctr0
111 * task0.ci_period.minVal = 0 // The minimum value, in units, that you expect
112 * to measure.
113 * task0.ci_period.maxVal = 0 // The minimum value, in units, that you expect
114 * to measure.
115 * task0.ci_period.units = DAQmx_Val_Seconds | DAQmx_Val_Ticks // One of
116 * these strings
117 * task0.ci_period.edge = DAQmx_Val_Rising | DAQmx_Val_Falling // One of
118 * these strings
119 * task0.ci_period.measTime = 0 // NI says: "Always pass 0 for this parameter."
120 * task0.ci_period.divisor = 1 // NI says: "Always pass 1 for this parameter."
121 *
122 * ; Counter: count the number of rising or falling edges of a digital signal
123 * task0.ci_count_edges.counter = Dev1/ctr0
124 * task0.ci_count_edges.edge = DAQmx_Val_Rising | DAQmx_Val_Falling //
125 * One of these strings
126 * task0.ci_count_edges.initialCount = 0 // The value from which to start
127 * counting
128 * task0.ci_count_edges.countDirection = DAQmx_Val_CountUp | DAQmx_Val_CountDown
129 * | DAQmx_Val_ExtControlled // One of these strings
130 *
131 * ; Counter: measure the width of a digital pulse
132 * task0.ci_pulse_width.counter = Dev1/ctr0
133 * task0.ci_pulse_width.minVal = 0 // The minimum value, in units, that
134 * you expect to measure.
135 * task0.ci_pulse_width.maxVal = 0 // The minimum value, in units, that
136 * you expect to measure.
137 * task0.ci_pulse_width.units = DAQmx_Val_Seconds | DAQmx_Val_Ticks //
138 * One of these strings
139 * task0.ci_pulse_width.startingEdge = DAQmx_Val_Rising | DAQmx_Val_Falling //
140 * One of these strings
141 *
142 * ; Counter: uses a linear encoder to measure linear position
143 * task0.ci_lin_encoder.counter = Dev1/ctr0
144 * task0.ci_lin_encoder.decodingType = DAQmx_Val_X1 | DAQmx_Val_X2 | DAQmx_Val_X4
145 * | DAQmx_Val_TwoPulseCounting // One of these strings
146 * task0.ci_lin_encoder.ZidxEnable = false | true | 0 | 1 // enable z
147 * indexing?
148 * task0.ci_lin_encoder.ZidxVal = 0 // The value, in units, to which to
149 * reset the measurement when signal Z is high and signal A and signal B are at
150 * the states you specify with ZidxPhase.
151 * task0.ci_lin_encoder.ZidxPhase = DAQmx_Val_AHighBHigh | DAQmx_Val_AHighBLow
152 * | DAQmx_Val_ALowBHigh | DAQmx_Val_ALowBLow // One of these strings
153 * task0.ci_lin_encoder.units = DAQmx_Val_Meters | DAQmx_Val_Inches |
154 * DAQmx_Val_Ticks // One of these strings
155 * task0.ci_lin_encoder.distPerPulse = 0.1 // The distance measured for each
156 * pulse the encoder generates. Specify this value in units.
157 * task0.ci_lin_encoder.initialPos = 0.0 // The position of the encoder when
158 * the measurement begins. This value is in units.
159 *
160 * ; Counter: uses an angular encoder to measure angular position
161 * task0.ci_ang_encoder.counter = Dev1/ctr0
162 * task0.ci_ang_encoder.decodingType = DAQmx_Val_X1 | DAQmx_Val_X2 | DAQmx_Val_X4
163 * | DAQmx_Val_TwoPulseCounting // One of these strings
164 * task0.ci_ang_encoder.ZidxEnable = 0 | 1 | false | true // enable z
165 * indexing
166 * task0.ci_ang_encoder.ZidxVal = 0 // The value, in units, to which to
167 * reset the measurement when signal Z is high and signal A and signal B are at
168 * the states you specify with ZidxPhase.
169 * task0.ci_ang_encoder.ZidxPhase = DAQmx_Val_AHighBHigh | DAQmx_Val_AHighBLow
170 * | DAQmx_Val_ALowBHigh | DAQmx_Val_ALowBLow // One of these strings
171 * task0.ci_ang_encoder.units = DAQmx_Val_Degrees | DAQmx_Val_Radians |
172 * DAQmx_Val_Ticks // One of these strings
173 * task0.ci_ang_encoder.pulsesPerRev = 512 // The number of pulses the encoder
174 * generates per revolution.
175 * task0.ci_ang_encoder.initialAngle = 0.0 // The position of the encoder when
176 * the measurement begins. This value is in units.
177 * task0.ci_ang_encoder.decimate = 1 // Grab 1 out of N readings
178 *
179 * ; Output digital pulses:
180 * task0.co_pulses.counter = Dev1/ctr1
181 * task0.co_pulses.idleState = DAQmx_Val_High | DAQmx_Val_Low
182 * task0.co_pulses.initialDelay = 0 // The amount of time in seconds to
183 * wait before generating the first pulse.
184 * task0.co_pulses.freq = 100 // The frequency of the pulses to
185 * generate (Hertz)
186 * task0.co_pulses.dutyCycle = 0.5 // The width of the pulse divided by
187 * the pulse period.
188 * \endcode
189 *
190 * See also:
191 * - [MRPT]/samples/NIDAQ_test
192 * - Sample .ini files for rawlog-grabber in
193 * [MRPT]/share/mrpt/config_files/rawlog-grabber/
194 * - NI DAQmx C reference:
195 * http://others-help.mrpt.org/ni-daqmx_c_reference_help/
196 * - NI DAQmx Base 3.x C reference:
197 * http://others-help.mrpt.org/ni-daqmx_base_3.x_c_function_reference/
198 *
199 * DAQmx Base Installation
200 * ------------------------
201 * Go to http://ni.com and download the "DAQmx Base" package for your OS. Install
202 * following NI's instructions.
203 * As of 2013, the latest version is 3.7.
204 *
205 * \note This class requires compiling MRPT with support for "NI DAQmx" or "NI
206 * DAQmx Base". While compiling MRPT,
207 * check the "MRPT_HAS_NI_DAQmx"/"MRPT_HAS_NI_DAQmxBASE" option and
208 * correctly set the new variables to
209 * the library include directory and library file.
210 *
211 * \note As of 2013, NI seems not to support compiling 64bit programs, so you can
212 * must build MRPT for 32bits if you need this class.
213 *
214 * \ingroup mrpt_hwdrivers_grp
215 */
217  public CGenericSensor
218 {
220  public:
221  /** Constructor */
223 
224  /** Destructor */
225  virtual ~CNationalInstrumentsDAQ();
226 
227  /** Setup and launch the DAQ tasks, in parallel threads.
228  * Access to grabbed data with CNationalInstrumentsDAQ::readFromDAQ() or
229  * the standard CGenericSensor::doProcess() */
230  virtual void initialize();
231 
232  /** Stop the grabbing threads for DAQ tasks. It is automatically called at
233  * destruction. */
234  void stop();
235 
236  // See docs in parent class
237  void doProcess();
238 
239  /** Receives data from the DAQ thread(s). It returns a maximum number of one
240  * observation object per running grabber threads, that is, per each DAQmx
241  * "task".
242  * This method MUST BE CALLED in a timely fashion by the user to allow the
243  * proccessing of incoming data. It can be run in a different thread safely.
244  * This is internally called when using the alternative
245  * CGenericSensor::doProcess() interface.
246  * No observations may be returned if there are not samples enough yet
247  * from any task.
248  */
249  void readFromDAQ(
250  std::vector<mrpt::obs::CObservationRawDAQ::Ptr>& outObservations,
251  bool& hardwareError);
252 
253  /** Set voltage outputs to all the outputs in an AOUT task
254  * For the meaning of parameters, refere to NI DAQmx docs for
255  * DAQmxBaseWriteAnalogF64()
256  * \note The number of samples in \a volt_values must match the number of
257  * channels in the task when it was initiated.
258  */
260  size_t task_index, size_t nSamplesPerChannel, const double* volt_values,
261  double timeout, bool groupedByChannel);
262 
263  /** Changes the boolean state of one digital output line.
264  * For the meaning of parameters, refere to NI DAQmx docs for
265  * DAQmxBaseWriteAnalogF64()
266  * \note The number of samples in \a volt_values must match the number of
267  * channels in the task when it was initiated.
268  */
270  size_t task_index, bool line_value, double timeout);
271 
272  /** Returns true if initialize() was called and at least one task is
273  * running. */
274  bool checkDAQIsWorking() const;
275 
276  /** Each of the tasks to create in CNationalInstrumentsDAQ::initialize().
277  * Refer to the docs on config file formats of
278  * mrpt::hwdrivers::CNationalInstrumentsDAQ to learn on the meaning
279  * of each field. Also, see National Instruments' DAQmx API docs online.
280  */
282  {
283  TaskDescription();
284 
288 
289  /** Sample clock config: samples per second. Continuous (infinite)
290  * sampling is assumed. */
292  /** Sample clock source: may be empty (default value) for some channels.
293  */
295  /** (Default=0) From NI's docs: The number of samples the buffer can
296  * hold for each channel in the task. Zero indicates no buffer should be
297  * allocated. Use a buffer size of 0 to perform a hardware-timed
298  * operation without using a buffer. */
300  /** (Default=1000) The number of samples to grab at once from each
301  * channel. */
303  /** (Default="task###") */
305 
306  struct desc_ai_t
307  {
309  : terminalConfig("DAQmx_Val_NRSE"),
310  minVal(-10),
311  maxVal(10),
313  {
314  }
315 
317  double minVal, maxVal;
318  /** *IMPORTANT* This must be the total number of channels listed in
319  * "physicalChannel" (e.g. 4 for "Dev1/ai0:3") */
320  unsigned int physicalChannelCount;
321  }
322  /** Analog inputs */
323  ai;
324 
325  struct desc_ao_t
326  {
329  /** *IMPORTANT* This must be the total number of channels listed in
330  * "physicalChannel" (e.g. 1 for "Dev1/ao0") */
331  unsigned int physicalChannelCount;
332  double minVal, maxVal;
333  }
334  /** Analog outputs */
335  ao;
336 
337  struct desc_di_t
338  {
339  /** The digital line (for example "Dev1/port0/line1") */
341  }
342  /** Digital inputs (di) */
343  di;
344 
345  struct desc_do_t
346  {
347  /** The digital line (for example "Dev1/port0/line1") */
349  }
350  /** Digital outs (do) */
351  douts;
352 
354  {
356  {
357  }
358 
360  double minVal, maxVal;
361  double measTime;
362  int divisor;
363  }
364  /** Counter: period of a digital signal */
365  ci_period;
366 
368  {
370  : countDirection("DAQmx_Val_CountUp"), initialCount(0)
371  {
372  }
373 
376  }
377  /** Counter: period of a digital signal */
379 
381  {
384  double minVal, maxVal;
385  }
386  /** Counter: measure the width of a digital pulse */
388 
390  {
392  : ZidxEnable(false),
393  ZidxVal(0),
394  distPerPulse(0.1),
395  initialPos(0)
396  {
397  }
398 
401  double ZidxVal;
402  double distPerPulse;
403  double initialPos;
404  }
405  /** Counter: uses a linear encoder to measure linear position */
407 
409  {
411  : ZidxEnable(false),
412  ZidxVal(0),
413  pulsesPerRev(512),
414  initialAngle(0),
415  decimate(1),
416  decimate_cnt(0)
417  {
418  }
419 
422  double ZidxVal;
424  double initialAngle;
426  }
427  /** Counter: uses an angular encoder to measure angular position */
429 
431  {
433  : idleState("DAQmx_Val_Low"),
434  initialDelay(0),
435  freq(1000),
436  dutyCycle(0.5)
437  {
438  }
439 
442  }
443  /** Output counter: digital pulses output */
444  co_pulses;
445 
446  }; // end of TaskDescription
447 
448  /** Publicly accessible vector with the list of tasks to be launched upon
449  * call to CNationalInstrumentsDAQ::initialize().
450  * Changing these while running will have no effects.
451  */
452  std::vector<TaskDescription> task_definitions;
453 
454  protected:
455  /** See the class documentation at the top for expected parameters */
457  const mrpt::config::CConfigFileBase& configSource,
458  const std::string& iniSection);
459 
460  private:
461  /** A buffer for doProcess */
462  std::vector<mrpt::obs::CObservationRawDAQ::Ptr> m_nextObservations;
463 
465  {
466  TInfoPerTask();
467 
468  void* taskHandle;
469  std::thread hThread;
470 
471  std::unique_ptr<mrpt::io::CPipeReadEndPoint> read_pipe;
472  std::unique_ptr<mrpt::io::CPipeWriteEndPoint> write_pipe;
473 
475  std::atomic<int> new_obs_available;
476 
477  /** A copy of the original task description that generated this thread.
478  */
480  };
481 
482  std::list<TInfoPerTask> m_running_tasks;
483 
484  /** Method to be executed in each parallel thread. */
485  void grabbing_thread(TInfoPerTask& ipt);
486 
487 }; // end class
488 
489 } // end namespace
490 } // end namespace
491 
492 #endif
A generic interface for a wide-variety of sensors designed to be used in the application RawLogGrabbe...
Each of the tasks to create in CNationalInstrumentsDAQ::initialize().
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_ang_encoder_t ci_ang_encoder
Counter: uses an angular encoder to measure angular position.
virtual void initialize()
Setup and launch the DAQ tasks, in parallel threads.
std::string line
The digital line (for example "Dev1/port0/line1")
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_co_pulses_t co_pulses
Output counter: digital pulses output.
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_do_t douts
Digital outs (do)
bool checkDAQIsWorking() const
Returns true if initialize() was called and at least one task is running.
TaskDescription task
A copy of the original task description that generated this thread.
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_count_edges_t ci_count_edges
Counter: period of a digital signal.
This class allows loading and storing values and vectors of different types from a configuration text...
std::string sampleClkSource
Sample clock source: may be empty (default value) for some channels.
void writeDigitalOutputTask(size_t task_index, bool line_value, double timeout)
Changes the boolean state of one digital output line.
Versatile class for consistent logging and management of output messages.
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_di_t di
Digital inputs (di)
void stop()
Stop the grabbing threads for DAQ tasks.
void grabbing_thread(TInfoPerTask &ipt)
Method to be executed in each parallel thread.
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_pulse_width_t ci_pulse_width
Counter: measure the width of a digital pulse.
An interface to read from data acquisition boards compatible with National Instruments "DAQmx Base" o...
GLsizei const GLchar ** string
Definition: glext.h:4101
std::vector< mrpt::obs::CObservationRawDAQ::Ptr > m_nextObservations
A buffer for doProcess.
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()
uint32_t bufferSamplesPerChannel
(Default=0) From NI&#39;s docs: The number of samples the buffer can hold for each channel in the task...
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_lin_encoder_t ci_lin_encoder
Counter: uses a linear encoder to measure linear position.
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ai_t ai
Analog inputs.
void readFromDAQ(std::vector< mrpt::obs::CObservationRawDAQ::Ptr > &outObservations, bool &hardwareError)
Receives data from the DAQ thread(s).
std::string line
The digital line (for example "Dev1/port0/line1")
#define DEFINE_GENERIC_SENSOR(class_name)
This declaration must be inserted in all CGenericSensor classes definition, within the class declarat...
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
uint32_t samplesPerChannelToRead
(Default=1000) The number of samples to grab at once from each channel.
std::vector< TaskDescription > task_definitions
Publicly accessible vector with the list of tasks to be launched upon call to CNationalInstrumentsDAQ...
void doProcess()
This method will be invoked at a minimum rate of "process_rate" (Hz)
void loadConfig_sensorSpecific(const mrpt::config::CConfigFileBase &configSource, const std::string &iniSection)
See the class documentation at the top for expected parameters.
unsigned int physicalChannelCount
IMPORTANT This must be the total number of channels listed in "physicalChannel" (e.g.
std::unique_ptr< mrpt::io::CPipeWriteEndPoint > write_pipe
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ao_t ao
Analog outputs.
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_period_t ci_period
Counter: period of a digital signal.
unsigned int physicalChannelCount
IMPORTANT This must be the total number of channels listed in "physicalChannel" (e.g.
double samplesPerSecond
Sample clock config: samples per second.
std::unique_ptr< mrpt::io::CPipeReadEndPoint > read_pipe
unsigned __int32 uint32_t
Definition: rptypes.h:47



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ad3a9d8ae Tue May 1 23:10:22 2018 -0700 at lun oct 28 00:14:14 CET 2019