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