nidaqmxdriver.cpp
1 /***************************************************************************
2  Copyright (C) 2002-2015 Kentaro Kitagawa
3  kitagawa@phys.s.u-tokyo.ac.jp
4 
5  This program is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License as published by the Free Software Foundation; either
8  version 2 of the License, or (at your option) any later version.
9 
10  You should have received a copy of the GNU Library General
11  Public License and a list of authors along with this program;
12  see the files COPYING and AUTHORS.
13 ***************************************************************************/
14 #include "nidaqmxdriver.h"
15 #if !defined __WIN32__ && !defined WINDOWS && !defined _WIN32
16  #include <sys/errno.h>
17 #endif
18 //#include <boost/math/common_factor.hpp>
19 //using boost::math::lcm;
20 
21 inline int unsigned gcd(unsigned int a, unsigned int b){
22  if( !b) return a;
23  return gcd(b, a % b);
24 }
25 inline unsigned int lcm(unsigned int a, unsigned int b){
26  return a * b / gcd(a,b);
27 }
28 
29 //struct ProductInfo {
30 // const char *type;
31 // const char *series;
32 // int flags;
33 // unsigned long ai_max_rate; //!< [kHz]
34 // unsigned long ao_max_rate; //!< [kHz]
35 // unsigned long di_max_rate; //!< [kHz]
36 // unsigned long do_max_rate; //!< [kHz]
37 //};
39 XNIDAQmxInterface::sc_productInfoList[] = {
40  {"PCI-6110", "S", 0, 5000uL, 1000uL, 0, 0},
41  {"PXI-6110", "S", 0, 5000uL, 1000uL, 0, 0},
42  {"PCI-6111", "S", 0, 5000uL, 1000uL, 0, 0},
43  {"PXI-6111", "S", 0, 5000uL, 1000uL, 0, 0},
44  {"PCI-6115", "S", 0, 10000uL, 2500uL, 10000uL, 10000uL},
45  {"PXI-6115", "S", 0, 10000uL, 2500uL, 10000uL, 10000uL},
46  {"PCI-6120", "S", 0, 800uL, 2500uL, 5000uL, 5000uL},
47  {"PCI-6220", "M", 0, 250uL, 0, 1000uL, 1000uL},
48  {"PXI-6220", "M", 0, 250uL, 0, 1000uL, 1000uL},
49  {"PCI-6221", "M", 0, 250uL, 740uL, 1000uL, 1000uL},
50  {"PXI-6221", "M", 0, 250uL, 740uL, 1000uL, 1000uL},
51  {"PCI-6224", "M", 0, 250uL, 0, 1000uL, 1000uL},
52  {"PXI-6224", "M", 0, 250uL, 0, 1000uL, 1000uL},
53  {"PCI-6229", "M", 0, 250uL, 625uL, 1000uL, 1000uL},
54  {"PXI-6229", "M", 0, 250uL, 625uL, 1000uL, 1000uL},
55  {"PCI-6250", "M", 0, 1000uL, 0uL, 5000uL, 5000uL},
56  {"PXI-6250", "M", 0, 1000uL, 0uL, 5000uL, 5000uL},
57  {"PCI-6251", "M", 0, 1000uL, 1000uL, 5000uL, 5000uL},
58  {"PCIe-6251", "M", 0, 1000uL, 1000uL, 5000uL, 5000uL},
59  {"PXI-6251", "M", 0, 1000uL, 1000uL, 5000uL, 5000uL},
60  {"PCI-6254", "M", 0, 1000uL, 0uL, 5000uL, 5000uL},
61  {"PXI-6254", "M", 0, 1000uL, 0uL, 5000uL, 5000uL},
62  {"PCI-6255", "M", 0, 750uL, 1000uL, 5000uL, 5000uL},
63  {"PXI-6255", "M", 0, 750uL, 1000uL, 5000uL, 5000uL},
64  {"PCI-6255", "M", 0, 1000uL, 1000uL, 5000uL, 5000uL},
65  {"PCIe-6255", "M", 0, 1000uL, 1000uL, 5000uL, 5000uL},
66  {"PXI-6255", "M", 0, 1000uL, 1000uL, 5000uL, 5000uL},
67  {"PCIe-6321", "X", 0, 250uL, 900uL, 1000uL, 1000uL},
68  {"PCIe-6323", "X", 0, 250uL, 900uL, 1000uL, 1000uL},
69  {"PCIe-6341", "X", 0, 500uL, 900uL, 1000uL, 1000uL},
70  {"PCIe-6343", "X", 0, 500uL, 900uL, 1000uL, 1000uL},
71  {"PCIe-6351", "X", 0, 1000uL, 1000uL, 5000uL, 5000uL},
72  {"PCIe-6353", "X", 0, 1000uL, 1000uL, 5000uL, 5000uL},
73  {"PCIe-6361", "X", 0, 1000uL, 1000uL, 5000uL, 5000uL},
74  {"PCIe-6363", "X", 0, 1000uL, 1000uL, 5000uL, 5000uL},
75  {0, 0, 0, 0, 0, 0, 0},
76 };
77 
78 //for synchronization.
79 static XString g_pciClockMaster; //Device exporting clock to RTSI7.
80 static float64 g_pciClockMasterRate = 0.0;
81 static XString g_pciClockExtRefTerm; //External Reference Clock
82 static float64 g_pciClockExtRefRate;
83 static TaskHandle g_pciClockMasterTask = (TaskHandle)-1;
84 static int g_daqmx_open_cnt;
85 static XMutex g_daqmx_mutex;
86 static std::deque<shared_ptr<XNIDAQmxInterface::XNIDAQmxRoute> > g_daqmx_sync_routes;
87 
89 XNIDAQmxInterface::SoftwareTrigger::s_virtualTrigList(new XNIDAQmxInterface::SoftwareTrigger::SoftwareTriggerList);
90 XNIDAQmxInterface::SoftwareTrigger::STRGTalker XNIDAQmxInterface::SoftwareTrigger::s_onChange;
91 
92 shared_ptr<XNIDAQmxInterface::SoftwareTrigger>
93 XNIDAQmxInterface::SoftwareTrigger::create(const char *label, unsigned int bits) {
94  shared_ptr<SoftwareTrigger> p(new SoftwareTrigger(label, bits));
95 
96  //inserting the new trigger source to the list atomically.
97  for(local_shared_ptr<SoftwareTriggerList> old_list(s_virtualTrigList);;) {
98  local_shared_ptr<SoftwareTriggerList> new_list(new SoftwareTriggerList( *old_list));
99  new_list->push_back(p);
100  if(s_virtualTrigList.compareAndSwap(old_list, new_list)) break;
101  }
102  onChange().talk(p);
103  return p;
104 }
105 
106 XNIDAQmxInterface::SoftwareTrigger::SoftwareTrigger(const char *label, unsigned int bits)
107  : m_label(label), m_bits(bits),
108  m_risingEdgeMask(0u), m_fallingEdgeMask(0u) {
109  clear_();
110  m_isPersistentCoherent = false;
111 }
112 void
113 XNIDAQmxInterface::SoftwareTrigger::unregister(const shared_ptr<SoftwareTrigger> &p) {
114  //performing it atomically.
115  for(local_shared_ptr<SoftwareTriggerList> old_list(s_virtualTrigList);;) {
116  local_shared_ptr<SoftwareTriggerList> new_list(new SoftwareTriggerList( *old_list));
117  new_list->erase(std::find(new_list->begin(), new_list->end(), p));
118  if(s_virtualTrigList.compareAndSwap(old_list, new_list)) break;
119  }
120  onChange().talk(p);
121 }
122 void
123 XNIDAQmxInterface::SoftwareTrigger::clear_() {
124  uint64_t x;
125  while(FastQueue::key t = m_fastQueue.atomicFront(&x)) {
126  m_fastQueue.atomicPop(t);
127  }
128  m_slowQueue.clear();
129  m_slowQueueSize = 0;
130 }
131 void
132 XNIDAQmxInterface::SoftwareTrigger::stamp(uint64_t cnt) {
133  readBarrier();
134  if(cnt < m_endOfBlank) return;
135  if(cnt == 0) return; //ignore.
136  try {
137  m_fastQueue.push(cnt);
138  }
139  catch (FastQueue::nospace_error&) {
140  XScopedLock<XMutex> lock(m_mutex);
141  fprintf(stderr, "Slow queue!\n");
142  m_slowQueue.push_back(cnt);
143  if(m_slowQueue.size() > 100000u)
144  m_slowQueue.pop_front();
145  else
146  ++m_slowQueueSize;
147  }
148  m_endOfBlank = cnt + m_blankTerm;
149 }
150 void
151 XNIDAQmxInterface::SoftwareTrigger::start(float64 freq) {
152  {
153  XScopedLock<XMutex> lock(m_mutex);
154  m_endOfBlank = 0;
155  if(!m_blankTerm) m_blankTerm = lrint(0.02 * freq);
156  m_freq = freq;
157  clear_();
158  }
159  onStart().talk(shared_from_this());
160 }
161 
162 void
163 XNIDAQmxInterface::SoftwareTrigger::stop() {
164  XScopedLock<XMutex> lock(m_mutex);
165  clear_();
166  m_endOfBlank = (uint64_t)-1LL;
167 }
168 void
169 XNIDAQmxInterface::SoftwareTrigger::connect(uint32_t rising_edge_mask,
170  uint32_t falling_edge_mask) throw (XInterface::XInterfaceError &) {
171  XScopedLock<XMutex> lock(m_mutex);
172  clear_();
173  if(m_risingEdgeMask || m_fallingEdgeMask)
175  i18n_noncontext("Duplicated connection to virtual trigger is not supported."), __FILE__, __LINE__);
176  m_risingEdgeMask = rising_edge_mask;
177  m_fallingEdgeMask = falling_edge_mask;
178 }
179 void
180 XNIDAQmxInterface::SoftwareTrigger::disconnect() {
181  XScopedLock<XMutex> lock(m_mutex);
182  clear_();
183  m_risingEdgeMask = 0;
184  m_fallingEdgeMask = 0;
185 }
186 uint64_t
187 XNIDAQmxInterface::SoftwareTrigger::tryPopFront(uint64_t threshold, float64 freq__) {
188  unsigned int freq_em = lrint(freq());
189  unsigned int freq_rc = lrint(freq__);
190  unsigned int gcd__ = gcd(freq_em, freq_rc);
191 
192  uint64_t cnt;
193  if(m_slowQueueSize) {
194  XScopedLock<XMutex> lock(m_mutex);
195  if(FastQueue::key t = m_fastQueue.atomicFront(&cnt)) {
196  if((cnt < m_slowQueue.front()) || !m_slowQueueSize) {
197  cnt = (cnt * (freq_rc / gcd__)) / (freq_em / gcd__);
198  if(cnt >= threshold)
199  return 0uLL;
200  if(m_fastQueue.atomicPop(t))
201  return cnt;
202  return 0uLL;
203  }
204  }
205  if( !m_slowQueueSize)
206  return 0uLL;
207  cnt = m_slowQueue.front();
208  cnt = (cnt * (freq_rc / gcd__)) / (freq_em / gcd__);
209  if(cnt >= threshold)
210  return 0uLL;
211  m_slowQueue.pop_front();
212  --m_slowQueueSize;
213  return cnt;
214  }
215  if(FastQueue::key t = m_fastQueue.atomicFront(&cnt)) {
216  cnt = (cnt * (freq_rc / gcd__)) / (freq_em / gcd__);
217  if(cnt >= threshold)
218  return 0uLL;
219  if(m_fastQueue.atomicPop(t))
220  return cnt;
221  }
222  return 0uLL;
223 }
224 
225 void
227  XScopedLock<XMutex> lock(m_mutex);
228  clear_();
229  m_endOfBlank = 0;
230 }
231 void
232 XNIDAQmxInterface::SoftwareTrigger::clear(uint64_t now, float64 freq__) {
233  unsigned int freq_em= lrint(freq());
234  unsigned int freq_rc = lrint(freq__);
235  unsigned int gcd__ = gcd(freq_em, freq_rc);
236  now = (now * (freq_em / gcd__)) / (freq_rc / gcd__);
237 
238  XScopedLock<XMutex> lock(m_mutex);
239  uint64_t x;
240  while(FastQueue::key t = m_fastQueue.atomicFront(&x)) {
241  if(x <= now)
242  m_fastQueue.atomicPop(t);
243  else
244  break;
245  }
246  while(m_slowQueue.size() && (m_slowQueue.front() <= now)) {
247  m_slowQueue.pop_front();
248  --m_slowQueueSize;
249  }
250 }
251 void
252 XNIDAQmxInterface::SoftwareTrigger::forceStamp(uint64_t now, float64 freq__) {
253  unsigned int freq_em= lrint(freq());
254  unsigned int freq_rc = lrint(freq__);
255  unsigned int gcd__ = gcd(freq_em, freq_rc);
256  now = (now * (freq_em / gcd__)) / (freq_rc / gcd__);
257 
258  XScopedLock<XMutex> lock(m_mutex);
259  ++m_slowQueueSize;
260  m_slowQueue.push_front(now);
261  std::sort(m_slowQueue.begin(), m_slowQueue.end());
262 }
263 
264 const char *
266 #ifdef HAVE_NI_DAQMX
267  int32 bus;
268  DAQmxGetDevBusType(devName(), &bus);
269  switch(bus) {
270  case DAQmx_Val_PCI:
271  case DAQmx_Val_PCIe:
272  return "PCI";
273  case DAQmx_Val_PXI:
274 // case DAQmx_Val_PXIe:
275  return "PXI";
276  case DAQmx_Val_USB:
277  return "USB";
278  default:
279  return "Unknown";
280  }
281 #else
282  return "n/a";
283 #endif //HAVE_NI_DAQMX
284 }
285 void
286 XNIDAQmxInterface::synchronizeClock(TaskHandle task) {
287  float64 rate = g_pciClockMasterRate;
288  XString src = formatString("/%s/RTSI7", devName());
289  if(devName() == g_pciClockMaster) {
290  src = g_pciClockExtRefTerm;
291  rate = g_pciClockExtRefRate;
292  if( !src.length())
293  return;
294  }
295 
296  if((productSeries() == XString("M")) || (productSeries() == XString("X"))) {
297  if(busArchType() == XString("PCI")) {
298  CHECK_DAQMX_RET(DAQmxSetRefClkSrc(task, src.c_str()));
299  CHECK_DAQMX_RET(DAQmxSetRefClkRate(task, rate));
300  }
301  if(busArchType() == XString("PXI")) {
302  CHECK_DAQMX_RET(DAQmxSetRefClkSrc(task,"PXI_Clk10"));
303  CHECK_DAQMX_RET(DAQmxSetRefClkRate(task, 10e6));
304  }
305  }
306  if(productSeries() == XString("S")) {
307  if(busArchType() == XString("PCI")) {
308  CHECK_DAQMX_RET(DAQmxSetMasterTimebaseSrc(task, src.c_str()));
309  CHECK_DAQMX_RET(DAQmxSetMasterTimebaseRate(task, rate));
310  }
311  if(busArchType() == XString("PXI")) {
312  CHECK_DAQMX_RET(DAQmxSetMasterTimebaseSrc(task,"PXI_Clk10"));
313  CHECK_DAQMX_RET(DAQmxSetMasterTimebaseRate(task, 10e6));
314  }
315  }
316 }
317 
318 XString
319 XNIDAQmxInterface::getNIDAQmxErrMessage()
320 {
321 #ifdef HAVE_NI_DAQMX
322  char str[2048];
323  DAQmxGetExtendedErrorInfo(str, sizeof(str));
324  errno = 0;
325  return XString(str);
326 #else
327  return XString();
328 #endif //HAVE_NI_DAQMX
329 }
330 XString
331 XNIDAQmxInterface::getNIDAQmxErrMessage(int status) {
332 #ifdef HAVE_NI_DAQMX
333  char str[2048];
334  DAQmxGetErrorString(status, str, sizeof(str));
335  errno = 0;
336  return XString(str);
337 #else
338  return XString();
339 #endif //HAVE_NI_DAQMX
340 }
341 int
342 XNIDAQmxInterface::checkDAQmxError(int ret, const char*file, int line) {
343  if(ret >= 0) return ret;
344  errno = 0;
345  throw XInterface::XInterfaceError(getNIDAQmxErrMessage(), file, line);
346  return 0;
347 }
348 
349 void
350 XNIDAQmxInterface::parseList(const char *str, std::deque<XString> &list)
351 {
352  list.clear();
353  XString org(str);
354  const char *del = ", \t";
355  for(int pos = 0; pos != std::string::npos; ) {
356  int spos = org.find_first_not_of(del, pos);
357  if(spos == std::string::npos) break;
358  pos = org.find_first_of(del, spos);
359  if(pos == std::string::npos)
360  list.push_back(org.substr(spos));
361  else
362  list.push_back(org.substr(spos, pos - spos));
363  }
364 }
365 
366 
367 XNIDAQmxInterface::XNIDAQmxInterface(const char *name, bool runtime, const shared_ptr<XDriver> &driver) :
368  XInterface(name, runtime, driver),
369  m_productInfo(0L) {
370 #ifdef HAVE_NI_DAQMX
371  char buf[2048];
372  CHECK_DAQMX_RET(DAQmxGetSysDevNames(buf, sizeof(buf)));
373  std::deque<XString> list;
374  parseList(buf, list);
375  iterate_commit([=, &buf](Transaction &tr){
376  for(auto it = list.cbegin(); it != list.cend(); ++it) {
377  CHECK_DAQMX_RET(DAQmxGetDevProductType(it->c_str(), buf, sizeof(buf)));
378  tr[ *device()].add(*it + " [" + buf + "]");
379  }
380  });
381 #endif //HAVE_NI_DAQMX
382 }
383 XNIDAQmxInterface::XNIDAQmxRoute::XNIDAQmxRoute(const char*src, const char*dst, int *pret)
384  : m_src(src), m_dst(dst) {
385 #ifdef HAVE_NI_DAQMX
386  if(pret) {
387  int ret = 0;
388  ret = DAQmxConnectTerms(src, dst, DAQmx_Val_DoNotInvertPolarity);
389  if(ret < 0)
390  m_src.clear();
391  *pret = ret;
392  }
393  else {
394  try {
395  CHECK_DAQMX_ERROR(DAQmxConnectTerms(src, dst, DAQmx_Val_DoNotInvertPolarity));
396  dbgPrint(QString("Connect route from %1 to %2.").arg(src).arg(dst));
397  }
398  catch (XInterface::XInterfaceError &e) {
399  e.print();
400  m_src.clear();
401  }
402  }
403 #endif //HAVE_NI_DAQMX
404 }
405 XNIDAQmxInterface::XNIDAQmxRoute::~XNIDAQmxRoute() {
406  if(!m_src.length()) return;
407  try {
408  CHECK_DAQMX_RET(DAQmxDisconnectTerms(m_src.c_str(), m_dst.c_str()));
409  dbgPrint(QString("Disconnect route from %1 to %2.").arg(m_src).arg(m_dst));
410  }
411  catch (XInterface::XInterfaceError &e) {
412  e.print();
413  }
414 }
415 void
416 XNIDAQmxInterface::open() throw (XInterfaceError &) {
417 #ifdef HAVE_NI_DAQMX
418  char buf[256];
419 
420  Snapshot shot( *this);
421  if(sscanf(shot[ *device()].to_str().c_str(), "%255s", buf) != 1)
422  throw XOpenInterfaceError(__FILE__, __LINE__);
423 
424  XScopedLock<XMutex> lock(g_daqmx_mutex);
425  if(g_daqmx_open_cnt == 0) {
426  //Routes the master clock for synchronizations.
427  g_pciClockExtRefTerm.clear();
428 // CHECK_DAQMX_RET(DAQmxCreateTask("", &g_task_sync_master));
429  char buf[2048];
430  CHECK_DAQMX_RET(DAQmxGetSysDevNames(buf, sizeof(buf)));
431  std::deque<XString> list;
432  XNIDAQmxInterface::parseList(buf, list);
433  std::deque<XString> pcidevs;
434  for(std::deque<XString>::iterator it = list.begin(); it != list.end(); ++it) {
435  // Device reset.
436  DAQmxResetDevice(it->c_str());
437  // Search for master clock among PCI(e) devices.
438  int32 bus;
439  DAQmxGetDevBusType(it->c_str(), &bus);
440  if((bus == DAQmx_Val_PCI) || (bus == DAQmx_Val_PCIe)) {
441  pcidevs.push_back(*it);
442  }
443  }
444  if(pcidevs.size() > 1) {
445  for(std::deque<XString>::iterator it = pcidevs.begin(); it != pcidevs.end(); ++it) {
446  //M series only.
447  CHECK_DAQMX_RET(DAQmxGetDevProductType(it->c_str(), buf, sizeof(buf)));
448  XString type = buf;
449  for(const ProductInfo *pit = sc_productInfoList; pit->type; pit++) {
450  if((pit->type == type) && ((pit->series == XString("M")) || (pit->series == XString("X")) )) {
451  XString inp_term = formatString("/%s/PFI0", it->c_str());
452  //Detects external clock source.
453  if(routeExternalClockSource(it->c_str(), inp_term.c_str())) {
454  fprintf(stderr, "Reference Clock for PLL Set to %s\n", inp_term.c_str());
455  g_pciClockMaster = *it;
456  pcidevs.clear();
457  pcidevs.push_back(g_pciClockMaster);
458  }
459  break;
460  }
461  }
462  if(g_pciClockMaster.length())
463  break;
464  }
465  for(std::deque<XString>::iterator it = pcidevs.begin(); it != pcidevs.end(); ++it) {
466  //AO of S series device cannot handle external master clock properly.
467  //Thus S should be a master when high-speed AOs are used.
468  CHECK_DAQMX_RET(DAQmxGetDevProductType(it->c_str(), buf, sizeof(buf)));
469  XString type = buf;
470  for(const ProductInfo *pit = sc_productInfoList; pit->type; ++pit) {
471  if((pit->type == type) && (pit->series == XString("S"))) {
472  //RTSI synchronizations.
473  shared_ptr<XNIDAQmxInterface::XNIDAQmxRoute> route;
474  float64 freq = 20.0e6;
475  route.reset(new XNIDAQmxInterface::XNIDAQmxRoute(
476  formatString("/%s/20MHzTimebase", it->c_str()).c_str(),
477  formatString("/%s/RTSI7", it->c_str()).c_str()));
478  g_daqmx_sync_routes.push_back(route);
479  fprintf(stderr, "20MHz Reference Clock exported from %s\n", it->c_str());
480  g_pciClockMaster = *it;
481  g_pciClockMasterRate = freq;
482  pcidevs.clear();
483  break;
484  }
485  }
486  if(g_pciClockMaster.length())
487  break;
488  }
489  for(std::deque<XString>::iterator it = pcidevs.begin(); it != pcidevs.end(); ++it) {
490  //M series only.
491  CHECK_DAQMX_RET(DAQmxGetDevProductType(it->c_str(), buf, sizeof(buf)));
492  XString type = buf;
493  for(const ProductInfo *pit = sc_productInfoList; pit->type; pit++) {
494  if((pit->type == type) && ((pit->series == XString("M")) || (pit->series == XString("X")) )) {
495  //Detects external clock source.
496  fprintf(stderr, "20MHz Reference Clock exported from %s\n", it->c_str());
497  //M series device cannot export 20MHzTimebase freely.
498  XString ctrdev = formatString("%s/ctr1", it->c_str());
499  //Continuous pulse train generation. Duty = 50%.
500  CHECK_DAQMX_RET(DAQmxCreateTask("", &g_pciClockMasterTask));
501  float64 freq = 20e6; //20MHz
502  CHECK_DAQMX_RET(DAQmxCreateCOPulseChanFreq(g_pciClockMasterTask,
503  ctrdev.c_str(), "", DAQmx_Val_Hz, DAQmx_Val_Low, 0.0,
504  freq, 0.5));
505  CHECK_DAQMX_RET(DAQmxCfgImplicitTiming(g_pciClockMasterTask, DAQmx_Val_ContSamps, 1000));
506  CHECK_DAQMX_RET(DAQmxSetCOPulseTerm(g_pciClockMasterTask, ctrdev.c_str(), formatString("/%s/RTSI7", it->c_str()).c_str()));
507  if(g_pciClockExtRefTerm.length()) {
508  CHECK_DAQMX_RET(DAQmxSetRefClkSrc(g_pciClockMasterTask, g_pciClockExtRefTerm.c_str()));
509  CHECK_DAQMX_RET(DAQmxSetRefClkRate(g_pciClockMasterTask, g_pciClockExtRefRate));
510  }
511  CHECK_DAQMX_RET(DAQmxStartTask(g_pciClockMasterTask));
512  g_pciClockMaster = *it;
513  g_pciClockMasterRate = freq;
514  pcidevs.clear();
515  break;
516  }
517  }
518  if(g_pciClockMaster.length())
519  break;
520  }
521  }
522  }
523  g_daqmx_open_cnt++;
524 
525  XString devname = buf;
526  CHECK_DAQMX_RET(DAQmxGetDevProductType(devname.c_str(), buf, sizeof(buf)));
527  XString type = buf;
528 
529  m_productInfo = NULL;
530  for(const ProductInfo *it = sc_productInfoList; it->type; it++) {
531  if(it->type == type) {
532  m_productInfo = it;
533  m_devname = devname;
534  return;
535  }
536  }
537  throw XInterfaceError(i18n("No device info. for product [%1].").arg(type), __FILE__, __LINE__);
538 #endif //HAVE_NI_DAQMX
539 }
540 bool
541 XNIDAQmxInterface::routeExternalClockSource(const char *dev, const char *inp_term) {
542 #ifdef HAVE_NI_DAQMX
543  XString ctrdev = formatString("/%s/ctr0", dev);
544  TaskHandle taskCounting = 0;
545  //Measures an external source frequency.
546  CHECK_DAQMX_RET(DAQmxCreateTask("",&taskCounting));
547  CHECK_DAQMX_RET(DAQmxCreateCIFreqChan(taskCounting,
548  ctrdev.c_str(), "", 1000000, 25000000, DAQmx_Val_Hz,
549  DAQmx_Val_Rising, DAQmx_Val_LargeRng2Ctr, 0.01, 100, NULL));
550  CHECK_DAQMX_RET(DAQmxCfgImplicitTiming(taskCounting, DAQmx_Val_ContSamps, 1000));
551  CHECK_DAQMX_RET(DAQmxSetCIFreqTerm(taskCounting, ctrdev.c_str(), inp_term));
552 
553  CHECK_DAQMX_RET(DAQmxStartTask(taskCounting));
554  float64 data[1000];
555  int32 cnt;
556  int32 ret = DAQmxReadCounterF64(taskCounting, 1000, 0.05, data,1000, &cnt, 0);
557  float64 freq = 0.0;
558  for(int i = cnt / 2; i < cnt; ++i)
559  freq += data[i];
560  freq /= cnt - cnt / 2; //averaging.
561  DAQmxStopTask(taskCounting);
562  DAQmxClearTask(taskCounting);
563  if(ret)
564  return false;
565  fprintf(stderr, "%.5g Hz detected at the counter input term %s.\n", (double)freq, inp_term);
566 
567  uint64_t freq_cand[] = {10000000, 20000000, 0};
568  for(uint64_t *f = freq_cand; *f; ++f) {
569  if(fabs(freq - *f) < *f * 0.001) {
570  g_pciClockExtRefTerm = inp_term;
571  g_pciClockExtRefRate = *f;
572  return true;
573  }
574  }
575 #endif //HAVE_NI_DAQMX
576 
577  return false;
578 }
579 void
580 XNIDAQmxInterface::close() throw (XInterfaceError &) {
581  m_productInfo = NULL;
582  if(m_devname.length()) {
583  m_devname.clear();
584 
585  XScopedLock<XMutex> lock(g_daqmx_mutex);
586  g_daqmx_open_cnt--;
587  if(g_daqmx_open_cnt == 0) {
588  if(g_pciClockMasterTask != (TaskHandle)-1) {
589  CHECK_DAQMX_RET(DAQmxClearTask(g_pciClockMasterTask));
590  }
591  g_pciClockMasterTask = (TaskHandle)-1;
592  g_daqmx_sync_routes.clear();
593  }
594  }
595 }

Generated for KAME4 by  doxygen 1.8.3