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-2017, 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/synch/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 */
216 class CNationalInstrumentsDAQ : public mrpt::utils::COutputLogger,
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) */
352 
354  {
356  {
357  }
358 
360  double minVal, maxVal;
361  double measTime;
362  int divisor;
363  }
364  /** Counter: period of a digital signal */
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 */
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::utils::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::synch::CPipeReadEndPoint> read_pipe;
472  std::unique_ptr<mrpt::synch::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
#define DEFINE_GENERIC_SENSOR(class_name)
This declaration must be inserted in all CGenericSensor classes definition, within the class declarat...
A generic interface for a wide-variety of sensors designed to be used in the application RawLogGrabbe...
An interface to read from data acquisition boards compatible with National Instruments "DAQmx Base" o...
void stop()
Stop the grabbing threads for DAQ tasks.
virtual void initialize()
Setup and launch the DAQ tasks, in parallel threads.
void loadConfig_sensorSpecific(const mrpt::utils::CConfigFileBase &configSource, const std::string &iniSection)
See the class documentation at the top for expected parameters.
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,...
bool checkDAQIsWorking() const
Returns true if initialize() was called and at least one task is running.
void readFromDAQ(std::vector< mrpt::obs::CObservationRawDAQ::Ptr > &outObservations, bool &hardwareError)
Receives data from the DAQ thread(s).
void writeDigitalOutputTask(size_t task_index, bool line_value, double timeout)
Changes the boolean state of one digital output line.
std::vector< TaskDescription > task_definitions
Publicly accessible vector with the list of tasks to be launched upon call to CNationalInstrumentsDAQ...
void grabbing_thread(TInfoPerTask &ipt)
Method to be executed in each parallel thread.
void doProcess()
This method will be invoked at a minimum rate of "process_rate" (Hz)
This class allows loading and storing values and vectors of different types from a configuration text...
GLsizei const GLchar ** string
Definition: glext.h:4101
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
unsigned __int32 uint32_t
Definition: rptypes.h:47
std::unique_ptr< mrpt::synch::CPipeReadEndPoint > read_pipe
TaskDescription task
A copy of the original task description that generated this thread.
std::unique_ptr< mrpt::synch::CPipeWriteEndPoint > write_pipe
unsigned int physicalChannelCount
IMPORTANT This must be the total number of channels listed in "physicalChannel" (e....
unsigned int physicalChannelCount
IMPORTANT This must be the total number of channels listed in "physicalChannel" (e....
std::string line
The digital line (for example "Dev1/port0/line1")
std::string line
The digital line (for example "Dev1/port0/line1")
Each of the tasks to create in CNationalInstrumentsDAQ::initialize().
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_count_edges_t ci_count_edges
Counter: period of a digital signal.
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_co_pulses_t co_pulses
Output counter: digital pulses output.
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_pulse_width_t ci_pulse_width
Counter: measure the width of a digital pulse.
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ai_t ai
Analog inputs.
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_lin_encoder_t ci_lin_encoder
Counter: uses a linear encoder to measure linear position.
double samplesPerSecond
Sample clock config: samples per second.
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ao_t ao
Analog outputs.
uint32_t samplesPerChannelToRead
(Default=1000) The number of samples to grab at once from each channel.
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_ang_encoder_t ci_ang_encoder
Counter: uses an angular encoder to measure angular position.
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_di_t di
Digital inputs (di)
uint32_t bufferSamplesPerChannel
(Default=0) From NI's docs: The number of samples the buffer can hold for each channel in the task.
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_period_t ci_period
Counter: period of a digital signal.
std::string sampleClkSource
Sample clock source: may be empty (default value) for some channels.
struct mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_do_t douts
Digital outs (do)



Page generated by Doxygen 1.9.1 for MRPT 1.9.9 Git: 63ea9d1f1 Thu Nov 23 00:06:53 2017 +0100 at mar 26 may 2026 12:19:29 CEST