Main MRPT website > C++ reference for MRPT 1.3.2
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-2015, 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&#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::auto_ptr< mrpt::synch::CPipeReadEndPoint > read_pipe
unsigned int physicalChannelCount
IMPORTANT This must be the total number of channels listed in "physicalChannel" (e.g. 1 for "Dev1/ao0")
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.g. 4 for "Dev1/ai0:3")
#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.11 for MRPT 1.3.2 SVN: at Mon May 9 06:50:38 UTC 2016