nidaqdso.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 //! \todo sub-sampling rate synchronization.
15 
16 #include "nidaqdso.h"
17 
18 #ifndef HAVE_NI_DAQMX
19  #define DAQmx_Val_FallingSlope 0
20  #define DAQmx_Val_RisingSlope 0
21  #define DAQmx_Val_DigEdge 0
22  #define DAQmxGetReadAvailSampPerChan(x,y) 0
23 #endif //HAVE_NI_DAQMX
24 
25 #include <qmessagebox.h>
26 #include "xwavengraph.h"
27 
28 REGISTER_TYPE(XDriverList, NIDAQmxDSO, "National Instruments DAQ as DSO");
29 
30 #define TASK_UNDEF ((TaskHandle)-1)
31 #define NUM_MAX_CH 4
32 
33 XNIDAQmxDSO::XNIDAQmxDSO(const char *name, bool runtime,
34  Transaction &tr_meas, const shared_ptr<XMeasure> &meas) :
35  XNIDAQmxDriver<XDSO>(name, runtime, ref(tr_meas), meas),
36  m_dsoRawRecordBankLatest(0),
37  m_task(TASK_UNDEF) {
38 
39  iterate_commit([=](Transaction &tr){
40  tr[ *recordLength()] = 2000;
41  tr[ *timeWidth()] = 1e-2;
42  tr[ *average()] = 1;
43  for(auto &&x: {vFullScale1(), vFullScale2(), vFullScale3(), vFullScale4()}) {
44  tr[ *x].add({"0.4", "1", "2", "4", "10", "20", "40", "84"});
45  tr[ *x] = "20";
46  }
47  });
48  if(isMemLockAvailable()) {
49  const void *FIRST_OF_MLOCK_MEMBER = &m_recordBuf;
50  const void *LAST_OF_MLOCK_MEMBER = &m_task;
51  //Suppress swapping.
52  mlock(FIRST_OF_MLOCK_MEMBER, (size_t)LAST_OF_MLOCK_MEMBER - (size_t)FIRST_OF_MLOCK_MEMBER);
53  }
54 
55  vOffset1()->disable();
56  vOffset2()->disable();
57  vOffset3()->disable();
58  vOffset4()->disable();
59 }
60 XNIDAQmxDSO::~XNIDAQmxDSO() {
61  clearAcquision();
62 }
63 void
64 XNIDAQmxDSO::onSoftTrigChanged(const shared_ptr<XNIDAQmxInterface::SoftwareTrigger> &) {
65  iterate_commit([=](Transaction &tr){
66  tr[ *trigSource()].clear();
67  XString series = interface()->productSeries();
68  {
69  char buf[2048];
70  {
71  CHECK_DAQMX_RET(DAQmxGetDevAIPhysicalChans(interface()->devName(), buf, sizeof(buf)));
72  std::deque<XString> chans;
73  XNIDAQmxInterface::parseList(buf, chans);
74  for(std::deque<XString>::iterator it = chans.begin(); it != chans.end(); ++it) {
75  tr[ *trigSource()].add(it->c_str());
76  }
77  }
78  //M series
79  const char* sc_m[] = {
80  "PFI0", "PFI1", "PFI2", "PFI3", "PFI4", "PFI5", "PFI6", "PFI7",
81  "PFI8", "PFI9", "PFI10", "PFI11", "PFI12", "PFI13", "PFI14", "PFI15",
82  "Ctr0InternalOutput", "Ctr1InternalOutput",
83  "Ctr0Source",
84  "Ctr0Gate",
85  "Ctr1Source",
86  "Ctr1Gate",
87  "FrequencyOutput",
88  0L};
89  //S series
90  const char* sc_s[] = {
91  "PFI0", "PFI1", "PFI2", "PFI3", "PFI4", "PFI5", "PFI6", "PFI7",
92  "PFI8", "PFI9",
93  "Ctr0InternalOutput",
94  "OnboardClock",
95  "Ctr0Source",
96  "Ctr0Gate",
97  0L};
98  const char **sc = sc_m;
99  if(series == "S")
100  sc = sc_s;
101  for(const char **it = sc; *it; it++) {
102  XString str(formatString("/%s/%s", interface()->devName(), *it));
103  tr[ *trigSource()].add(str);
104  }
106  list(XNIDAQmxInterface::SoftwareTrigger::virtualTrigList());
107  for(XNIDAQmxInterface::SoftwareTrigger::SoftwareTriggerList_it
108  it = list->begin(); it != list->end(); ++it) {
109  for(unsigned int i = 0; i < ( *it)->bits(); i++) {
110  tr[ *trigSource()].add(
111  formatString("%s/line%d", ( *it)->label(), i));
112  }
113  }
114  }
115  tr.unmark(m_lsnOnTrigSourceChanged); //avoids nested transactions.
116  });
117 }
118 void
120  XScopedLock<XInterface> lock( *interface());
121  m_running = false;
122  char buf[2048];
123  {
124  CHECK_DAQMX_RET(DAQmxGetDevAIPhysicalChans(interface()->devName(), buf, sizeof(buf)));
125  std::deque<XString> chans;
126  XNIDAQmxInterface::parseList(buf, chans);
127  iterate_commit([=](Transaction &tr){
128  for(auto it = chans.cbegin(); it != chans.cend(); ++it) {
129  for(auto &&x: {trace1(), trace2(), trace3(), trace4()})
130  tr[ *x].add(it->c_str());
131  }
132  });
133  }
134  onSoftTrigChanged(shared_ptr<XNIDAQmxInterface::SoftwareTrigger>());
135 
136  m_suspendRead = true;
137  m_threadReadAI.reset(new XThread<XNIDAQmxDSO>(shared_from_this(),
138  &XNIDAQmxDSO::executeReadAI));
139  m_threadReadAI->resume();
140 
141  this->start();
142 
143  m_lsnOnSoftTrigChanged = XNIDAQmxInterface::SoftwareTrigger::onChange().connectWeakly(
144  shared_from_this(), &XNIDAQmxDSO::onSoftTrigChanged,
145  XListener::FLAG_MAIN_THREAD_CALL);
146  createChannels();
147 }
148 void
150  XScopedLock<XInterface> lock( *interface());
151 
152  m_lsnOnSoftTrigChanged.reset();
153 
154  clearAcquision();
155 
156  if(m_threadReadAI) {
157  m_threadReadAI->terminate();
158  }
159 
160  iterate_commit([=](Transaction &tr){
161  for(auto &&x: {trace1(), trace2(), trace3(), trace4()})
162  tr[ *x].clear();
163  });
164 
165  m_recordBuf.clear();
166  m_record_av.clear();
167 
168  interface()->stop();
169 }
170 void
171 XNIDAQmxDSO::clearAcquision() {
172  XScopedLock<XInterface> lock( *interface());
173  m_suspendRead = true;
174  XScopedLock<XRecursiveMutex> lock2(m_readMutex);
175 
176  try {
177  disableTrigger();
178  }
179  catch (XInterface::XInterfaceError &e) {
180  e.print();
181  }
182 
183  if(m_task != TASK_UNDEF) {
184  CHECK_DAQMX_RET(DAQmxClearTask(m_task));
185  }
186  m_task = TASK_UNDEF;
187 }
188 void
189 XNIDAQmxDSO::disableTrigger() {
190  XScopedLock<XInterface> lock( *interface());
191  m_suspendRead = true;
192  XScopedLock<XRecursiveMutex> lock2(m_readMutex);
193 
194  if(m_running) {
195  m_running = false;
196  CHECK_DAQMX_RET(DAQmxStopTask(m_task));
197  }
198  if(m_task != TASK_UNDEF) {
199  uInt32 num_ch;
200  CHECK_DAQMX_RET(DAQmxGetTaskNumChans(m_task, &num_ch));
201  if(num_ch) {
202  CHECK_DAQMX_RET(DAQmxDisableStartTrig(m_task));
203  CHECK_DAQMX_RET(DAQmxDisableRefTrig(m_task));
204  }
205  }
206 
207  m_preTriggerPos = 0;
208 
209  //reset virtual trigger setup.
210  if(m_softwareTrigger)
211  m_softwareTrigger->disconnect();
212  m_lsnOnSoftTrigStarted.reset();
213  m_softwareTrigger.reset();
214 }
215 void
216 XNIDAQmxDSO::setupTrigger() {
217  XScopedLock<XInterface> lock( *interface());
218  Snapshot shot( *this);
219  m_suspendRead = true;
220  XScopedLock<XRecursiveMutex> lock2(m_readMutex);
221 
222  unsigned int pretrig = lrint(shot[ *trigPos()] / 100.0 * shot[ *recordLength()]);
223  m_preTriggerPos = pretrig;
224 
225  XString atrig;
226  XString dtrig;
227  XString src = shot[ *trigSource()].to_str();
228 
229  char buf[2048];
230  {
231  CHECK_DAQMX_RET(DAQmxGetDevAIPhysicalChans(interface()->devName(), buf, sizeof(buf)));
232  std::deque<XString> chans;
233  XNIDAQmxInterface::parseList(buf, chans);
234  for(std::deque<XString>::iterator it = chans.begin(); it != chans.end(); ++it) {
235  if(src == *it)
236  atrig = *it;
237  }
238  }
239  if( !atrig.length())
240  dtrig = src;
241 
242  int32 trig_spec = shot[ *trigFalling()] ? DAQmx_Val_FallingSlope : DAQmx_Val_RisingSlope;
243 
244  if(m_softwareTrigger) {
245  dtrig = m_softwareTrigger->armTerm();
246  trig_spec = DAQmx_Val_RisingSlope;
247  pretrig = 0;
248  CHECK_DAQMX_RET(DAQmxSetReadOverWrite(m_task, DAQmx_Val_OverwriteUnreadSamps));
249  m_softwareTrigger->setPersistentCoherentMode(shot[ *dRFMode()] >= 1);
250  }
251 
252  //Small # of pretriggers is not allowed for ReferenceTrigger.
253  if( !m_softwareTrigger && (pretrig < 2)) {
254  pretrig = 0;
255  m_preTriggerPos = pretrig;
256  }
257 
258  if( !pretrig) {
259  if(atrig.length()) {
260  CHECK_DAQMX_RET(
261  DAQmxCfgAnlgEdgeStartTrig(m_task,
262  atrig.c_str(), trig_spec, shot[ *trigLevel()]));
263  }
264  if(dtrig.length()) {
265  CHECK_DAQMX_RET(
266  DAQmxCfgDigEdgeStartTrig(m_task,
267  dtrig.c_str(), trig_spec));
268  }
269  }
270  else {
271  if(atrig.length()) {
272  CHECK_DAQMX_RET(
273  DAQmxCfgAnlgEdgeRefTrig(m_task,
274  atrig.c_str(), trig_spec, shot[ *trigLevel()], pretrig));
275  }
276  if(dtrig.length()) {
277  CHECK_DAQMX_RET(
278  DAQmxCfgDigEdgeRefTrig(m_task,
279  dtrig.c_str(), trig_spec, pretrig));
280  }
281  }
282 
283  char ch[256];
284  CHECK_DAQMX_RET(DAQmxGetTaskChannels(m_task, ch, sizeof(ch)));
285  if(interface()->productFlags() & XNIDAQmxInterface::FLAG_BUGGY_DMA_AI) {
286  CHECK_DAQMX_RET(DAQmxSetAIDataXferMech(m_task, ch,
287  DAQmx_Val_Interrupts));
288  }
289  if(interface()->productFlags() & XNIDAQmxInterface::FLAG_BUGGY_XFER_COND_AI) {
290  uInt32 bufsize;
291  CHECK_DAQMX_RET(DAQmxGetBufInputOnbrdBufSize(m_task, &bufsize));
292  CHECK_DAQMX_RET(
293  DAQmxSetAIDataXferReqCond(m_task, ch,
294  (m_softwareTrigger || (bufsize/2 < m_recordBuf.size())) ? DAQmx_Val_OnBrdMemNotEmpty :
295  DAQmx_Val_OnBrdMemMoreThanHalfFull));
296  }
297 }
298 void
299 XNIDAQmxDSO::setupSoftwareTrigger() {
300  Snapshot shot( *this);
301  XString src = shot[ *trigSource()].to_str();
302  //setup virtual trigger.
304  list(XNIDAQmxInterface::SoftwareTrigger::virtualTrigList());
305  for(XNIDAQmxInterface::SoftwareTrigger::SoftwareTriggerList_it
306  it = list->begin(); it != list->end(); ++it) {
307  for(unsigned int i = 0; i < ( *it)->bits(); i++) {
308  if(src == formatString("%s/line%d", ( *it)->label(), i)) {
309  m_softwareTrigger = *it;
310  m_softwareTrigger->connect(
311  !shot[ *trigFalling()] ? (1uL << i) : 0,
312  shot[ *trigFalling()] ? (1uL << i) : 0);
313  }
314  }
315  }
316 }
317 void
319  XScopedLock<XInterface> lock( *interface());
320  Snapshot shot( *this);
321  m_suspendRead = true;
322  XScopedLock<XRecursiveMutex> lock2(m_readMutex);
323 
324  if(m_running) {
325  m_running = false;
326  CHECK_DAQMX_RET(DAQmxStopTask(m_task));
327  }
328 
329  uInt32 num_ch;
330  CHECK_DAQMX_RET(DAQmxGetTaskNumChans(m_task, &num_ch));
331  if(num_ch == 0) return;
332 
333  disableTrigger();
334  setupSoftwareTrigger();
335 
336  const unsigned int len = shot[ *recordLength()];
337  for(unsigned int i = 0; i < 2; i++) {
338  DSORawRecord &rec = m_dsoRawRecordBanks[i];
339  rec.record.resize(len * num_ch * (rec.isComplex ? 2 : 1));
340  assert(rec.numCh == num_ch);
341  if(isMemLockAvailable()) {
342  mlock(&rec.record[0], rec.record.size() * sizeof(int32_t));
343  }
344  }
345  m_recordBuf.resize(len * num_ch);
346  if(isMemLockAvailable()) {
347  mlock( &m_recordBuf[0], m_recordBuf.size() * sizeof(tRawAI));
348  }
349 
350  uInt32 onbrd_size;
351  CHECK_DAQMX_RET(DAQmxGetBufInputOnbrdBufSize(m_task, &onbrd_size));
352  fprintf(stderr, "Using on-brd bufsize=%d\n", (int)onbrd_size);
353  unsigned int bufsize = len;
354  if(m_softwareTrigger) {
355  bufsize = std::max(bufsize * 8, (unsigned int)lrint((len / shot[ *timeWidth()]) * 1.0));
356  bufsize = std::max(bufsize, (unsigned int)(onbrd_size / num_ch));
357  }
358 
359  CHECK_DAQMX_RET(
360  DAQmxCfgSampClkTiming(m_task,
361  //! debug!
362  // formatString("/%s/Ctr0InternalOutput", interface()->devName()),
363  NULL, // internal source
364  len / shot[ *timeWidth()],
365  DAQmx_Val_Rising,
366  !m_softwareTrigger ? DAQmx_Val_FiniteSamps : DAQmx_Val_ContSamps,
367  bufsize
368  ));
369 
370  interface()->synchronizeClock(m_task);
371 
372  {
373  uInt32 size;
374  CHECK_DAQMX_RET(DAQmxGetBufInputBufSize(m_task, &size));
375  fprintf(stderr, "Using buffer size of %d\n", (int)size);
376  if(size != bufsize) {
377  fprintf(stderr, "Try to modify buffer size from %d to %d\n", (int)size, (int)bufsize);
378  CHECK_DAQMX_RET(DAQmxCfgInputBuffer(m_task, bufsize));
379  }
380  }
381 
382  CHECK_DAQMX_RET(DAQmxSetExportedSampClkOutputTerm(m_task, formatString("/%s/PFI7", interface()->devName()).c_str()));
383 // m_sampleClockRoute.reset(new XNIDAQmxInterface::XNIDAQmxRoute(
384 // formatString("/%s/ai/SampleClock", interface()->devName()).c_str(),
385 // formatString("/%s/PFI7", interface()->devName()).c_str()));
386 
387  float64 rate;
388  // CHECK_DAQMX_RET(DAQmxGetRefClkRate(m_task, &rate));
389  // dbgPrint(QString("Reference Clk rate = %1.").arg(rate));
390  CHECK_DAQMX_RET(DAQmxGetSampClkRate(m_task, &rate));
391  m_interval = 1.0 / rate;
392 
393  setupTrigger();
394 
395  startSequence();
396 }
397 
398 void
399 XNIDAQmxDSO::createChannels() {
400  XScopedLock<XInterface> lock( *interface());
401  Snapshot shot( *this);
402  m_suspendRead = true;
403  XScopedLock<XRecursiveMutex> lock2(m_readMutex);
404 
405  clearAcquision();
406 
407  CHECK_DAQMX_RET(DAQmxCreateTask("", &m_task));
408  assert(m_task != TASK_UNDEF);
409 
410  if(shot[ *trace1()] >= 0) {
411  CHECK_DAQMX_RET(
412  DAQmxCreateAIVoltageChan(m_task,
413  shot[ *trace1()].to_str().c_str(),
414  "",
415  DAQmx_Val_Cfg_Default,
416  -atof(shot[ *vFullScale1()].to_str().c_str()) / 2.0,
417  atof(shot[ *vFullScale1()].to_str().c_str()) / 2.0,
418  DAQmx_Val_Volts,
419  NULL
420  ));
421 
422  //obtain range info.
423  for(unsigned int i = 0; i < CAL_POLY_ORDER; i++)
424  m_coeffAI[0][i] = 0.0;
425  CHECK_DAQMX_RET(
426  DAQmxGetAIDevScalingCoeff(m_task,
427  shot[ *trace1()].to_str().c_str(),
428  m_coeffAI[0], CAL_POLY_ORDER));
429  }
430  if(shot[ *trace2()] >= 0) {
431  CHECK_DAQMX_RET(
432  DAQmxCreateAIVoltageChan(m_task,
433  shot[ *trace2()].to_str().c_str(),
434  "",
435  DAQmx_Val_Cfg_Default,
436  -atof(shot[ *vFullScale2()].to_str().c_str()) / 2.0,
437  atof(shot[ *vFullScale2()].to_str().c_str()) / 2.0,
438  DAQmx_Val_Volts,
439  NULL
440  ));
441  //obtain range info.
442  for(unsigned int i = 0; i < CAL_POLY_ORDER; i++)
443  m_coeffAI[1][i] = 0.0;
444  CHECK_DAQMX_RET(DAQmxGetAIDevScalingCoeff(m_task,
445  shot[ *trace2()].to_str().c_str(),
446  m_coeffAI[1], CAL_POLY_ORDER));
447  }
448  if(shot[ *trace3()] >= 0) {
449  CHECK_DAQMX_RET(
450  DAQmxCreateAIVoltageChan(m_task,
451  shot[ *trace3()].to_str().c_str(),
452  "",
453  DAQmx_Val_Cfg_Default,
454  -atof(shot[ *vFullScale3()].to_str().c_str()) / 2.0,
455  atof(shot[ *vFullScale3()].to_str().c_str()) / 2.0,
456  DAQmx_Val_Volts,
457  NULL
458  ));
459  //obtain range info.
460  for(unsigned int i = 0; i < CAL_POLY_ORDER; i++)
461  m_coeffAI[2][i] = 0.0;
462  CHECK_DAQMX_RET(DAQmxGetAIDevScalingCoeff(m_task,
463  shot[ *trace3()].to_str().c_str(),
464  m_coeffAI[2], CAL_POLY_ORDER));
465  }
466  if(shot[ *trace4()] >= 0) {
467  CHECK_DAQMX_RET(
468  DAQmxCreateAIVoltageChan(m_task,
469  shot[ *trace4()].to_str().c_str(),
470  "",
471  DAQmx_Val_Cfg_Default,
472  -atof(shot[ *vFullScale4()].to_str().c_str()) / 2.0,
473  atof(shot[ *vFullScale4()].to_str().c_str()) / 2.0,
474  DAQmx_Val_Volts,
475  NULL
476  ));
477  //obtain range info.
478  for(unsigned int i = 0; i < CAL_POLY_ORDER; i++)
479  m_coeffAI[3][i] = 0.0;
480  CHECK_DAQMX_RET(DAQmxGetAIDevScalingCoeff(m_task,
481  shot[ *trace4()].to_str().c_str(),
482  m_coeffAI[3], CAL_POLY_ORDER));
483  }
484 
485  uInt32 num_ch;
486  CHECK_DAQMX_RET(DAQmxGetTaskNumChans(m_task, &num_ch));
487 
488  //accumlation buffer.
489  for(unsigned int i = 0; i < 2; i++) {
490  DSORawRecord &rec(m_dsoRawRecordBanks[i]);
491  rec.acqCount = 0;
492  rec.accumCount = 0;
493  rec.numCh = num_ch;
494  rec.isComplex = (shot[ *dRFMode()] == DRFMODE_COHERENT_SG);
495  }
496 
497  if(num_ch == 0) {
498  return;
499  }
500 
501  CHECK_DAQMX_RET(DAQmxRegisterDoneEvent(m_task, 0, &XNIDAQmxDSO::onTaskDone_, this));
502 
503  setupTiming();
504 }
505 void
506 XNIDAQmxDSO::clearStoredSoftwareTrigger() {
507  uInt64 total_samps = 0;
508  if(m_running)
509  CHECK_DAQMX_RET(DAQmxGetReadTotalSampPerChanAcquired(m_task, &total_samps));
510  m_softwareTrigger->clear(total_samps, 1.0 / m_interval);
511 }
512 void
513 XNIDAQmxDSO::onSoftTrigStarted(const shared_ptr<XNIDAQmxInterface::SoftwareTrigger> &) {
514  XScopedLock<XInterface> lock( *interface());
515  m_suspendRead = true;
516  XScopedLock<XRecursiveMutex> lock2(m_readMutex);
517 
518  if(m_running) {
519  m_running = false;
520  CHECK_DAQMX_RET(DAQmxStopTask(m_task));
521  }
522 
523  const DSORawRecord &rec(m_dsoRawRecordBanks[m_dsoRawRecordBankLatest]);
524  m_softwareTrigger->setBlankTerm(m_interval * rec.recordLength);
525 // fprintf(stderr, "Virtual trig start.\n");
526 
527  uInt32 num_ch;
528  CHECK_DAQMX_RET(DAQmxGetTaskNumChans(m_task, &num_ch));
529  if(num_ch > 0) {
530  int32 type;
531  CHECK_DAQMX_RET(DAQmxGetStartTrigType(m_task, &type));
532  if(type != DAQmx_Val_DigEdge) {
533  setupTrigger();
534  }
535  CHECK_DAQMX_RET(DAQmxStartTask(m_task));
536  m_suspendRead = false;
537  m_running = true;
538  }
539 }
540 int32
541 XNIDAQmxDSO::onTaskDone_(TaskHandle task, int32 status, void *data) {
542  XNIDAQmxDSO *obj = static_cast<XNIDAQmxDSO*>(data);
543  obj->onTaskDone(task, status);
544  return status;
545 }
546 void
547 XNIDAQmxDSO::onTaskDone(TaskHandle /*task*/, int32 status) {
548  if(status) {
549  gErrPrint(getLabel() + XNIDAQmxInterface::getNIDAQmxErrMessage(status));
550  m_suspendRead = true;
551  }
552 }
553 void
554 XNIDAQmxDSO::onForceTriggerTouched(const Snapshot &shot, XTouchableNode *) {
555  XScopedLock<XInterface> lock( *interface());
556  m_suspendRead = true;
557  XScopedLock<XRecursiveMutex> lock2(m_readMutex);
558 
559  if(m_softwareTrigger) {
560  if(m_running) {
561  uInt64 total_samps;
562  CHECK_DAQMX_RET(DAQmxGetReadTotalSampPerChanAcquired(m_task, &total_samps));
563  m_softwareTrigger->forceStamp(total_samps, 1.0 / m_interval);
564  m_suspendRead = false;
565  }
566  }
567  else {
568  disableTrigger();
569  CHECK_DAQMX_RET(DAQmxStartTask(m_task));
570  m_suspendRead = false;
571  m_running = true;
572  }
573 }
574 inline bool
575 XNIDAQmxDSO::tryReadAISuspend(const atomic<bool> &terminated) {
576  if(m_suspendRead) {
577  m_readMutex.unlock();
578  while(m_suspendRead && !terminated) msecsleep(30);
579  m_readMutex.lock();
580  return true;
581  }
582  return false;
583 }
584 void *
585 XNIDAQmxDSO::executeReadAI(const atomic<bool> &terminated) {
586  while( !terminated) {
587  try {
588  acquire(terminated);
589  }
590  catch (XInterface::XInterfaceError &e) {
591  e.print(getLabel());
592  m_suspendRead = true;
593  }
594  }
595  return NULL;
596 }
597 void
598 XNIDAQmxDSO::acquire(const atomic<bool> &terminated) {
599  XScopedLock<XRecursiveMutex> lock(m_readMutex);
600  while( !terminated) {
601 
602  if( !m_running) {
603  tryReadAISuspend(terminated);
604  msecsleep(30);
605  return;
606  }
607 
608  uInt32 num_ch;
609  CHECK_DAQMX_RET(DAQmxGetReadNumChans(m_task, &num_ch));
610  if(num_ch == 0) {
611  tryReadAISuspend(terminated);
612  msecsleep(30);
613  return;
614  }
615 
616  const DSORawRecord &old_rec(m_dsoRawRecordBanks[m_dsoRawRecordBankLatest]);
617  if(num_ch != old_rec.numCh)
618  throw XInterface::XInterfaceError(i18n("Inconsistent channel number."), __FILE__, __LINE__);
619 
620  const unsigned int size = m_recordBuf.size() / num_ch;
621  const float64 freq = 1.0 / m_interval;
622  unsigned int cnt = 0;
623 
624  uint64_t samplecnt_at_trigger = 0;
625  if(m_softwareTrigger) {
626  shared_ptr<XNIDAQmxInterface::SoftwareTrigger> &vt(m_softwareTrigger);
627 
628  while( !terminated) {
629  if(tryReadAISuspend(terminated))
630  return;
631  uInt64 total_samps;
632  CHECK_DAQMX_RET(DAQmxGetReadTotalSampPerChanAcquired(m_task, &total_samps));
633  samplecnt_at_trigger = vt->tryPopFront(total_samps, freq);
634  if(samplecnt_at_trigger) {
635  uInt32 bufsize;
636  CHECK_DAQMX_RET(DAQmxGetBufInputBufSize(m_task, &bufsize));
637  if(total_samps - samplecnt_at_trigger + m_preTriggerPos > bufsize * 4 / 5) {
638  gWarnPrint(i18n("Buffer Overflow."));
639  continue;
640  }
641  //set read pos.
642  int16 tmpbuf[NUM_MAX_CH];
643  int32 samps;
644  CHECK_DAQMX_RET(DAQmxSetReadRelativeTo(m_task, DAQmx_Val_MostRecentSamp));
645  CHECK_DAQMX_RET(DAQmxSetReadOffset(m_task, -1));
646  CHECK_DAQMX_RET(DAQmxReadBinaryI16(m_task, 1,
647  0, DAQmx_Val_GroupByScanNumber,
648  tmpbuf, NUM_MAX_CH, &samps, NULL
649  ));
650  CHECK_DAQMX_RET(DAQmxSetReadRelativeTo(m_task, DAQmx_Val_CurrReadPos));
651  CHECK_DAQMX_RET(DAQmxSetReadOffset(m_task, 0));
652  uInt64 curr_rdpos;
653  CHECK_DAQMX_RET(DAQmxGetReadCurrReadPos(m_task, &curr_rdpos));
654  int32 offset = samplecnt_at_trigger - m_preTriggerPos - curr_rdpos;
655  CHECK_DAQMX_RET(DAQmxSetReadOffset(m_task, offset));
656  // fprintf(stderr, "hit! %d %d %d\n", (int)offset, (int)lastcnt, (int)m_preTriggerPos);
657  break;
658  }
659  msecsleep(lrint(1e3 * size * m_interval / 6));
660  }
661  }
662  else {
663  if(m_preTriggerPos) {
664  CHECK_DAQMX_RET(DAQmxSetReadRelativeTo(m_task, DAQmx_Val_FirstPretrigSamp));
665  }
666  else {
667  CHECK_DAQMX_RET(DAQmxSetReadRelativeTo(m_task, DAQmx_Val_FirstSample));
668  }
669 
670  CHECK_DAQMX_RET(DAQmxSetReadOffset(m_task, 0));
671  }
672  if(terminated)
673  return;
674 
675  const unsigned int num_samps = std::min(size, 8192u);
676  for(; cnt < size;) {
677  int32 samps;
678  samps = std::min(size - cnt, num_samps);
679  while( !terminated) {
680  if(tryReadAISuspend(terminated))
681  return;
682  uInt32 space;
683  int ret = DAQmxGetReadAvailSampPerChan(m_task, &space);
684  if( !ret && (space >= (uInt32)samps))
685  break;
686  msecsleep(lrint(1e3 * (samps - space) * m_interval));
687  }
688  if(terminated)
689  return;
690  CHECK_DAQMX_RET(DAQmxReadBinaryI16(m_task, samps,
691  0.0, DAQmx_Val_GroupByScanNumber,
692  &m_recordBuf[cnt * num_ch], samps * num_ch, &samps, NULL
693  ));
694  cnt += samps;
695  if( !m_softwareTrigger) {
696  CHECK_DAQMX_RET(DAQmxSetReadOffset(m_task, cnt));
697  }
698  else {
699  CHECK_DAQMX_RET(DAQmxSetReadOffset(m_task, 0));
700  }
701  }
702 
703  Snapshot shot( *this);
704  const unsigned int av = shot[ *average()];
705  const bool sseq = shot[ *singleSequence()];
706  //obtain unlocked bank.
707  int bank;
708  for(;;) {
709  bank = 1 - m_dsoRawRecordBankLatest;
710  if(m_dsoRawRecordBanks[bank].tryLock())
711  break;
712  bank = m_dsoRawRecordBankLatest;
713  if(m_dsoRawRecordBanks[bank].tryLock())
714  break;
715  }
716  assert((bank >= 0) && (bank < 2));
717  DSORawRecord &new_rec(m_dsoRawRecordBanks[bank]);
718  unsigned int accumcnt = old_rec.accumCount;
719 
720  if( !sseq || (accumcnt < av)) {
721  if( !m_softwareTrigger) {
722  if(m_running) {
723  m_running = false;
724  CHECK_DAQMX_RET(DAQmxStopTask(m_task));
725  }
726  CHECK_DAQMX_RET(DAQmxStartTask(m_task));
727  m_running = true;
728  }
729  }
730 
731  cnt = std::min(cnt, old_rec.recordLength);
732  new_rec.recordLength = cnt;
733  // num_ch = std::min(num_ch, old_rec->numCh);
734  new_rec.numCh = num_ch;
735  const unsigned int bufsize = new_rec.recordLength * num_ch;
736  tRawAI *pbuf = &m_recordBuf[0];
737  const int32_t *pold = &old_rec.record[0];
738  int32_t *paccum = &new_rec.record[0];
739  //Optimized accumlation.
740  unsigned int div = bufsize / 4;
741  unsigned int rest = bufsize % 4;
742  if(new_rec.isComplex) {
743  double ph = phaseOfRF(shot, samplecnt_at_trigger, m_interval);
744  double cosph = cos(ph);
745  double sinph = sin(ph);
746  //real part.
747  for(unsigned int i = 0; i < div; i++) {
748  *paccum++ = *pold++ + *pbuf++ * cosph;
749  *paccum++ = *pold++ + *pbuf++ * cosph;
750  *paccum++ = *pold++ + *pbuf++ * cosph;
751  *paccum++ = *pold++ + *pbuf++ * cosph;
752  }
753  for(unsigned int i = 0; i < rest; i++)
754  *paccum++ = *pold++ + *pbuf++ * cosph;
755  //imag part.
756  for(unsigned int i = 0; i < div; i++) {
757  *paccum++ = *pold++ + *pbuf++ * sinph;
758  *paccum++ = *pold++ + *pbuf++ * sinph;
759  *paccum++ = *pold++ + *pbuf++ * sinph;
760  *paccum++ = *pold++ + *pbuf++ * sinph;
761  }
762  for(unsigned int i = 0; i < rest; i++)
763  *paccum++ = *pold++ + *pbuf++ * cosph;
764  }
765  else {
766  for(unsigned int i = 0; i < div; i++) {
767  *paccum++ = *pold++ + *pbuf++;
768  *paccum++ = *pold++ + *pbuf++;
769  *paccum++ = *pold++ + *pbuf++;
770  *paccum++ = *pold++ + *pbuf++;
771  }
772  for(unsigned int i = 0; i < rest; i++)
773  *paccum++ = *pold++ + *pbuf++;
774  }
775  new_rec.acqCount = old_rec.acqCount + 1;
776  accumcnt++;
777 
778  while( !sseq && (av <= m_record_av.size()) && !m_record_av.empty()) {
779  if(new_rec.isComplex)
780  throw XInterface::XInterfaceError(i18n("Moving average with coherent SG is not supported."), __FILE__, __LINE__);
781  int32_t *paccum = &(new_rec.record[0]);
782  tRawAI *psub = &(m_record_av.front()[0]);
783  unsigned int div = bufsize / 4;
784  unsigned int rest = bufsize % 4;
785  for(unsigned int i = 0; i < div; i++) {
786  *paccum++ -= *psub++;
787  *paccum++ -= *psub++;
788  *paccum++ -= *psub++;
789  *paccum++ -= *psub++;
790  }
791  for(unsigned int i = 0; i < rest; i++)
792  *paccum++ -= *psub++;
793  m_record_av.pop_front();
794  accumcnt--;
795  }
796  new_rec.accumCount = accumcnt;
797  // substitute the record with the working set.
798  m_dsoRawRecordBankLatest = bank;
799  new_rec.unlock();
800  if( !sseq) {
801  m_record_av.push_back(m_recordBuf);
802  }
803  if(sseq && (accumcnt >= av)) {
804  if(m_softwareTrigger) {
805  if(m_running) {
806  m_suspendRead = true;
807  }
808  }
809  }
810  }
811 }
812 void
814  XScopedLock<XInterface> lock( *interface());
815  m_suspendRead = true;
816  XScopedLock<XRecursiveMutex> lock2(m_readMutex);
817 
818  {
819  m_dsoRawRecordBankLatest = 0;
820  for(unsigned int i = 0; i < 2; i++) {
821  DSORawRecord &rec(m_dsoRawRecordBanks[i]);
822  rec.acqCount = 0;
823  rec.accumCount = 0;
824  }
825  DSORawRecord &rec(m_dsoRawRecordBanks[0]);
826  if(!rec.numCh)
827  return;
828  rec.recordLength = rec.record.size() / rec.numCh / (rec.isComplex ? 2 : 1);
829  memset(&rec.record[0], 0, rec.record.size() * sizeof(int32_t));
830  }
831  m_record_av.clear();
832 
833  if(m_softwareTrigger) {
834  if( !m_lsnOnSoftTrigStarted)
835  m_lsnOnSoftTrigStarted = m_softwareTrigger->onStart().connectWeakly(
836  shared_from_this(), &XNIDAQmxDSO::onSoftTrigStarted);
837  if(m_running) {
838  clearStoredSoftwareTrigger();
839  m_suspendRead = false;
840  }
841  else {
842  CHECK_DAQMX_RET(DAQmxTaskControl(m_task, DAQmx_Val_Task_Commit));
843  statusPrinter()->printMessage(i18n("Restart the software-trigger source."));
844  }
845  }
846  else {
847  if(m_running) {
848  m_running = false;
849  if(m_task != TASK_UNDEF)
850  CHECK_DAQMX_RET(DAQmxStopTask(m_task));
851  }
852  uInt32 num_ch;
853  CHECK_DAQMX_RET(DAQmxGetTaskNumChans(m_task, &num_ch));
854  if(num_ch > 0) {
855  CHECK_DAQMX_RET(DAQmxStartTask(m_task));
856  m_suspendRead = false;
857  m_running = true;
858  }
859  }
860  // CHECK_DAQMX_RET(DAQmxSetReadOffset(m_task, 0));
861 }
862 
863 int
864 XNIDAQmxDSO::acqCount(bool *seq_busy) {
865  const DSORawRecord &rec(m_dsoRawRecordBanks[m_dsoRawRecordBankLatest]);
866  Snapshot shot( *this);
867  *seq_busy = ((unsigned int)rec.acqCount < shot[ *average()]);
868  return rec.acqCount;
869 }
870 
871 double
872 XNIDAQmxDSO::getTimeInterval() {
873  return m_interval;
874 }
875 
876 inline float64
877 XNIDAQmxDSO::aiRawToVolt(const float64 *pcoeff, float64 raw) {
878  float64 x = 1.0;
879  float64 y = 0.0;
880  for(unsigned int i = 0; i < CAL_POLY_ORDER; i++) {
881  y += *(pcoeff++) * x;
882  x *= raw;
883  }
884  return y;
885 }
886 
887 void
888 XNIDAQmxDSO::getWave(shared_ptr<RawData> &writer, std::deque<XString> &) {
889  XScopedLock<XInterface> lock( *interface());
890 
891  int bank;
892  for(;;) {
893  bank = m_dsoRawRecordBankLatest;
894  if(m_dsoRawRecordBanks[bank].tryLock())
895  break;
896  bank = 1 - bank;
897  if(m_dsoRawRecordBanks[bank].tryLock())
898  break;
899  }
900  readBarrier();
901  assert((bank >= 0) && (bank < 2));
902  DSORawRecord &rec(m_dsoRawRecordBanks[bank]);
903 
904  if(rec.accumCount == 0) {
905  rec.unlock();
906  throw XDriver::XSkippedRecordError(__FILE__, __LINE__);
907  }
908  uInt32 num_ch = rec.numCh;
909  uInt32 len = rec.recordLength;
910 
911  char buf[2048];
912  CHECK_DAQMX_RET(DAQmxGetReadChannelsToRead(m_task, buf, sizeof(buf)));
913 
914  if(rec.isComplex)
915  num_ch *= 2;
916  writer->push((uint32_t)num_ch);
917  writer->push((uint32_t)m_preTriggerPos);
918  writer->push((uint32_t)len);
919  writer->push((uint32_t)rec.accumCount);
920  writer->push((double)m_interval);
921  for(unsigned int ch = 0; ch < num_ch; ch++) {
922  for(unsigned int i = 0; i < CAL_POLY_ORDER; i++) {
923  int ch_real = ch;
924  if(rec.isComplex) ch_real = ch / 2;
925  writer->push((double)m_coeffAI[ch_real][i]);
926  }
927  }
928  const int32_t *p = &(rec.record[0]);
929  const unsigned int size = len * num_ch;
930  for(unsigned int i = 0; i < size; i++)
931  writer->push<int32_t>( *p++);
932  XString str(buf);
933  writer->insert(writer->end(), str.begin(), str.end());
934  str = ""; //reserved/
935  writer->insert(writer->end(), str.begin(), str.end());
936 
937  rec.unlock();
938 }
939 void
941  const unsigned int num_ch = reader.pop<uint32_t>();
942  const unsigned int pretrig = reader.pop<uint32_t>();
943  const unsigned int len = reader.pop<uint32_t>();
944  const unsigned int accumCount = reader.pop<uint32_t>();
945  const double interval = reader.pop<double>();
946 
947  tr[ *this].setParameters(num_ch, - (double)pretrig * interval, interval, len);
948 
949  double *wave[NUM_MAX_CH * 2];
950  float64 coeff[NUM_MAX_CH * 2][CAL_POLY_ORDER];
951  for(unsigned int j = 0; j < num_ch; j++) {
952  for(unsigned int i = 0; i < CAL_POLY_ORDER; i++) {
953  coeff[j][i] = reader.pop<double>();
954  }
955 
956  wave[j] = tr[ *this].waveDisp(j);
957  }
958 
959  const float64 prop = 1.0 / accumCount;
960  for(unsigned int i = 0; i < len; i++) {
961  for(unsigned int j = 0; j < num_ch; j++)
962  *(wave[j])++ = aiRawToVolt(coeff[j], reader.pop<int32_t>() * prop);
963  }
964 }
965 
966 void
967 XNIDAQmxDSO::onAverageChanged(const Snapshot &shot, XValueNodeBase *) {
968  startSequence();
969 }
970 
971 void
972 XNIDAQmxDSO::onSingleChanged(const Snapshot &shot, XValueNodeBase *) {
973  startSequence();
974 }
975 void
976 XNIDAQmxDSO::onTrigPosChanged(const Snapshot &shot, XValueNodeBase *) {
977  createChannels();
978 }
979 void
980 XNIDAQmxDSO::onTrigSourceChanged(const Snapshot &shot, XValueNodeBase *) {
981  createChannels();
982 }
983 void
984 XNIDAQmxDSO::onTrigLevelChanged(const Snapshot &shot, XValueNodeBase *) {
985  createChannels();
986 }
987 void
988 XNIDAQmxDSO::onTrigFallingChanged(const Snapshot &shot, XValueNodeBase *) {
989  createChannels();
990 }
991 void
992 XNIDAQmxDSO::onTimeWidthChanged(const Snapshot &shot, XValueNodeBase *) {
993  createChannels();
994 }
995 void
996 XNIDAQmxDSO::onTrace1Changed(const Snapshot &shot, XValueNodeBase *) {
997  createChannels();
998 }
999 void
1000 XNIDAQmxDSO::onTrace2Changed(const Snapshot &shot, XValueNodeBase *) {
1001  createChannels();
1002 }
1003 void
1004 XNIDAQmxDSO::onTrace3Changed(const Snapshot &shot, XValueNodeBase *) {
1005  createChannels();
1006 }
1007 void
1008 XNIDAQmxDSO::onTrace4Changed(const Snapshot &shot, XValueNodeBase *) {
1009  createChannels();
1010 }
1011 void
1012 XNIDAQmxDSO::onVFullScale1Changed(const Snapshot &shot, XValueNodeBase *) {
1013  createChannels();
1014 }
1015 void
1016 XNIDAQmxDSO::onVFullScale2Changed(const Snapshot &shot, XValueNodeBase *) {
1017  createChannels();
1018 }
1019 void
1020 XNIDAQmxDSO::onVFullScale3Changed(const Snapshot &shot, XValueNodeBase *) {
1021  createChannels();
1022 }
1023 void
1024 XNIDAQmxDSO::onVFullScale4Changed(const Snapshot &shot, XValueNodeBase *) {
1025  createChannels();
1026 }
1027 void
1028 XNIDAQmxDSO::onVOffset1Changed(const Snapshot &shot, XValueNodeBase *) {
1029  createChannels();
1030 }
1031 void
1032 XNIDAQmxDSO::onVOffset2Changed(const Snapshot &shot, XValueNodeBase *) {
1033  createChannels();
1034 }
1035 void
1036 XNIDAQmxDSO::onVOffset3Changed(const Snapshot &shot, XValueNodeBase *) {
1037  createChannels();
1038 }
1039 void
1040 XNIDAQmxDSO::onVOffset4Changed(const Snapshot &shot, XValueNodeBase *) {
1041  createChannels();
1042 }
1043 void
1044 XNIDAQmxDSO::onRecordLengthChanged(const Snapshot &shot, XValueNodeBase *) {
1045  createChannels();
1046 }

Generated for KAME4 by  doxygen 1.8.3