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



Page generated by Doxygen 1.8.14 for MRPT 1.5.6 Git: 4c65e8431 Tue Apr 24 08:18:17 2018 +0200 at lun oct 28 01:35:26 CET 2019