Main MRPT website > C++ reference for MRPT 1.5.7
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 #include <mrpt/system/threads.h>
18 #include <list>
19 #include <memory>
20 #include <atomic>
21 
22 namespace mrpt
23 {
24  namespace hwdrivers
25  {
26  /** An interface to read from data acquisition boards compatible with National Instruments "DAQmx Base" or "DAQmx".
27  * Refer to DAQmx Base C API reference online to learn more on the concepts of "channels", "tasks" (which in this MRPT class
28  * are mapped to independent grabbing threads), etc.
29  * 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.
30  *
31  * This class can be used as a sensor from the application "rawlog-grabber", or directly as a C++ class from a user program.
32  * Refer to the example: [MRPT]/samples/NIDAQ_test
33  *
34  * Samples will be returned inside mrpt::obs::CObservationRawDAQ in "packets" of a predefined number of samples, which can
35  * be changed by the user through the "samplesPerChannelToRead" parameter of each task.
36  *
37  * For multichannels tasks, samples will be **interleaved**. For example, the readings from successive timesteps for 4 ADC channels
38  * will be available in the ADC vector inside mrpt::obs::CObservationRawDAQ in this order:
39  *
40  * - A0[0] A1[0] A2[0] A3[0] A0[1] A1[1] A2[1] A3[1] A0[2] A1[2] A2[2] A3[2] ...
41  *
42  * The sensor label (field "m_sensorLabel") of each grabbed observation will be the concatenation of this class sensor label,
43  * a dot (".") and the task label (default="task###", with ### the task index).
44  *
45  * \code
46  * PARAMETERS IN THE ".INI"-LIKE CONFIGURATION STRINGS:
47  * -------------------------------------------------------
48  * [supplied_section_name]
49  * ; Number of tasks (each will run in a thread). Task indices are 0-based.
50  * ; (Parameters below follow NIs DAQmx API notation)
51  * num_tasks = 1
52  *
53  * ; Channels, separated by commas if more than one.
54  * ; - "ai": Analog inputs
55  * ; - "ao": Analog outputs
56  * ; - "di": Digital inputs
57  * ; - "do": Digital inputs
58  * ; - "ci_period",
59  * ; "ci_count_edges", "ci_pulse_width",
60  * ; "ci_lin_encoder", "ci_ang_encoder" : Counters & encoders (WARNING: NI says "a task can include only one counter input channel")
61  * ; - "co_pulses": Output digital pulses (WARNING: NI says "a task can include only one counter output channel")
62  * ;
63  * task0.channels = ai //, ao, di, do, ci_ang_encoder
64  * ;task0.taskLabel= MY_LABEL // Optional textual label to build the mrpt::obs::CObservation sensor label (default: task number)
65  * task0.samplesPerSecond = 1000 // Samples per second. Continuous (infinite) sampling is assumed.
66  * task0.samplesPerChannelToRead = 1000 // The number of samples to grab at once from each channel.
67  * ;task0.bufferSamplesPerChannel = 200000 // Increase if you have errors about " Onboard device memory overflow.(...)"
68  *
69  * ; Analog input channel params.
70  * task0.ai.physicalChannel = Dev1/ai0:3, Dev1/ai6
71  * task0.ai.physicalChannelCount = 5 // *IMPORTANT* This must be the total number of channels listed in "physicalChannel" (e.g. 4 for "Dev1/ai0:3")
72  * task0.ai.terminalConfig = DAQmx_Val_Cfg_Default | DAQmx_Val_RSE | DAQmx_Val_NRSE | DAQmx_Val_Diff // One of these strings
73  * task0.ai.minVal = -10.0 // Volts
74  * task0.ai.maxVal = 10.0 // Volts
75  *
76  * ; Analog output channel params.
77  * task0.ao.physicalChannel = Dev1/ao0, Dev1/ao2:4
78  * task0.ao.physicalChannelCount = 4 // *IMPORTANT* This must be the total number of channels listed in "physicalChannel" (e.g. 1 for "Dev1/ao0")
79  * task0.ao.minVal = -10.0 // Volts
80  * task0.ao.maxVal = 10.0 // Volts
81  *
82  * ; Digital input channel params.
83  * task0.di.line = Dev1/port1/line0
84  *
85  * ; Digital input channel params.
86  * task0.do.line = Dev1/port1/line2
87  *
88  * ; Counter: period of a digital signal
89  * task0.ci_period.counter = Dev1/ctr0
90  * task0.ci_period.minVal = 0 // The minimum value, in units, that you expect to measure.
91  * task0.ci_period.maxVal = 0 // The minimum value, in units, that you expect to measure.
92  * task0.ci_period.units = DAQmx_Val_Seconds | DAQmx_Val_Ticks // One of these strings
93  * task0.ci_period.edge = DAQmx_Val_Rising | DAQmx_Val_Falling // One of these strings
94  * task0.ci_period.measTime = 0 // NI says: "Always pass 0 for this parameter."
95  * task0.ci_period.divisor = 1 // NI says: "Always pass 1 for this parameter."
96  *
97  * ; Counter: count the number of rising or falling edges of a digital signal
98  * task0.ci_count_edges.counter = Dev1/ctr0
99  * task0.ci_count_edges.edge = DAQmx_Val_Rising | DAQmx_Val_Falling // One of these strings
100  * task0.ci_count_edges.initialCount = 0 // The value from which to start counting
101  * task0.ci_count_edges.countDirection = DAQmx_Val_CountUp | DAQmx_Val_CountDown | DAQmx_Val_ExtControlled // One of these strings
102  *
103  * ; Counter: measure the width of a digital pulse
104  * task0.ci_pulse_width.counter = Dev1/ctr0
105  * task0.ci_pulse_width.minVal = 0 // The minimum value, in units, that you expect to measure.
106  * task0.ci_pulse_width.maxVal = 0 // The minimum value, in units, that you expect to measure.
107  * task0.ci_pulse_width.units = DAQmx_Val_Seconds | DAQmx_Val_Ticks // One of these strings
108  * task0.ci_pulse_width.startingEdge = DAQmx_Val_Rising | DAQmx_Val_Falling // One of these strings
109  *
110  * ; Counter: uses a linear encoder to measure linear position
111  * task0.ci_lin_encoder.counter = Dev1/ctr0
112  * task0.ci_lin_encoder.decodingType = DAQmx_Val_X1 | DAQmx_Val_X2 | DAQmx_Val_X4 | DAQmx_Val_TwoPulseCounting // One of these strings
113  * task0.ci_lin_encoder.ZidxEnable = false | true | 0 | 1 // enable z indexing?
114  * 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.
115  * task0.ci_lin_encoder.ZidxPhase = DAQmx_Val_AHighBHigh | DAQmx_Val_AHighBLow | DAQmx_Val_ALowBHigh | DAQmx_Val_ALowBLow // One of these strings
116  * task0.ci_lin_encoder.units = DAQmx_Val_Meters | DAQmx_Val_Inches | DAQmx_Val_Ticks // One of these strings
117  * task0.ci_lin_encoder.distPerPulse = 0.1 // The distance measured for each pulse the encoder generates. Specify this value in units.
118  * task0.ci_lin_encoder.initialPos = 0.0 // The position of the encoder when the measurement begins. This value is in units.
119  *
120  * ; Counter: uses an angular encoder to measure angular position
121  * task0.ci_ang_encoder.counter = Dev1/ctr0
122  * task0.ci_ang_encoder.decodingType = DAQmx_Val_X1 | DAQmx_Val_X2 | DAQmx_Val_X4 | DAQmx_Val_TwoPulseCounting // One of these strings
123  * task0.ci_ang_encoder.ZidxEnable = 0 | 1 | false | true // enable z indexing
124  * 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.
125  * task0.ci_ang_encoder.ZidxPhase = DAQmx_Val_AHighBHigh | DAQmx_Val_AHighBLow | DAQmx_Val_ALowBHigh | DAQmx_Val_ALowBLow // One of these strings
126  * task0.ci_ang_encoder.units = DAQmx_Val_Degrees | DAQmx_Val_Radians | DAQmx_Val_Ticks // One of these strings
127  * task0.ci_ang_encoder.pulsesPerRev = 512 // The number of pulses the encoder generates per revolution.
128  * task0.ci_ang_encoder.initialAngle = 0.0 // The position of the encoder when the measurement begins. This value is in units.
129  * task0.ci_ang_encoder.decimate = 1 // Grab 1 out of N readings
130  *
131  * ; Output digital pulses:
132  * task0.co_pulses.counter = Dev1/ctr1
133  * task0.co_pulses.idleState = DAQmx_Val_High | DAQmx_Val_Low
134  * task0.co_pulses.initialDelay = 0 // The amount of time in seconds to wait before generating the first pulse.
135  * task0.co_pulses.freq = 100 // The frequency of the pulses to generate (Hertz)
136  * task0.co_pulses.dutyCycle = 0.5 // The width of the pulse divided by the pulse period.
137  * \endcode
138  *
139  * See also:
140  * - [MRPT]/samples/NIDAQ_test
141  * - Sample .ini files for rawlog-grabber in [MRPT]/share/mrpt/config_files/rawlog-grabber/
142  * - NI DAQmx C reference: http://others-help.mrpt.org/ni-daqmx_c_reference_help/
143  * - NI DAQmx Base 3.x C reference: http://others-help.mrpt.org/ni-daqmx_base_3.x_c_function_reference/
144  *
145  * DAQmx Base Installation
146  * ------------------------
147  * Go to http://ni.com and download the "DAQmx Base" package for your OS. Install following NI's instructions.
148  * As of 2013, the latest version is 3.7.
149  *
150  * \note This class requires compiling MRPT with support for "NI DAQmx" or "NI DAQmx Base". While compiling MRPT,
151  * check the "MRPT_HAS_NI_DAQmx"/"MRPT_HAS_NI_DAQmxBASE" option and correctly set the new variables to
152  * the library include directory and library file.
153  *
154  * \note As of 2013, NI seems not to support compiling 64bit programs, so you can must build MRPT for 32bits if you need this class.
155  *
156  * \ingroup mrpt_hwdrivers_grp
157  */
158  class HWDRIVERS_IMPEXP CNationalInstrumentsDAQ : public mrpt::utils::COutputLogger, public CGenericSensor
159  {
161  public:
162  /** Constructor */
164 
165  /** Destructor */
166  virtual ~CNationalInstrumentsDAQ();
167 
168  /** Setup and launch the DAQ tasks, in parallel threads.
169  * Access to grabbed data with CNationalInstrumentsDAQ::readFromDAQ() or the standard CGenericSensor::doProcess() */
170  virtual void initialize();
171 
172  /** Stop the grabbing threads for DAQ tasks. It is automatically called at destruction. */
173  void stop();
174 
175  // See docs in parent class
176  void doProcess();
177 
178  /** 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".
179  * 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.
180  * This is internally called when using the alternative CGenericSensor::doProcess() interface.
181  * No observations may be returned if there are not samples enough yet from any task.
182  */
183  void readFromDAQ(
184  std::vector<mrpt::obs::CObservationRawDAQPtr> &outObservations,
185  bool & hardwareError );
186 
187  /** Set voltage outputs to all the outputs in an AOUT task
188  * For the meaning of parameters, refere to NI DAQmx docs for DAQmxBaseWriteAnalogF64()
189  * \note The number of samples in \a volt_values must match the number of channels in the task when it was initiated.
190  */
191  void writeAnalogOutputTask(size_t task_index, size_t nSamplesPerChannel, const double * volt_values, double timeout, bool groupedByChannel);
192 
193  /** Changes the boolean state of one digital output line.
194  * For the meaning of parameters, refere to NI DAQmx docs for DAQmxBaseWriteAnalogF64()
195  * \note The number of samples in \a volt_values must match the number of channels in the task when it was initiated.
196  */
197  void writeDigitalOutputTask(size_t task_index, bool line_value, double timeout);
198 
199  /** Returns true if initialize() was called and at least one task is running. */
200  bool checkDAQIsWorking() const;
201 
202 
203  /** Each of the tasks to create in CNationalInstrumentsDAQ::initialize().
204  * Refer to the docs on config file formats of mrpt::hwdrivers::CNationalInstrumentsDAQ to learn on the meaning
205  * of each field. Also, see National Instruments' DAQmx API docs online.
206  */
208  {
209  TaskDescription();
210 
211  bool has_ai, has_ao, has_di, has_do;
212  bool has_ci_period, has_ci_count_edges,has_ci_pulse_width,has_ci_lin_encoder,has_ci_ang_encoder, has_co_pulses;
213 
214 
215  double samplesPerSecond; //!< Sample clock config: samples per second. Continuous (infinite) sampling is assumed.
216  std::string sampleClkSource; //!< Sample clock source: may be empty (default value) for some channels.
217  uint32_t bufferSamplesPerChannel; //!< (Default=0) From NI's docs: The number of samples the buffer can hold for each channel in the task. Zero indicates no buffer should be allocated. Use a buffer size of 0 to perform a hardware-timed operation without using a buffer.
218  uint32_t samplesPerChannelToRead; //!< (Default=1000) The number of samples to grab at once from each channel.
219  std::string taskLabel; //!< (Default="task###")
220 
222  {
223  desc_ai_t() : terminalConfig("DAQmx_Val_NRSE"),minVal(-10), maxVal(10),physicalChannelCount(0) { }
224 
225  std::string physicalChannel, terminalConfig;
226  double minVal, maxVal;
227  unsigned int physicalChannelCount; //!< *IMPORTANT* This must be the total number of channels listed in "physicalChannel" (e.g. 4 for "Dev1/ai0:3")
228  }
229  ai; //!< Analog inputs
230 
232  {
233  desc_ao_t() : physicalChannelCount(0),minVal(-10), maxVal(10) { }
234 
236  unsigned int physicalChannelCount; //!< *IMPORTANT* This must be the total number of channels listed in "physicalChannel" (e.g. 1 for "Dev1/ao0")
237  double minVal, maxVal;
238  }
239  ao; //!< Analog outputs
240 
242  {
243  std::string line; //!< The digital line (for example "Dev1/port0/line1")
244  }
245  di; //!< Digital inputs (di)
246 
248  {
249  std::string line; //!< The digital line (for example "Dev1/port0/line1")
250  }
251  douts; //!< Digital outs (do)
252 
254  {
255  desc_ci_period_t() : minVal(0),maxVal(0),measTime(0),divisor(1) { }
256 
257  std::string counter, units, edge;
258  double minVal,maxVal;
259  double measTime;
260  int divisor;
261  }
262  ci_period; //!< Counter: period of a digital signal
263 
265  {
266  desc_ci_count_edges_t() : countDirection("DAQmx_Val_CountUp"),initialCount(0) { }
267 
270  }
271  ci_count_edges; //!< Counter: period of a digital signal
272 
274  {
275  desc_ci_pulse_width_t() : minVal(0),maxVal(0) { }
276 
277  std::string counter, units, startingEdge;
278  double minVal,maxVal;
279  }
280  ci_pulse_width; //!< Counter: measure the width of a digital pulse
281 
283  {
284  desc_ci_lin_encoder_t() : ZidxEnable(false),ZidxVal(0),distPerPulse(0.1),initialPos(0) { }
285 
286  std::string counter, decodingType, ZidxPhase,units;
288  double ZidxVal;
289  double distPerPulse;
290  double initialPos;
291  }
292  ci_lin_encoder; //!< Counter: uses a linear encoder to measure linear position
293 
295  {
296  desc_ci_ang_encoder_t() : ZidxEnable(false),ZidxVal(0),pulsesPerRev(512),initialAngle(0),decimate(1),decimate_cnt(0) { }
297 
298  std::string counter, decodingType, ZidxPhase,units;
300  double ZidxVal;
302  double initialAngle;
303  int decimate, decimate_cnt;
304  }
305  ci_ang_encoder; //!< Counter: uses an angular encoder to measure angular position
306 
308  {
309  desc_co_pulses_t() : idleState("DAQmx_Val_Low"),initialDelay(0),freq(1000),dutyCycle(0.5) { }
310 
311  std::string counter, idleState;
312  double initialDelay,freq,dutyCycle;
313  }
314  co_pulses; //!< Output counter: digital pulses output
315 
316  }; // end of TaskDescription
317 
318  /** Publicly accessible vector with the list of tasks to be launched upon call to CNationalInstrumentsDAQ::initialize().
319  * Changing these while running will have no effects.
320  */
321  std::vector<TaskDescription> task_definitions;
322 
323  protected:
324  /** See the class documentation at the top for expected parameters */
325  void loadConfig_sensorSpecific(
326  const mrpt::utils::CConfigFileBase &configSource,
327  const std::string &iniSection );
328 
329  private:
330  std::vector<mrpt::obs::CObservationRawDAQPtr> m_nextObservations; //!< A buffer for doProcess
331 
333  {
334  TInfoPerTask();
335 
336  void * taskHandle;
338  std::unique_ptr<mrpt::synch::CPipeReadEndPoint> read_pipe;
339  std::unique_ptr<mrpt::synch::CPipeWriteEndPoint> write_pipe;
340  bool must_close, is_closed;
341  std::atomic<long> new_obs_available;
342 
343  TaskDescription task; //!< A copy of the original task description that generated this thread.
344  };
345 
346  std::list<TInfoPerTask> m_running_tasks;
347 
348  /** Method to be executed in each parallel thread. */
349  void grabbing_thread(TInfoPerTask &ipt);
350 
351  }; // end class
352 
353  } // end namespace
354 } // end namespace
355 
356 #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...
std::vector< mrpt::obs::CObservationRawDAQPtr > m_nextObservations
A buffer for doProcess.
std::vector< TaskDescription > task_definitions
Publicly accessible vector with the list of tasks to be launched upon call to CNationalInstrumentsDAQ...
This class allows loading and storing values and vectors of different types from a configuration text...
GLsizei const GLchar ** string
Definition: glext.h:3919
JHUFF_TBL long freq[]
Definition: jchuff.h:44
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
unsigned __int32 uint32_t
Definition: rptypes.h:49
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 HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_count_edges_t ci_count_edges
Counter: period of a digital signal.
struct HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ao_t ao
Analog outputs.
struct HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_di_t di
Digital inputs (di)
struct HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_do_t douts
Digital outs (do)
struct HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_ang_encoder_t ci_ang_encoder
Counter: uses an angular encoder to measure angular position.
double samplesPerSecond
Sample clock config: samples per second. Continuous (infinite) sampling is assumed.
struct HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_period_t ci_period
Counter: period of a digital signal.
struct HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_co_pulses_t co_pulses
Output counter: digital pulses output.
uint32_t samplesPerChannelToRead
(Default=1000) The number of samples to grab at once from each channel.
struct HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_pulse_width_t ci_pulse_width
Counter: measure the width of a digital pulse.
struct HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ai_t ai
Analog inputs.
uint32_t bufferSamplesPerChannel
(Default=0) From NI's docs: The number of samples the buffer can hold for each channel in the task....
struct HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_lin_encoder_t ci_lin_encoder
Counter: uses a linear encoder to measure linear position.
std::string sampleClkSource
Sample clock source: may be empty (default value) for some channels.
A MRPT thread handle.
Definition: threads.h:29



Page generated by Doxygen 1.9.1 for MRPT 1.5.7 Git: 5902e14cc Wed Apr 24 15:04:01 2019 +0200 at mar 26 may 2026 13:12:03 CEST