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 
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 
268  std::string counter, edge, countDirection;
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 
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
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().
std::string line
The digital line (for example "Dev1/port0/line1")
TaskDescription task
A copy of the original task description that generated this thread.
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.
JHUFF_TBL long freq[]
Definition: jchuff.h:44
An interface to read from data acquisition boards compatible with National Instruments "DAQmx Base" o...
GLsizei const GLchar ** string
Definition: glext.h:3919
uint32_t bufferSamplesPerChannel
(Default=0) From NI&#39;s docs: The number of samples the buffer can hold for each channel in the task...
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...
std::unique_ptr< mrpt::synch::CPipeWriteEndPoint > write_pipe
unsigned int physicalChannelCount
IMPORTANT This must be the total number of channels listed in "physicalChannel" (e.g. 1 for "Dev1/ao0")
A MRPT thread handle.
Definition: threads.h:28
unsigned int physicalChannelCount
IMPORTANT This must be the total number of channels listed in "physicalChannel" (e.g. 4 for "Dev1/ai0:3")
std::unique_ptr< mrpt::synch::CPipeReadEndPoint > read_pipe
double samplesPerSecond
Sample clock config: samples per second. Continuous (infinite) sampling is assumed.
std::vector< mrpt::obs::CObservationRawDAQPtr > m_nextObservations
A buffer for doProcess.
unsigned __int32 uint32_t
Definition: rptypes.h:49



Page generated by Doxygen 1.8.14 for MRPT 1.5.7 Git: 5902e14cc Wed Apr 24 15:04:01 2019 +0200 at lun oct 28 01:39:17 CET 2019