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 
256  std::string counter, units, edge;
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 
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 
310  std::string counter, idleState;
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
#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 acts exactly as an int (or long) variable, but with atomic increment and decrement operato...
Definition: atomic_incr.h:38
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::auto_ptr< mrpt::synch::CPipeReadEndPoint > read_pipe
std::auto_ptr< mrpt::synch::CPipeWriteEndPoint > write_pipe
TaskDescription task
A copy of the original task description that generated this thread.
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.
This structure contains the information needed to interface the threads API on each platform:
Definition: threads.h:26



Page generated by Doxygen 1.9.1 for MRPT 1.5.6 Git: 4c65e8431 Tue Apr 24 08:18:17 2018 +0200 at mar 26 may 2026 13:06:43 CEST