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



Page generated by Doxygen 1.8.15 for MRPT 1.4.0 SVN: at Sat Aug 3 20:09:00 UTC 2019