Main MRPT website > C++ reference for MRPT 1.5.9
CNationalInstrumentsDAQ.cpp
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 #include "hwdrivers-precomp.h" // Precompiled headers
11 
13 #include <iterator> // advance()
14 
15 // If we have both, DAQmx & DAQmxBase, prefer DAQmx:
16 #define MRPT_HAS_SOME_NIDAQMX (MRPT_HAS_NIDAQMXBASE || MRPT_HAS_NIDAQMX)
17 
18 #define MRPT_USE_NIDAQMXBASE (MRPT_HAS_NIDAQMXBASE && !MRPT_HAS_NIDAQMX)
19 #define MRPT_USE_NIDAQMX (MRPT_HAS_NIDAQMX)
20 
21 
22 #if MRPT_USE_NIDAQMXBASE
23 # include "NIDAQmxBase.h" // Include file for NI-DAQmx Base API
24 #endif
25 #if MRPT_USE_NIDAQMX
26 # include "NIDAQmx.h" // Include file for NI-DAQmx API
27 #endif
28 
29 // Macros to use either DAQmx or DAQmx Base automatically, depending on the installed libraries:
30 #if MRPT_USE_NIDAQMXBASE
31 # define MRPT_DAQmxGetExtendedErrorInfo DAQmxBaseGetExtendedErrorInfo
32 # define MRPT_DAQmxCreateTask DAQmxBaseCreateTask
33 # define MRPT_DAQmxCreateAIVoltageChan DAQmxBaseCreateAIVoltageChan
34 # define MRPT_DAQmxCreateAOVoltageChan DAQmxBaseCreateAOVoltageChan
35 # define MRPT_DAQmxCreateDIChan DAQmxBaseCreateDIChan
36 # define MRPT_DAQmxCreateDOChan DAQmxBaseCreateDOChan
37 # define MRPT_DAQmxCreateCIPeriodChan DAQmxBaseCreateCIPeriodChan
38 # define MRPT_DAQmxCreateCICountEdgesChan DAQmxBaseCreateCICountEdgesChan
39 # define MRPT_DAQmxCreateCIPulseWidthChan DAQmxBaseCreateCIPulseWidthChan
40 # define MRPT_DAQmxCreateCILinEncoderChan DAQmxBaseCreateCILinEncoderChan
41 # define MRPT_DAQmxCreateCIAngEncoderChan DAQmxBaseCreateCIAngEncoderChan
42 # define MRPT_DAQmxCreateCOPulseChanFreq DAQmxBaseCreateCOPulseChanFreq
43 # define MRPT_DAQmxCfgSampClkTiming DAQmxBaseCfgSampClkTiming
44 # define MRPT_DAQmxCfgInputBuffer DAQmxBaseCfgInputBuffer
45 # define MRPT_DAQmxCfgOutputBuffer DAQmxBaseCfgOutputBuffer
46 # define MRPT_DAQmxStartTask DAQmxBaseStartTask
47 # define MRPT_DAQmxStopTask DAQmxBaseStopTask
48 # define MRPT_DAQmxClearTask DAQmxBaseClearTask
49 # define MRPT_DAQmxReadAnalogF64 DAQmxBaseReadAnalogF64
50 # define MRPT_DAQmxReadCounterF64 DAQmxBaseReadCounterF64
51 # define MRPT_DAQmxReadDigitalU8 DAQmxBaseReadDigitalU8
52 # define MRPT_DAQmxWriteAnalogF64 DAQmxBaseWriteAnalogF64
53 # define MRPT_DAQmxWriteDigitalU32 DAQmxBaseWriteDigitalU32
54 # define MRPT_DAQmxWriteDigitalLines DAQmxBaseWriteDigitalLines
55 #else
56 # define MRPT_DAQmxGetExtendedErrorInfo DAQmxGetExtendedErrorInfo
57 # define MRPT_DAQmxCreateTask DAQmxCreateTask
58 # define MRPT_DAQmxCreateAIVoltageChan DAQmxCreateAIVoltageChan
59 # define MRPT_DAQmxCreateAOVoltageChan DAQmxCreateAOVoltageChan
60 # define MRPT_DAQmxCreateDIChan DAQmxCreateDIChan
61 # define MRPT_DAQmxCreateDOChan DAQmxCreateDOChan
62 # define MRPT_DAQmxCreateCIPeriodChan DAQmxCreateCIPeriodChan
63 # define MRPT_DAQmxCreateCICountEdgesChan DAQmxCreateCICountEdgesChan
64 # define MRPT_DAQmxCreateCIPulseWidthChan DAQmxCreateCIPulseWidthChan
65 # define MRPT_DAQmxCreateCILinEncoderChan DAQmxCreateCILinEncoderChan
66 # define MRPT_DAQmxCreateCIAngEncoderChan DAQmxCreateCIAngEncoderChan
67 # define MRPT_DAQmxCreateCOPulseChanFreq DAQmxCreateCOPulseChanFreq
68 # define MRPT_DAQmxCfgSampClkTiming DAQmxCfgSampClkTiming
69 # define MRPT_DAQmxCfgInputBuffer DAQmxCfgInputBuffer
70 # define MRPT_DAQmxCfgOutputBuffer DAQmxCfgOutputBuffer
71 # define MRPT_DAQmxStartTask DAQmxStartTask
72 # define MRPT_DAQmxStopTask DAQmxStopTask
73 # define MRPT_DAQmxClearTask DAQmxClearTask
74 # define MRPT_DAQmxReadAnalogF64 DAQmxReadAnalogF64
75 # define MRPT_DAQmxReadCounterF64 DAQmxReadCounterF64
76 # define MRPT_DAQmxReadDigitalU8 DAQmxReadDigitalU8
77 # define MRPT_DAQmxWriteAnalogF64 DAQmxWriteAnalogF64
78 # define MRPT_DAQmxWriteDigitalU32 DAQmxWriteDigitalU32
79 # define MRPT_DAQmxWriteDigitalLines DAQmxWriteDigitalLines
80 #endif
81 
82 // An auxiliary macro to check and report errors in the DAQmx library as exceptions with a well-explained message.
83 #define MRPT_DAQmx_ErrChk(functionCall) \
84  if( (functionCall)<0) \
85  { \
86  char errBuff[2048]; \
87  MRPT_DAQmxGetExtendedErrorInfo(errBuff,2048); \
88  std::string sErr = mrpt::format("DAQ error: '%s'\nCalling: '%s'",errBuff,#functionCall); \
89  THROW_EXCEPTION(sErr) \
90  }
91 
92 
93 using namespace mrpt::hwdrivers;
94 using namespace mrpt::obs;
95 using namespace mrpt::system;
96 using namespace std;
97 
99 
100 // ------------- CNationalInstrumentsDAQ::TInfoPerTask -----------
101 // Default ctor:
103  taskHandle(0),
104  must_close(false),
105  is_closed(false),
106  new_obs_available(0),
107  task()
108 { }
109 
110 /* -----------------------------------------------------
111  Constructor
112  ----------------------------------------------------- */
114  mrpt::utils::COutputLogger("CNationalInstrumentsDAQ")
115 {
116  m_sensorLabel = "NIDAQ";
117 }
118 
119 // Just like "MRPT_LOAD_HERE_CONFIG_VAR" but...
120 #define MY_LOAD_HERE_CONFIG_VAR(variableName,variableType,targetVariable,configFileObject,sectionNameStr) \
121  targetVariable = configFileObject.read_##variableType(sectionNameStr,variableName,targetVariable,false);
122 
123 #define MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT(variableName,variableType,targetVariable,configFileObject,sectionNameStr) \
124  { try { \
125  targetVariable = configFileObject.read_##variableType(sectionNameStr,variableName,targetVariable,true); \
126  } catch (std::exception &) \
127  { \
128  THROW_EXCEPTION( format( "Value for '%s' not found in config file", std::string(variableName).c_str() )); \
129  } }\
130 
131 
132 
133 /* -----------------------------------------------------
134  loadConfig_sensorSpecific
135  ----------------------------------------------------- */
137  const mrpt::utils::CConfigFileBase &cfg,
138  const std::string &sect )
139 {
140  //std::vector<TaskDescription> task_definitions;
141  task_definitions.clear();
142 
143  const unsigned int nTasks = cfg.read_uint64_t(sect, "num_tasks", 0, true );
144  if (!nTasks) {
145  std::cerr << "[CNationalInstrumentsDAQ] Warning: Number of tasks is zero. No datalogging will be done.\n";
146  }
147 
148  task_definitions.resize(nTasks);
149  for (unsigned int i=0;i<nTasks;i++)
150  {
152  const string sTask = mrpt::format("task%u",i);
153 
154  // Read general settings for this task:
155  // ---------------------------------------
156  const string sChanns = cfg.read_string(sect,sTask+string(".channels"),"",true);
157  vector<string> lstStrChanns;
158  mrpt::system::tokenize(sChanns," \t,",lstStrChanns);
159  if (lstStrChanns.empty())
160  THROW_EXCEPTION_FMT("List of channels for task %u is empty!",i)
161 
162  MY_LOAD_HERE_CONFIG_VAR( sTask+string(".samplesPerSecond"), double, t.samplesPerSecond, cfg,sect)
163  MY_LOAD_HERE_CONFIG_VAR( sTask+string(".samplesPerChannelToRead"), double, t.samplesPerChannelToRead, cfg,sect)
164  MY_LOAD_HERE_CONFIG_VAR( sTask+string(".sampleClkSource"), string, t.sampleClkSource, cfg,sect)
165  MY_LOAD_HERE_CONFIG_VAR( sTask+string(".bufferSamplesPerChannel"), double, t.bufferSamplesPerChannel, cfg,sect)
166  t.taskLabel = cfg.read_string(sect,sTask+string(".taskLabel"), sTask, false );
167 
168  for (size_t j=0;j<lstStrChanns.size();j++)
169  {
170  if (strCmpI(lstStrChanns[j],"ai"))
171  {
172  t.has_ai = true;
173  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ai.physicalChannel"), string, t.ai.physicalChannel, cfg,sect)
174  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ai.physicalChannelCount"), uint64_t, t.ai.physicalChannelCount, cfg,sect)
175  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ai.terminalConfig"), string, t.ai.terminalConfig, cfg,sect)
176  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ai.minVal"), double, t.ai.minVal, cfg,sect)
177  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ai.maxVal"), double, t.ai.maxVal, cfg,sect)
178  }
179  else if (strCmpI(lstStrChanns[j],"ao"))
180  {
181  t.has_ao = true;
182  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ao.physicalChannel"), string, t.ao.physicalChannel, cfg,sect)
183  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ao.physicalChannelCount"), uint64_t, t.ao.physicalChannelCount, cfg,sect)
184  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ao.minVal"), double, t.ao.minVal, cfg,sect)
185  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ao.maxVal"), double, t.ao.maxVal, cfg,sect)
186  }
187  else if (strCmpI(lstStrChanns[j],"di"))
188  {
189  t.has_di = true;
190  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".di.line"), string, t.di.line, cfg,sect)
191  }
192  else if (strCmpI(lstStrChanns[j],"do"))
193  {
194  t.has_do = true;
195  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".do.line"), string, t.douts.line, cfg,sect)
196  }
197  else if (strCmpI(lstStrChanns[j],"ci_period"))
198  {
199  t.has_ci_period = true;
200  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_period.counter"), string, t.ci_period.counter, cfg,sect)
201  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_period.minVal"), double, t.ci_period.minVal, cfg,sect)
202  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_period.maxVal"), double, t.ci_period.maxVal, cfg,sect)
203  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_period.units"), string, t.ci_period.units, cfg,sect)
204  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_period.edge"), string, t.ci_period.edge, cfg,sect)
205  MY_LOAD_HERE_CONFIG_VAR( sTask+string(".ci_period.measTime"), double, t.ci_period.measTime, cfg,sect)
206  MY_LOAD_HERE_CONFIG_VAR( sTask+string(".ci_period.divisor"), int, t.ci_period.divisor, cfg,sect)
207  }
208  else if (strCmpI(lstStrChanns[j],"ci_count_edges"))
209  {
210  t.has_ci_count_edges = true;
211  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_count_edges.counter"), string, t.ci_count_edges.counter, cfg,sect)
212  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_count_edges.edge"), string, t.ci_count_edges.edge, cfg,sect)
213  MY_LOAD_HERE_CONFIG_VAR( sTask+string(".ci_count_edges.initialCount"), int, t.ci_count_edges.initialCount, cfg,sect)
214  MY_LOAD_HERE_CONFIG_VAR( sTask+string(".ci_count_edges.countDirection"), string, t.ci_count_edges.countDirection, cfg,sect)
215  }
216  else if (strCmpI(lstStrChanns[j],"ci_pulse_width"))
217  {
218  t.has_ci_pulse_width = true;
219  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_pulse_width.counter"), string, t.ci_pulse_width.counter, cfg,sect)
220  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_pulse_width.minVal"), double, t.ci_pulse_width.minVal, cfg,sect)
221  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_pulse_width.maxVal"), double, t.ci_pulse_width.maxVal, cfg,sect)
222  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_pulse_width.units"), string, t.ci_pulse_width.units, cfg,sect)
223  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_pulse_width.startingEdge"), string, t.ci_pulse_width.startingEdge, cfg,sect)
224  }
225  else if (strCmpI(lstStrChanns[j],"ci_lin_encoder"))
226  {
227  t.has_ci_lin_encoder = true;
228  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_lin_encoder.counter"), string, t.ci_lin_encoder.counter, cfg,sect)
229  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_lin_encoder.decodingType"), string, t.ci_lin_encoder.decodingType, cfg,sect)
230  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_lin_encoder.ZidxEnable"), bool, t.ci_lin_encoder.ZidxEnable, cfg,sect)
231  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_lin_encoder.ZidxVal"), double, t.ci_lin_encoder.ZidxVal, cfg,sect)
232  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_lin_encoder.ZidxPhase"), string, t.ci_lin_encoder.ZidxPhase, cfg,sect)
233  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_lin_encoder.units"), string, t.ci_lin_encoder.units, cfg,sect)
234  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_lin_encoder.distPerPulse"), double, t.ci_lin_encoder.distPerPulse, cfg,sect)
235  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_lin_encoder.initialPos"), double, t.ci_lin_encoder.initialPos, cfg,sect)
236  }
237  else if (strCmpI(lstStrChanns[j],"ci_ang_encoder"))
238  {
239  t.has_ci_ang_encoder = true;
240  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_ang_encoder.counter"), string, t.ci_ang_encoder.counter, cfg,sect)
241  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_ang_encoder.decodingType"), string, t.ci_ang_encoder.decodingType, cfg,sect)
242  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_ang_encoder.ZidxEnable"), bool, t.ci_ang_encoder.ZidxEnable, cfg,sect)
243  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_ang_encoder.ZidxVal"), double, t.ci_ang_encoder.ZidxVal, cfg,sect)
244  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_ang_encoder.ZidxPhase"), string, t.ci_ang_encoder.ZidxPhase, cfg,sect)
245  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_ang_encoder.units"), string, t.ci_ang_encoder.units, cfg,sect)
246  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_ang_encoder.pulsesPerRev"), int, t.ci_ang_encoder.pulsesPerRev, cfg,sect)
247  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".ci_ang_encoder.initialAngle"), double, t.ci_ang_encoder.initialAngle, cfg,sect)
248  MY_LOAD_HERE_CONFIG_VAR( sTask+string(".ci_ang_encoder.decimate"), int, t.ci_ang_encoder.decimate, cfg,sect)
249 
250  }
251  else if (strCmpI(lstStrChanns[j],"co_pulses"))
252  {
253  t.has_co_pulses = true;
254  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".co_pulses.counter"), string, t.co_pulses.counter, cfg,sect)
255  MY_LOAD_HERE_CONFIG_VAR( sTask+string(".co_pulses.idleState"), string, t.co_pulses.idleState, cfg,sect)
256  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".co_pulses.initialDelay"), double, t.co_pulses.initialDelay, cfg,sect)
257  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".co_pulses.freq"), double, t.co_pulses.freq, cfg,sect)
258  MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT( sTask+string(".co_pulses.dutyCycle"), double, t.co_pulses.dutyCycle, cfg,sect)
259  }
260  else
261  {
262  THROW_EXCEPTION_FMT("Unknown channel type '%s'! See the docs of CNationalInstrumentsDAQ",lstStrChanns[j].c_str())
263  }
264  } // end for each "k" channel in channel "i"
265  } // end for "i", each task
266 }
267 
268 /* -----------------------------------------------------
269  Destructor
270  ----------------------------------------------------- */
272 {
273  this->stop();
274 }
275 
276 #if MRPT_HAS_SOME_NIDAQMX
277 // Declare a table to convert strings to their DAQmx #define values:
278 struct daqmx_str_val {
279  const char* str;
280  int val;
281 };
282 
283 const daqmx_str_val daqmx_vals[] = {
284  { "DAQmx_Val_Cfg_Default", DAQmx_Val_Cfg_Default },
285  { "DAQmx_Val_RSE", DAQmx_Val_RSE },
286  { "DAQmx_Val_NRSE", DAQmx_Val_NRSE },
287  { "DAQmx_Val_Diff", DAQmx_Val_Diff },
288  { "DAQmx_Val_Seconds", DAQmx_Val_Seconds },
289  { "DAQmx_Val_Rising", DAQmx_Val_Rising },
290  { "DAQmx_Val_Falling", DAQmx_Val_Falling },
291  { "DAQmx_Val_CountUp", DAQmx_Val_CountUp },
292  { "DAQmx_Val_CountDown", DAQmx_Val_CountDown },
293  { "DAQmx_Val_ExtControlled", DAQmx_Val_ExtControlled },
294  { "DAQmx_Val_AHighBHigh", DAQmx_Val_AHighBHigh },
295  { "DAQmx_Val_AHighBLow", DAQmx_Val_AHighBLow },
296  { "DAQmx_Val_ALowBHigh", DAQmx_Val_ALowBHigh },
297  { "DAQmx_Val_ALowBLow", DAQmx_Val_ALowBLow },
298  { "DAQmx_Val_X1", DAQmx_Val_X1},
299  { "DAQmx_Val_X2", DAQmx_Val_X2},
300  { "DAQmx_Val_X4", DAQmx_Val_X4},
301  { "DAQmx_Val_Meters", DAQmx_Val_Meters },
302  { "DAQmx_Val_Inches", DAQmx_Val_Inches },
303  { "DAQmx_Val_Ticks", DAQmx_Val_Ticks },
304  { "DAQmx_Val_Degrees", DAQmx_Val_Degrees },
305  { "DAQmx_Val_Radians", DAQmx_Val_Radians },
306  { "DAQmx_Val_High", DAQmx_Val_High},
307  { "DAQmx_Val_Low", DAQmx_Val_Low}
308 };
309 
310 int daqmx_defstr2num(const std::string &str)
311 {
312  const std::string s = mrpt::system::trim(str);
313 
314  for (unsigned int i=0;i<sizeof(daqmx_vals)/sizeof(daqmx_vals[0]);i++)
315  {
316  if (strCmpI(daqmx_vals[i].str,s.c_str()))
317  return daqmx_vals[i].val;
318  }
319  THROW_EXCEPTION_FMT("Error: Unknown DAQmx constant: %s",s.c_str())
320 }
321 #endif
322 
323 /* -----------------------------------------------------
324  initialize
325 ----------------------------------------------------- */
327 {
328 #if MRPT_HAS_SOME_NIDAQMX
329  this->stop();
330 
331  for (size_t i=0;i<task_definitions.size();i++)
332  {
333  const TaskDescription & tf = task_definitions[i];
334 
335  // Try to create a new task:
336  m_running_tasks.push_back(TInfoPerTask());
337  TInfoPerTask &ipt = m_running_tasks.back();
338  ipt.task = tf; // Save a copy of the task info for the thread to have all the needed info
339 
340  try
341  {
342  TaskHandle &taskHandle= *reinterpret_cast<TaskHandle*>(&ipt.taskHandle);
343 
344  MRPT_DAQmx_ErrChk (MRPT_DAQmxCreateTask("",&taskHandle));
345 
346  if (tf.has_ai) {
347  ASSERTMSG_(tf.ai.physicalChannelCount>0, "ai.physicalChannelCount is zero! Please, define it correctly.")
348 
350  tf.ai.physicalChannel.c_str(),NULL,
351  daqmx_defstr2num(tf.ai.terminalConfig),
352  tf.ai.minVal, tf.ai.maxVal,DAQmx_Val_Volts,NULL));
353  }
354  if (tf.has_ao) {
355  ASSERTMSG_(tf.ao.physicalChannelCount>0, "ai.physicalChannelCount is zero! Please, define it correctly.")
356 
358  tf.ao.physicalChannel.c_str(),NULL,
359  tf.ao.minVal, tf.ao.maxVal,DAQmx_Val_Volts,NULL));
360  }
361  if (tf.has_di) {
363  tf.di.line.c_str(),NULL,DAQmx_Val_ChanPerLine));
364  }
365  if (tf.has_do) {
367  tf.douts.line.c_str(),NULL,DAQmx_Val_ChanPerLine));
368  }
369  if (tf.has_ci_period) {
371  tf.ci_period.counter.c_str(),NULL,
373  daqmx_defstr2num(tf.ci_period.units),
374  daqmx_defstr2num(tf.ci_period.edge),
375  DAQmx_Val_LowFreq1Ctr,
376  tf.ci_period.measTime,
377  tf.ci_period.divisor,NULL));
378  }
379  if (tf.has_ci_count_edges) {
381  tf.ci_count_edges.counter.c_str(),NULL,
382  daqmx_defstr2num(tf.ci_count_edges.edge),
384  daqmx_defstr2num(tf.ci_count_edges.countDirection)));
385  }
386  if (tf.has_ci_pulse_width) {
388  tf.ci_pulse_width.counter.c_str(),NULL,
390  daqmx_defstr2num(tf.ci_pulse_width.units),
391  daqmx_defstr2num(tf.ci_pulse_width.startingEdge),
392  NULL));
393  }
394  if (tf.has_ci_lin_encoder) {
396  tf.ci_lin_encoder.counter.c_str(),NULL,
397  daqmx_defstr2num(tf.ci_lin_encoder.decodingType),
400  daqmx_defstr2num(tf.ci_lin_encoder.ZidxPhase),
401  daqmx_defstr2num(tf.ci_lin_encoder.units),
404  NULL));
405  }
406  if (tf.has_ci_ang_encoder) {
408  tf.ci_ang_encoder.counter.c_str(),NULL,
409  daqmx_defstr2num(tf.ci_ang_encoder.decodingType),
412  daqmx_defstr2num(tf.ci_ang_encoder.ZidxPhase),
413  daqmx_defstr2num(tf.ci_ang_encoder.units),
416  NULL));
417  }
418  if (tf.has_co_pulses) {
420  tf.co_pulses.counter.c_str(),NULL,
421  DAQmx_Val_Hz,
422  daqmx_defstr2num(tf.co_pulses.idleState),
424  tf.co_pulses.freq,
425  tf.co_pulses.dutyCycle));
426  }
427 
428  // Seems to be needed to avoid an errors avoid like:
429  // " Onboard device memory overflow. Because of system and/or bus-bandwidth limitations, the driver could not read data from the device fast enough to keep up with the device throughput."
431  {
432  // sample rate:
434  MRPT_DAQmx_ErrChk (MRPT_DAQmxCfgSampClkTiming(taskHandle,tf.sampleClkSource.c_str(),tf.samplesPerSecond,DAQmx_Val_Rising, DAQmx_Val_ContSamps,tf.samplesPerChannelToRead));
435 
437  }
438 
439  if (tf.has_ao)
440  {
441  // Nothing to do as long as we only need "on demand" outputs.
442  // MRPT_DAQmx_ErrChk (MRPT_DAQmxCfgOutputBuffer(taskHandle,2 /*tf.bufferSamplesPerChannel*/ ));
443  // // Output buffer MUST have some data before starting the task: write 0s:
444  // vector<double> d;
445  // d.assign(tf.ao.physicalChannelCount*2, 0.0);
446  // this->writeAnalogOutputTask(i,1 /* samples per channel */, &d[0], 0.10 /*timeout*/, false);
447  }
448 
449  // Create pipe:
451 
452  // Add a large timeout, just in case the writing thread dies unexpectedly so the reader doesn't hang on:
453  ipt.read_pipe->timeout_read_start_us = 100000; // 100ms
454  ipt.read_pipe->timeout_read_between_us = 100000; // 100ms
455 
457 
458  ipt.hThread = mrpt::system::createThreadFromObjectMethodRef<CNationalInstrumentsDAQ,TInfoPerTask>(this, &CNationalInstrumentsDAQ::grabbing_thread, ipt);
459 
460 
461  }
462  catch (std::exception const &e)
463  {
464  std::cerr << "[CNationalInstrumentsDAQ] Error:" << std::endl << e.what() << std::endl;
465  if( ipt.taskHandle!=NULL )
466  {
467  TaskHandle &taskHandle= *reinterpret_cast<TaskHandle*>(&ipt.taskHandle);
468  MRPT_DAQmxStopTask(taskHandle);
469  MRPT_DAQmxClearTask(taskHandle);
470  }
471 
472  // Stop thread:
473  if (!ipt.hThread.isClear())
474  {
475  ipt.must_close=true;
476  cerr << "[CNationalInstrumentsDAQ::initialize] Waiting for the grabbing thread to end due to exception...\n";
478  cerr << "[CNationalInstrumentsDAQ::initialize] Grabbing thread ended.\n";
479  }
480 
481  // Remove from list:
482  m_running_tasks.erase(--m_running_tasks.end());
483 
484  std::cerr << "[CNationalInstrumentsDAQ] Error while creating tasks. Closing other tasks before returning...\n";
485  this->stop();
486  std::cerr << "[CNationalInstrumentsDAQ] Closing tasks done.\n";
487 
488  throw; // Rethrow
489  }
490  } // end for each task_definitions[i]
491 
492 #else
493  THROW_EXCEPTION("MRPT was compiled without support for NI DAQmx!!")
494 #endif
495 }
496 
497 /** Stop the grabbing threads for DAQ tasks. It is automatically called at destruction. */
499 {
500  // Stop all threads:
501  for (list<TInfoPerTask>::iterator it=m_running_tasks.begin();it!=m_running_tasks.end();++it)
502  {
503  it->must_close=true;
504  }
505  if (m_verbose) cout << "[CNationalInstrumentsDAQ::stop] Waiting for grabbing threads to end...\n";
506  for (list<TInfoPerTask>::iterator it=m_running_tasks.begin();it!=m_running_tasks.end();++it)
507  {
508  // For some reason, join doesn't work...
509  // if (!it->hThread.isClear()) mrpt::system::joinThread(it->hThread);
510  // Polling:
511  for (size_t tim=0;tim<250 && !it->is_closed;tim++) { mrpt::system::sleep(1); }
512  it->hThread.clear();
513  }
514  if (m_verbose) cout << "[CNationalInstrumentsDAQ::stop] All threads ended.\n";
515 
516  // Stop all NI tasks:
517 #if MRPT_HAS_SOME_NIDAQMX
518  for (list<TInfoPerTask>::iterator it=m_running_tasks.begin();it!=m_running_tasks.end();++it)
519  {
520  TaskHandle &taskHandle= *reinterpret_cast<TaskHandle*>(&it->taskHandle);
521 
522  MRPT_DAQmxStopTask(taskHandle);
523  MRPT_DAQmxClearTask(taskHandle);
524  taskHandle=NULL;
525  }
526 #endif
527 }
528 
529 /** Returns true if initialize() was called successfully. */
531 {
532  return (!m_running_tasks.empty() && !m_running_tasks.begin()->is_closed);
533 }
534 
535 
536 /*-------------------------------------------------------------
537  readFromDAQ
538 -------------------------------------------------------------*/
540  std::vector<mrpt::obs::CObservationRawDAQPtr> &outObservations,
541  bool &hardwareError )
542 {
543  hardwareError = false;
544  outObservations.clear();
545 
546  if ( !checkDAQIsWorking() )
547  {
548  hardwareError = true;
549  return;
550  }
551 
552  // Read from the pipe:
553  m_state = ssWorking;
554 
555 
556  for (list<TInfoPerTask>::iterator it=m_running_tasks.begin();it!=m_running_tasks.end();++it)
557  {
558  CObservationRawDAQ tmp_obs;
559  try
560  {
561  if (it->new_obs_available!=0)
562  {
563  it->read_pipe->ReadObject(&tmp_obs);
564  --(it->new_obs_available);
565 
566  // Yes, valid block of samples was adquired:
567  outObservations.push_back(CObservationRawDAQPtr(new CObservationRawDAQ(tmp_obs)));
568  }
569  }
570  catch (...)
571  {
572  // Timeout...
573  }
574  }
575 }
576 
577 
578 /* -----------------------------------------------------
579  doProcess
580 ----------------------------------------------------- */
582 {
583  bool hwError;
584  readFromDAQ(m_nextObservations, hwError );
585 
586  if (hwError)
587  {
588  m_state = ssError;
589  THROW_EXCEPTION("Couldn't start DAQ task!");
590  }
591 
592  if (!m_nextObservations.empty())
593  {
594  m_state = ssWorking;
595 
596  std::vector<mrpt::utils::CSerializablePtr> new_obs;
597  new_obs.resize(m_nextObservations.size());
598 
599  for (size_t i=0;i<m_nextObservations.size();i++)
600  new_obs[i] = m_nextObservations[i];
601 
602  appendObservations(new_obs);
603  }
604 }
605 
606 
607 /* -----------------------------------------------------
608  grabbing_thread
609 ----------------------------------------------------- */
611 {
612 #if MRPT_HAS_SOME_NIDAQMX
613  try
614  {
615  TaskHandle &taskHandle= *reinterpret_cast<TaskHandle*>(&ipt.taskHandle);
616  if (m_verbose) cout << "[CNationalInstrumentsDAQ::grabbing_thread] Starting thread for task " << ipt.taskHandle << "\n";
617 
618  MRPT_TODO("Add write timeout")
619  //ipt.write_pipe->timeout_read_between_us
620 
621  const float timeout = 10*ipt.task.samplesPerChannelToRead/ipt.task.samplesPerSecond;
622 
623  int err=0;
624  vector<double> dBuf;
625  vector<uint8_t> u8Buf;
626 
627  const mrpt::obs::CObservationRawDAQ clean_obs;
629 
630  while (!ipt.must_close)
631  {
632  obs = clean_obs; // Start with an empty observation
633 
634  // Common stuff:
638 
639  bool there_are_data = false; // At least one channel?
640 
641  // Read from each channel in this task:
642  // -----------------------------------------------
643  if (ipt.task.has_ai)
644  {
646  obs.AIN_interleaved = true;
647 
648  const uint32_t totalSamplesToRead = ipt.task.ai.physicalChannelCount * ipt.task.samplesPerChannelToRead;
649  dBuf.resize(totalSamplesToRead);
650  int32 pointsReadPerChan=-1;
651  if ((err = MRPT_DAQmxReadAnalogF64(
652  taskHandle,
653  ipt.task.samplesPerChannelToRead,timeout, obs.AIN_interleaved ? DAQmx_Val_GroupByScanNumber : DAQmx_Val_GroupByChannel,
654  &dBuf[0],dBuf.size(),
655  &pointsReadPerChan,NULL))<0 && err!=DAQmxErrorSamplesNotYetAvailable)
656  {
657  MRPT_DAQmx_ErrChk(err)
658  }
659  else if (pointsReadPerChan>0) {
660  ASSERT_EQUAL_(totalSamplesToRead,pointsReadPerChan*ipt.task.ai.physicalChannelCount)
661  obs.AIN_double = dBuf;
662  there_are_data = true;
663  if (m_verbose) cout << "[CNationalInstrumentsDAQ::grabbing_thread] " << pointsReadPerChan << " analog samples read.\n";
664  }
665  } // end AI
666  if (ipt.task.has_di)
667  {
668  const uint32_t totalSamplesToRead = 1 * ipt.task.samplesPerChannelToRead;
669  u8Buf.resize(totalSamplesToRead);
670 
671  int32 pointsReadPerChan=-1;
672  if ((err = MRPT_DAQmxReadDigitalU8(
673  taskHandle,
674  ipt.task.samplesPerChannelToRead,timeout, DAQmx_Val_GroupByChannel,
675  &u8Buf[0],u8Buf.size(),
676  &pointsReadPerChan,NULL))<0 && err!=DAQmxErrorSamplesNotYetAvailable)
677  {
678  MRPT_DAQmx_ErrChk(err)
679  }
680  else if (pointsReadPerChan>0) {
681  ASSERT_EQUAL_(totalSamplesToRead,pointsReadPerChan*ipt.task.ai.physicalChannelCount)
682  obs.DIN = u8Buf;
683  there_are_data = true;
684  if (m_verbose) cout << "[CNationalInstrumentsDAQ::grabbing_thread] " << pointsReadPerChan << " digital samples read.\n";
685  }
686  } // end DI
688  {
689  const int32 totalSamplesToRead = ipt.task.samplesPerChannelToRead;
690  dBuf.resize(totalSamplesToRead);
691  int32 pointsReadPerChan=-1;
692  if ((err = MRPT_DAQmxReadCounterF64(
693  taskHandle,
694  totalSamplesToRead,timeout,
695  &dBuf[0],dBuf.size(),
696  &pointsReadPerChan,NULL))<0 && err!=DAQmxErrorSamplesNotYetAvailable)
697  {
698  MRPT_DAQmx_ErrChk(err)
699  }
700  else if (pointsReadPerChan>0) {
701  ASSERT_EQUAL_(totalSamplesToRead,pointsReadPerChan)
702 
703  // Decimate?
705  {
707 
708  obs.CNTRIN_double = dBuf;
709  there_are_data = true;
710  if (m_verbose && !obs.CNTRIN_double.empty())
711  {
712  static int decim=0;
713  if (!decim)
714  cout << "[CNationalInstrumentsDAQ::grabbing_thread] " << pointsReadPerChan << " counter samples read ([0]="<< obs.CNTRIN_double[0] <<").\n";
715  if (++decim>100) decim=0;
716  }
717  }
718  }
719  } // end COUNTERS
720 
721  // Send the observation to the main thread:
722  if (there_are_data)
723  {
724  ++(ipt.new_obs_available);
725  ipt.write_pipe->WriteObject(&obs);
726  //mrpt::system::sleep(1); // This seems to be needed to allow all objs to be sent to the recv thread
727  }
728  else {
730  }
731 
732  } // end of main thread loop
733  }
734  catch(std::exception &e)
735  {
736  std::cerr << "[CNationalInstrumentsDAQ::grabbing_thread] Exception:\n" << e.what() << std::endl;
737  }
738 #endif //MRPT_HAS_SOME_NIDAQMX
739 
740  ipt.is_closed = true;
741 }
742 
743 
744 void CNationalInstrumentsDAQ::writeAnalogOutputTask(size_t task_index, size_t nSamplesPerChannel, const double * volt_values, double timeout, bool groupedByChannel)
745 {
746 #if MRPT_HAS_SOME_NIDAQMX
747  ASSERT_(task_index<m_running_tasks.size())
748 
750  std::advance(it, task_index);
751  TInfoPerTask & ipt = *it;
752  TaskHandle &taskHandle= *reinterpret_cast<TaskHandle*>(&ipt.taskHandle);
753 
754  int32 samplesWritten=0;
755  int err=0;
756  if (err = MRPT_DAQmxWriteAnalogF64(
757  taskHandle,
758  nSamplesPerChannel,FALSE,timeout, groupedByChannel ? DAQmx_Val_GroupByChannel : DAQmx_Val_GroupByScanNumber,
759  const_cast<float64*>(volt_values),
760  &samplesWritten, NULL) )
761  {
762  MRPT_DAQmx_ErrChk(err)
763  }
764 #else
765  MRPT_UNUSED_PARAM(task_index); MRPT_UNUSED_PARAM(nSamplesPerChannel);
766  MRPT_UNUSED_PARAM(volt_values); MRPT_UNUSED_PARAM(timeout); MRPT_UNUSED_PARAM(groupedByChannel);
767 #endif
768 }
769 
770 void CNationalInstrumentsDAQ::writeDigitalOutputTask(size_t task_index, bool line_value, double timeout)
771 {
772 #if MRPT_HAS_SOME_NIDAQMX
773  ASSERT_(task_index<m_running_tasks.size())
774 
776  std::advance(it, task_index);
777  TInfoPerTask & ipt = *it;
778  TaskHandle &taskHandle= *reinterpret_cast<TaskHandle*>(&ipt.taskHandle);
779 
780  uInt8 dat = line_value ? 1:0;
781 
782  int32 samplesWritten=0;
783  int32 nSamplesPerChannel = 1;
784  int err=0;
785  if (err = MRPT_DAQmxWriteDigitalLines(
786  taskHandle,
787  nSamplesPerChannel,FALSE,timeout, DAQmx_Val_GroupByScanNumber,
788  &dat,&samplesWritten,NULL) )
789  {
790  MRPT_DAQmx_ErrChk(err)
791  }
792 
793 #else
794  MRPT_UNUSED_PARAM(task_index); MRPT_UNUSED_PARAM(line_value); MRPT_UNUSED_PARAM(timeout);
795 #endif
796 }
797 
798 
799 // Ctor:
801  has_ai(false), has_ao(false), has_di(false), has_do(false),
802  has_ci_period(false), has_ci_count_edges(false), has_ci_pulse_width(false), has_ci_lin_encoder(false),has_ci_ang_encoder(false),has_co_pulses(false),
803  samplesPerSecond(1000.0),
804  bufferSamplesPerChannel(200000),
805  samplesPerChannelToRead(1000)
806 {
807 }
#define ASSERT_EQUAL_(__A, __B)
void appendObservations(const std::vector< mrpt::utils::CSerializablePtr > &obj)
This method must be called by derived classes to enqueue a new observation in the list to be returned...
uint16_t AIN_channel_count
Readings from analog input (ADCs) channels (vector length=channel count) in Volts.
#define MRPT_DAQmxCreateCILinEncoderChan
double sample_rate
Readings from ticks counters, such as quadrature encoders.
struct HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_do_t douts
Digital outs (do)
Each of the tasks to create in CNationalInstrumentsDAQ::initialize().
GLdouble GLdouble t
Definition: glext.h:3610
struct HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_di_t di
Digital inputs (di)
#define ASSERT_ABOVE_(__A, __B)
virtual void initialize()
Setup and launch the DAQ tasks, in parallel threads.
#define MRPT_DAQmxCreateDOChan
std::string line
The digital line (for example "Dev1/port0/line1")
This namespace provides a OS-independent interface to many useful functions: filenames manipulation...
Definition: math_frwds.h:29
#define MRPT_DAQmxCreateCIPulseWidthChan
std::string m_sensorLabel
See CGenericSensor.
struct HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_lin_encoder_t ci_lin_encoder
Counter: uses a linear encoder to measure linear position.
#define MRPT_DAQmxStartTask
#define THROW_EXCEPTION(msg)
#define THROW_EXCEPTION_FMT(_FORMAT_STRING,...)
Scalar * iterator
Definition: eigen_plugins.h:23
#define MRPT_DAQmxCreateAOVoltageChan
#define MRPT_DAQmxCreateDIChan
mrpt::system::TTimeStamp now()
A shortcut for system::getCurrentTime.
Definition: datetime.h:70
Contains classes for various device interfaces.
std::string read_string(const std::string &section, const std::string &name, const std::string &defaultValue, bool failIfNotFound=false) const
#define MY_LOAD_HERE_CONFIG_VAR_NO_DEFAULT(variableName, variableType, targetVariable, configFileObject, sectionNameStr)
#define MRPT_DAQmxCreateCIPeriodChan
STL namespace.
bool checkDAQIsWorking() const
Returns true if initialize() was called and at least one task is running.
TaskDescription task
A copy of the original task description that generated this thread.
GLdouble s
Definition: glext.h:3602
#define MRPT_DAQmxWriteDigitalLines
#define MRPT_DAQmxReadAnalogF64
void readFromDAQ(std::vector< mrpt::obs::CObservationRawDAQPtr > &outObservations, bool &hardwareError)
Receives data from the DAQ thread(s).
#define MRPT_TODO(x)
This class allows loading and storing values and vectors of different types from a configuration text...
void loadConfig_sensorSpecific(const mrpt::utils::CConfigFileBase &configSource, const std::string &iniSection)
See the class documentation at the top for expected parameters.
struct HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ai_t ai
Analog inputs.
struct HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_co_pulses_t co_pulses
Output counter: digital pulses output.
#define MRPT_DAQmxWriteAnalogF64
#define MRPT_UNUSED_PARAM(a)
Can be used to avoid "not used parameters" warnings from the compiler.
std::string sampleClkSource
Sample clock source: may be empty (default value) for some channels.
#define MRPT_DAQmxCreateCIAngEncoderChan
void writeDigitalOutputTask(size_t task_index, bool line_value, double timeout)
Changes the boolean state of one digital output line.
void BASE_IMPEXP sleep(int time_ms) MRPT_NO_THROWS
An OS-independent method for sending the current thread to "sleep" for a given period of time...
Definition: threads.cpp:57
#define FALSE
Definition: jmorecfg.h:227
#define MRPT_DAQmxCfgInputBuffer
#define MRPT_DAQmxReadCounterF64
This namespace contains representation of robot actions and observations.
int val
Definition: mrpt_jpeglib.h:953
void stop()
Stop the grabbing threads for DAQ tasks.
#define MY_LOAD_HERE_CONFIG_VAR(variableName, variableType, targetVariable, configFileObject, sectionNameStr)
void grabbing_thread(TInfoPerTask &ipt)
Method to be executed in each parallel thread.
An interface to read from data acquisition boards compatible with National Instruments "DAQmx Base" o...
bool isClear() const
Returns true if the handle is uninitialized.
Definition: threads.h:45
std::string BASE_IMPEXP format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
bool AIN_interleaved
Whether the channels are interleaved (A0 A1 A2 A0 A1 A2...) or not (A0 A0 A0 A1 A1 A1 A2 A2 A2...
GLsizei const GLchar ** string
Definition: glext.h:3919
#define MRPT_DAQmxStopTask
Store raw data from a Data Acquisition (DAQ) device, such that input or output analog and digital cha...
struct HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_pulse_width_t ci_pulse_width
Counter: measure the width of a digital pulse.
#define IMPLEMENTS_GENERIC_SENSOR(class_name, NameSpace)
This must be inserted in all CGenericSensor classes implementation files:
void writeAnalogOutputTask(size_t task_index, size_t nSamplesPerChannel, const double *volt_values, double timeout, bool groupedByChannel)
Set voltage outputs to all the outputs in an AOUT task For the meaning of parameters, refere to NI DAQmx docs for DAQmxBaseWriteAnalogF64()
std::vector< double > CNTRIN_double
Readings from ticks counters, such as quadrature encoders.
void BASE_IMPEXP joinThread(TThreadHandle &threadHandle)
Waits until the given thread ends.
Definition: threads.cpp:74
struct HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ao_t ao
Analog outputs.
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")
std::string sensorLabel
An arbitrary label that can be used to identify the sensor.
unsigned __int64 uint64_t
Definition: rptypes.h:52
#define MRPT_DAQmxCreateAIVoltageChan
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
std::vector< double > AIN_double
Readings from analog input (ADCs) channels (vector length=channel count) in Volts.
mrpt::system::TTimeStamp timestamp
The associated UTC time-stamp. Where available, this should contain the accurate satellite-based time...
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...
void doProcess()
This method will be invoked at a minimum rate of "process_rate" (Hz)
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")
#define ASSERT_(f)
#define MRPT_DAQmxCreateCOPulseChanFreq
uint64_t read_uint64_t(const std::string &section, const std::string &name, uint64_t defaultValue, bool failIfNotFound=false) const
#define MRPT_DAQmx_ErrChk(functionCall)
unsigned int physicalChannelCount
IMPORTANT This must be the total number of channels listed in "physicalChannel" (e.g. 4 for "Dev1/ai0:3")
std::string BASE_IMPEXP trim(const std::string &str)
Removes leading and trailing spaces.
struct HWDRIVERS_IMPEXP mrpt::hwdrivers::CNationalInstrumentsDAQ::TaskDescription::desc_ci_ang_encoder_t ci_ang_encoder
Counter: uses an angular encoder to measure angular position.
#define MRPT_DAQmxClearTask
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.
std::vector< uint8_t > DIN
Present output values for 16-bit analog output (DACs) channels (vector length=channel count) in volts...
bool BASE_IMPEXP strCmpI(const std::string &s1, const std::string &s2)
Return true if the two strings are equal (case insensitive)
unsigned __int32 uint32_t
Definition: rptypes.h:49
#define ASSERTMSG_(f, __ERROR_MSG)
void BASE_IMPEXP tokenize(const std::string &inString, const std::string &inDelimiters, std::deque< std::string > &outTokens, bool skipBlankTokens=true) MRPT_NO_THROWS
Tokenizes a string according to a set of delimiting characters.
#define MRPT_DAQmxReadDigitalU8
#define MRPT_DAQmxCfgSampClkTiming
#define MRPT_DAQmxCreateCICountEdgesChan
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_ci_period_t ci_period
Counter: period of a digital signal.
#define MRPT_DAQmxCreateTask
static void createPipe(ReadPtr &outReadPipe, WritePtr &outWritePipe)
Creates a new pipe and returns the read & write end-points as newly allocated objects.
Definition: CPipe.h:131



Page generated by Doxygen 1.8.14 for MRPT 1.5.9 Git: 690a4699f Wed Apr 15 19:29:53 2020 +0200 at miƩ abr 15 19:30:12 CEST 2020