pulserdrivernidaqmx.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 "pulserdrivernidaqmx.h"
15 
16 //! \warning Current version of DAQmx (8.0.2) software for Linux may lack thread-safety in many-core systems.
17 //! When an app attempts concurrent data writing to two devices,
18 //! the driver sometimes causes freezing, ends with error, or leaves a syslog message,
19 //! "BUG: sleeping function called from invalid context at mm/slub.c:..."
20 //! Perhaps a global lock on DAQmx functions is necessary....
21 
22 #define PAUSING_BLANK_BEFORE 1u
23 #define PAUSING_BLANK_AFTER 1u
24 
25 #include "interface.h"
26 
27 #define TASK_UNDEF ((TaskHandle)-1)
28 #define RESOLUTION_UNDEF 1e-5
29 
30 template <typename T>
31 inline T *fastFill(T* p, T x, unsigned int cnt);
32 
33 XNIDAQmxPulser::XNIDAQmxPulser(const char *name, bool runtime,
34  Transaction &tr_meas, const shared_ptr<XMeasure> &meas):
35  XNIDAQmxDriver<XPulser>(name, runtime, ref(tr_meas), meas),
36  m_pausingBit(0), m_pausingCount(0),
37  m_running(false),
38  m_resolutionDO(RESOLUTION_UNDEF),
39  m_resolutionAO(RESOLUTION_UNDEF),
40  m_taskAO(TASK_UNDEF),
41  m_taskDO(TASK_UNDEF),
42  m_taskDOCtr(TASK_UNDEF),
43  m_taskGateCtr(TASK_UNDEF) {
44 
45  iterate_commit([=](Transaction &tr){
46  for(unsigned int i = 0; i < NUM_DO_PORTS; i++)
47  tr[ *portSel(i)].add("Pausing(PFI4)");
48  const int ports[] = {
49  PORTSEL_GATE, PORTSEL_PREGATE, PORTSEL_TRIG1, PORTSEL_TRIG2,
50  PORTSEL_GATE3, PORTSEL_COMB, PORTSEL_QSW, PORTSEL_ASW
51  };
52  for(unsigned int i = 0; i < sizeof(ports)/sizeof(int); i++) {
53  tr[ *portSel(i)] = ports[i];
54  }
55  });
56 
57  m_softwareTrigger = XNIDAQmxInterface::SoftwareTrigger::create(name, NUM_DO_PORTS);
58 
59  m_pausingCount = (PAUSING_BLANK_BEFORE + PAUSING_BLANK_AFTER) * 47;
60 
61  //memory locks.
62  if(isMemLockAvailable()) {
63  const void *FIRST_OF_MLOCK_MEMBER = &m_genPatternList;
64  const void *LAST_OF_MLOCK_MEMBER = &m_lowerLimAO[NUM_AO_CH];
65  mlock(FIRST_OF_MLOCK_MEMBER, (size_t)LAST_OF_MLOCK_MEMBER - (size_t)FIRST_OF_MLOCK_MEMBER);
66  }
67 }
68 
69 XNIDAQmxPulser::~XNIDAQmxPulser() {
70  clearTasks();
71  XNIDAQmxInterface::SoftwareTrigger::unregister(m_softwareTrigger);
72 }
73 
74 void
75 XNIDAQmxPulser::openDO(bool use_ao_clock) throw (XKameError &) {
76  XScopedLock<XRecursiveMutex> tlock(m_stateLock);
77 
78  if(intfDO()->maxDORate(1) == 0)
79  throw XInterface::XInterfaceError(i18n("HW-timed transfer needed."), __FILE__, __LINE__);
80 
81  if(m_resolutionDO == RESOLUTION_UNDEF)
82  m_resolutionDO = 1.0 / intfDO()->maxDORate(1);
83  fprintf(stderr, "Using DO rate = %f[kHz]\n", 1.0/m_resolutionDO);
84  setupTasksDO(use_ao_clock);
85 }
86 
87 void
88 XNIDAQmxPulser::openAODO() throw (XKameError &) {
89  XScopedLock<XRecursiveMutex> tlock(m_stateLock);
90 
91  if(intfDO()->maxDORate(1) == 0)
92  throw XInterface::XInterfaceError(i18n("HW-timed transfer needed."), __FILE__, __LINE__);
93  if(intfAO()->maxAORate(2) == 0)
94  throw XInterface::XInterfaceError(i18n("HW-timed transfer needed."), __FILE__, __LINE__);
95 
96  if((m_resolutionDO == RESOLUTION_UNDEF) || (m_resolutionAO == RESOLUTION_UNDEF))
97  {
98  double do_rate = intfDO()->maxDORate(1);
99  double ao_rate = intfAO()->maxAORate(2);
100  if(ao_rate <= do_rate)
101  do_rate = ao_rate;
102  else {
103  //Oversampling is unstable.
104  int oversamp = 1; //lrint(ao_rate / do_rate);
105  ao_rate = do_rate * oversamp;
106  }
107  m_resolutionDO = 1.0 / do_rate;
108  m_resolutionAO = 1.0 / ao_rate;
109  }
110  fprintf(stderr, "Using AO rate = %f[kHz]\n", 1.0/m_resolutionAO);
111 
112  setupTasksAODO();
113 }
114 
115 void
116 XNIDAQmxPulser::close() throw (XKameError &) {
117  XScopedLock<XRecursiveMutex> tlock(m_stateLock);
118 
119  try {
120  stopPulseGen();
121  }
122  catch (XInterface::XInterfaceError &e) {
123  e.print();
124  }
125 
126  {
127  clearTasks();
128 
129  m_resolutionDO = RESOLUTION_UNDEF;
130  m_resolutionAO = RESOLUTION_UNDEF;
131 
132  intfDO()->stop();
133  intfAO()->stop();
134  intfCtr()->stop();
135  }
136 }
137 void
138 XNIDAQmxPulser::clearTasks() {
139  if(m_taskAO != TASK_UNDEF)
140  CHECK_DAQMX_RET(DAQmxClearTask(m_taskAO));
141  if(m_taskDO != TASK_UNDEF)
142  CHECK_DAQMX_RET(DAQmxClearTask(m_taskDO));
143  if(m_taskDOCtr != TASK_UNDEF)
144  CHECK_DAQMX_RET(DAQmxClearTask(m_taskDOCtr));
145  if(m_taskGateCtr != TASK_UNDEF)
146  CHECK_DAQMX_RET(DAQmxClearTask(m_taskGateCtr));
147  m_taskAO = TASK_UNDEF;
148  m_taskDO = TASK_UNDEF;
149  m_taskDOCtr = TASK_UNDEF;
150  m_taskGateCtr = TASK_UNDEF;
151 }
152 
153 void
154 XNIDAQmxPulser::setupTasksDO(bool use_ao_clock) {
155  if(m_taskDO != TASK_UNDEF)
156  CHECK_DAQMX_RET(DAQmxClearTask(m_taskDO));
157  if(m_taskDOCtr != TASK_UNDEF)
158  CHECK_DAQMX_RET(DAQmxClearTask(m_taskDOCtr));
159  if(m_taskGateCtr != TASK_UNDEF)
160  CHECK_DAQMX_RET(DAQmxClearTask(m_taskGateCtr));
161 
162  float64 freq = 1e3 / resolution();
163 
164  CHECK_DAQMX_RET(DAQmxCreateTask("", &m_taskDO));
165  CHECK_DAQMX_RET(DAQmxCreateDOChan(m_taskDO,
166  formatString("%s/port0", intfDO()->devName()).c_str(),
167  "", DAQmx_Val_ChanForAllLines));
168  CHECK_DAQMX_RET(DAQmxRegisterDoneEvent(m_taskDO, 0, &XNIDAQmxPulser::onTaskDone_, this));
169 
170  XString do_clk_src;
171 
172  if(use_ao_clock) {
173  do_clk_src = formatString("/%s/ao/SampleClock", intfAO()->devName());
174  fprintf(stderr, "Using ao/SampleClock for DO.\n");
175  }
176  else {
177  do_clk_src = formatString("/%s/Ctr0InternalOutput", intfCtr()->devName());
178  XString ctrdev = formatString("%s/ctr0", intfCtr()->devName());
179  //Continuous pulse train generation. Duty = 50%.
180  CHECK_DAQMX_RET(DAQmxCreateTask("", &m_taskDOCtr));
181  CHECK_DAQMX_RET(DAQmxCreateCOPulseChanFreq(m_taskDOCtr,
182  ctrdev.c_str(), "", DAQmx_Val_Hz, DAQmx_Val_Low, 0.0,
183  freq, 0.5));
184  CHECK_DAQMX_RET(DAQmxRegisterDoneEvent(m_taskDOCtr, 0, &XNIDAQmxPulser::onTaskDone_, this));
185  CHECK_DAQMX_RET(DAQmxCfgImplicitTiming(m_taskDOCtr, DAQmx_Val_ContSamps, 1000));
186  intfCtr()->synchronizeClock(m_taskDOCtr);
187  m_softwareTrigger->setArmTerm(do_clk_src.c_str());
188  }
189 
190  unsigned int buf_size_hint = (unsigned int)lrint(1.0 * freq); //1sec.
191  //M series needs an external sample clock and trigger for DO channels.
192  CHECK_DAQMX_RET(DAQmxCfgSampClkTiming(m_taskDO,
193  do_clk_src.c_str(),
194  freq, DAQmx_Val_Rising, DAQmx_Val_ContSamps, buf_size_hint));
195 // intfDO()->synchronizeClock(m_taskDO); //not applicable for M series.
196 
197  uInt32 onbrdsize, bufsize;
198  CHECK_DAQMX_RET(DAQmxGetBufOutputOnbrdBufSize(m_taskDO, &onbrdsize));
199  fprintf(stderr, "DO On-board bufsize = %d\n", (int)onbrdsize);
200  if(m_pausingBit)
201  buf_size_hint /= 4;
202  CHECK_DAQMX_RET(DAQmxGetBufOutputBufSize(m_taskDO, &bufsize));
203  if(bufsize < buf_size_hint)
204  CHECK_DAQMX_RET(DAQmxCfgOutputBuffer(m_taskDO, std::max((uInt32)buf_size_hint, onbrdsize / 4)));
205  CHECK_DAQMX_RET(DAQmxGetBufOutputBufSize(m_taskDO, &bufsize));
206  fprintf(stderr, "DO Using bufsize = %d, freq = %f\n", (int)bufsize, freq);
207  m_preFillSizeDO = bufsize / 2;
208  m_transferSizeHintDO = std::min((unsigned int)onbrdsize / 2, m_preFillSizeDO / 16);
209  CHECK_DAQMX_RET(DAQmxSetWriteRegenMode(m_taskDO, DAQmx_Val_DoNotAllowRegen));
210 
211  {
212 // CHECK_DAQMX_RET(DAQmxSetWriteWaitMode(m_taskDO, DAQmx_Val_Poll));
213 // CHECK_DAQMX_RET(DAQmxSetWriteSleepTime(m_taskDO, 0.001));
214  char ch[256];
215  CHECK_DAQMX_RET(DAQmxGetTaskChannels(m_taskDO, ch, sizeof(ch)));
216  if(intfDO()->productFlags() & XNIDAQmxInterface::FLAG_BUGGY_DMA_DO) {
217  CHECK_DAQMX_RET(DAQmxSetDODataXferMech(m_taskDO, ch,
218  DAQmx_Val_Interrupts));
219  }
220  if(intfDO()->productFlags() & XNIDAQmxInterface::FLAG_BUGGY_XFER_COND_DO) {
221  CHECK_DAQMX_RET(DAQmxSetDODataXferReqCond(m_taskDO, ch,
222  DAQmx_Val_OnBrdMemNotFull));
223  }
224  }
225 
226  if(m_pausingBit) {
227  m_pausingGateTerm = formatString("/%s/PFI4", intfDO()->devName());
228  unsigned int ctr_no = use_ao_clock ? 0 : 1;
229  m_pausingCh = formatString("%s/ctr%u", intfCtr()->devName(), ctr_no);
230  m_pausingSrcTerm = formatString("/%s/Ctr%uInternalOutput", intfCtr()->devName(), ctr_no);
231  //set idle state to low level for synchronization.
232  CHECK_DAQMX_RET(DAQmxCreateTask("", &m_taskGateCtr));
233  CHECK_DAQMX_RET(DAQmxCreateCOPulseChanTime(m_taskGateCtr,
234  m_pausingCh.c_str(), "", DAQmx_Val_Seconds, DAQmx_Val_Low,
235  PAUSING_BLANK_BEFORE * resolution() * 1e-3,
236  PAUSING_BLANK_AFTER * resolution() * 1e-3,
237  m_pausingCount * resolution() * 1e-3));
238  CHECK_DAQMX_RET(DAQmxCfgImplicitTiming(m_taskGateCtr,
239  DAQmx_Val_FiniteSamps, 1));
240  CHECK_DAQMX_RET(DAQmxSetCOCtrTimebaseActiveEdge(m_taskGateCtr,
241  m_pausingCh.c_str(), DAQmx_Val_Rising));
242  intfCtr()->synchronizeClock(m_taskGateCtr);
243 
244  CHECK_DAQMX_RET(DAQmxCfgDigEdgeStartTrig(m_taskGateCtr,
245  m_pausingGateTerm.c_str(),
246  DAQmx_Val_Rising));
247  CHECK_DAQMX_RET(DAQmxSetStartTrigRetriggerable(m_taskGateCtr, true));
248 // CHECK_DAQMX_RET(DAQmxSetDigEdgeStartTrigDigSyncEnable(m_taskGateCtr, true));
249  if( !use_ao_clock) {
250  char doch[256];
251  CHECK_DAQMX_RET(DAQmxGetTaskChannels(m_taskDOCtr, doch, 256));
252  CHECK_DAQMX_RET(DAQmxSetCOCtrTimebaseActiveEdge(m_taskDOCtr,
253  doch, DAQmx_Val_Falling));
254  CHECK_DAQMX_RET(DAQmxSetPauseTrigType(m_taskDOCtr, DAQmx_Val_DigLvl));
255  CHECK_DAQMX_RET(DAQmxSetDigLvlPauseTrigSrc(m_taskDOCtr, m_pausingSrcTerm.c_str()));
256  CHECK_DAQMX_RET(DAQmxSetDigLvlPauseTrigWhen(m_taskDOCtr, DAQmx_Val_High));
257 // CHECK_DAQMX_RET(DAQmxSetDigLvlPauseTrigDigSyncEnable(m_taskDOCtr, true));
258  }
259  }
260 }
261 void
262 XNIDAQmxPulser::setupTasksAODO() {
263  if(m_taskAO != TASK_UNDEF)
264  CHECK_DAQMX_RET(DAQmxClearTask(m_taskAO));
265 
266  CHECK_DAQMX_RET(DAQmxCreateTask("", &m_taskAO));
267  CHECK_DAQMX_RET(DAQmxCreateAOVoltageChan(m_taskAO,
268  formatString("%s/ao0:1", intfAO()->devName()).c_str(), "",
269  -1.0, 1.0, DAQmx_Val_Volts, NULL));
270  CHECK_DAQMX_RET(DAQmxRegisterDoneEvent(m_taskAO, 0, &XNIDAQmxPulser::onTaskDone_, this));
271 
272  float64 freq = 1e3 / resolutionQAM();
273  unsigned int buf_size_hint = (unsigned int)lrint(1.0 * freq); //1sec.
274 
275  CHECK_DAQMX_RET(DAQmxCfgSampClkTiming(m_taskAO, "",
276  freq, DAQmx_Val_Rising, DAQmx_Val_ContSamps, buf_size_hint));
277  intfAO()->synchronizeClock(m_taskAO);
278 
279  int oversamp = lrint(resolution() / resolutionQAM());
280  setupTasksDO(oversamp == 1);
281  if(oversamp != 1) {
282  //Synchronizes ARM.
283  if(m_pausingBit) {
284  CHECK_DAQMX_RET(DAQmxSetArmStartTrigType(m_taskDOCtr, DAQmx_Val_DigEdge));
285  CHECK_DAQMX_RET(DAQmxSetDigEdgeArmStartTrigSrc(m_taskDOCtr,
286  formatString("/%s/ao/StartTrigger", intfAO()->devName()).c_str()));
287  CHECK_DAQMX_RET(DAQmxSetDigEdgeArmStartTrigEdge(m_taskDOCtr,
288  DAQmx_Val_Rising));
289  }
290  else {
291  CHECK_DAQMX_RET(DAQmxCfgDigEdgeStartTrig(m_taskDOCtr,
292  formatString("/%s/ao/StartTrigger", intfAO()->devName()).c_str(),
293  DAQmx_Val_Rising));
294  }
295  }
296 
297  if(m_pausingBit) {
298  CHECK_DAQMX_RET(DAQmxSetSampClkTimebaseActiveEdge(m_taskAO, DAQmx_Val_Rising));
299  CHECK_DAQMX_RET(DAQmxSetPauseTrigType(m_taskAO, DAQmx_Val_DigLvl));
300  CHECK_DAQMX_RET(DAQmxSetDigLvlPauseTrigSrc(m_taskAO, m_pausingSrcTerm.c_str()));
301  CHECK_DAQMX_RET(DAQmxSetDigLvlPauseTrigWhen(m_taskAO, DAQmx_Val_High));
302  }
303 
304  m_softwareTrigger->setArmTerm(
305  formatString("/%s/ao/SampleClock", intfAO()->devName()).c_str());
306 
307  //Buffer setup.
308  uInt32 onbrdsize, bufsize;
309 // CHECK_DAQMX_RET(DAQmxSetBufOutputOnbrdBufSize(m_taskAO, 4096u));
310  CHECK_DAQMX_RET(DAQmxGetBufOutputOnbrdBufSize(m_taskAO, &onbrdsize));
311  fprintf(stderr, "AO On-board bufsize = %d\n", (int)onbrdsize);
312  if(m_pausingBit)
313  buf_size_hint /= 4;
314  CHECK_DAQMX_RET(DAQmxGetBufOutputBufSize(m_taskAO, &bufsize));
315  if(bufsize < buf_size_hint)
316  CHECK_DAQMX_RET(DAQmxCfgOutputBuffer(m_taskAO, std::max((uInt32)buf_size_hint, onbrdsize / 4)));
317  CHECK_DAQMX_RET(DAQmxGetBufOutputBufSize(m_taskAO, &bufsize));
318  fprintf(stderr, "AO Using bufsize = %d\n", (int)bufsize);
319  m_preFillSizeAO = m_preFillSizeDO * oversamp;
320  m_transferSizeHintAO = std::min((unsigned int)onbrdsize / 2, m_transferSizeHintDO * oversamp);
321  CHECK_DAQMX_RET(DAQmxSetWriteRegenMode(m_taskAO, DAQmx_Val_DoNotAllowRegen));
322 
323  {
324 // CHECK_DAQMX_RET(DAQmxSetWriteWaitMode(m_taskAO, DAQmx_Val_Poll));
325 // CHECK_DAQMX_RET(DAQmxSetWriteSleepTime(m_taskAO, 0.001));
326  char ch[256];
327  CHECK_DAQMX_RET(DAQmxGetTaskChannels(m_taskAO, ch, sizeof(ch)));
328  if(intfAO()->productFlags() & XNIDAQmxInterface::FLAG_BUGGY_DMA_AO) {
329  CHECK_DAQMX_RET(DAQmxSetAODataXferMech(m_taskAO, ch,
330  DAQmx_Val_Interrupts));
331  }
332  if(intfAO()->productFlags() & XNIDAQmxInterface::FLAG_BUGGY_XFER_COND_AO) {
333  CHECK_DAQMX_RET(DAQmxSetAODataXferReqCond(m_taskAO, ch,
334  DAQmx_Val_OnBrdMemNotFull));
335  }
336 // CHECK_DAQMX_RET(DAQmxSetAOReglitchEnable(m_taskAO, ch, false));
337  }
338  //Voltage calibration/ranges.
339  for(unsigned int ch = 0; ch < NUM_AO_CH; ch++) {
340  //obtain range info.
341  for(unsigned int i = 0; i < CAL_POLY_ORDER; i++)
342  m_coeffAODev[ch][i] = 0.0;
343  CHECK_DAQMX_RET(DAQmxGetAODevScalingCoeff(m_taskAO,
344  formatString("%s/ao%d", intfAO()->devName(), ch).c_str(),
345  m_coeffAODev[ch], CAL_POLY_ORDER));
346  CHECK_DAQMX_RET(DAQmxGetAODACRngHigh(m_taskAO,
347  formatString("%s/ao%d", intfAO()->devName(), ch).c_str(),
348  &m_upperLimAO[ch]));
349  CHECK_DAQMX_RET(DAQmxGetAODACRngLow(m_taskAO,
350  formatString("%s/ao%d", intfAO()->devName(), ch).c_str(),
351  &m_lowerLimAO[ch]));
352  }
353 
354 /* CHECK_DAQMX_RET(DAQmxSetAOIdleOutputBehavior(m_taskAO,
355  formatString("%s/ao0:1", intfAO()->devName()).c_str(),
356  DAQmx_Val_ZeroVolts));
357 */
358 }
359 int32
360 XNIDAQmxPulser::onTaskDone_(TaskHandle task, int32 status, void *data) {
361  XNIDAQmxPulser *obj = static_cast<XNIDAQmxPulser*>(data);
362  obj->onTaskDone(task, status);
363  return status;
364 }
365 void
366 XNIDAQmxPulser::onTaskDone(TaskHandle task, int32 status) {
367  if(status) {
368  XString str;
369  if(task == m_taskDO) { str = "DO"; }
370  if(task == m_taskDOCtr) { str = "DOCtr"; }
371  if(task == m_taskAO) { str = "AO"; }
372  if(task == m_taskGateCtr) { str = "GateCtr"; }
373  gErrPrint(getLabel() + "\n" + str + "\n" + XNIDAQmxInterface::getNIDAQmxErrMessage(status));
374  try {
375  stopPulseGen();
376  }
377  catch (XInterface::XInterfaceError &e) {
378 // e.print();
379  }
380  }
381 }
382 template <typename T>
383 inline T *
384 fastFill(T* p, T x, unsigned int cnt) {
385  if(cnt > 100) {
386  for(;(intptr_t)p % (sizeof(uint64_t) / sizeof(T)); cnt--)
387  *p++ = x;
388  uint64_t *pp = (uint64_t *)p;
389  union {
390  uint64_t dw; T w[sizeof(uint64_t) / sizeof(T)];
391  } pack;
392  for(unsigned int i = 0; i < (sizeof(uint64_t) / sizeof(T)); i++)
393  pack.w[i] = x;
394  unsigned int pcnt = cnt / (sizeof(uint64_t) / sizeof(T));
395  cnt = cnt % (sizeof(uint64_t) / sizeof(T));
396  for(unsigned int i = 0; i < pcnt; i++)
397  *pp++ = pack.dw;
398  p = (T*)pp;
399  }
400  for(unsigned int i = 0; i < cnt; i++)
401  *p++ = x;
402  return p;
403 }
404 
405 void
406 XNIDAQmxPulser::preparePatternGen(const Snapshot &shot,
407  bool use_dummypattern, unsigned int blankpattern) {
408  if(use_dummypattern) {
409  //Creates dummy pattern.
410  shared_ptr<std::vector<GenPattern> > patlist_dummy(new std::vector<GenPattern>());
411  patlist_dummy->push_back(GenPattern(blankpattern, 100000));
412  patlist_dummy->push_back(GenPattern(blankpattern, 100000));
413  m_genPatternList = patlist_dummy;
414  for(unsigned int j = 0; j < PAT_QAM_MASK / PAT_QAM_PHASE; j++) {
415  m_genPulseWaveAO[j].reset();
416  }
417  }
418  else {
419  //Copies generated pattern from Payload.
420  m_genPatternList = shot[ *this].m_genPatternListNext;
421  for(unsigned int j = 0; j < PAT_QAM_MASK / PAT_QAM_PHASE; j++) {
422  m_genPulseWaveAO[j] = shot[ *this].m_genPulseWaveNextAO[j];
423  }
424  m_genAOZeroLevel = shot[ *this].m_genAOZeroLevelNext;
425  }
426 
427  //prepares ring buffers for pattern generation.
428  m_genLastPatIt = m_genPatternList->begin();
429  m_genRestCount = m_genPatternList->front().tonext;
430  m_genTotalCount += m_genPatternList->front().tonext;
431  m_patBufDO.reserve(m_preFillSizeDO);
432  if(m_taskAO != TASK_UNDEF) {
433  m_genAOIndex = 0;
434  m_patBufAO.reserve(m_preFillSizeAO);
435  }
436 
437  startBufWriter();
438 }
439 
440 void
441 XNIDAQmxPulser::startPulseGen(const Snapshot &shot) throw (XKameError &) {
442  XScopedLock<XRecursiveMutex> tlock(m_stateLock);
443  if(m_running && m_softwareTrigger->isPersistentCoherentMode()) {
444  startPulseGenFromFreeRun(shot);
445  return;
446  }
447 
448  stopPulseGen();
449 
450  unsigned int pausingbit = selectedPorts(shot, PORTSEL_PAUSING);
451  m_aswBit = selectedPorts(shot, PORTSEL_ASW);
452 
453  if((m_taskDO == TASK_UNDEF) ||
454  (m_pausingBit != pausingbit)) {
455  m_pausingBit = pausingbit;
456  clearTasks();
457  if(hasQAMPorts())
458  setupTasksAODO();
459  else
460  setupTasksDO(false);
461  }
462  {
463  uInt32 bufsize;
464  CHECK_DAQMX_RET(DAQmxGetBufOutputOnbrdBufSize(m_taskDO, &bufsize));
465  if( !m_pausingBit & (bufsize < 2047uL))
467  i18n("Use the pausing feature for a cheap DAQmx board.") + "\n"
468  + i18n("Look at the port-selection table."), __FILE__, __LINE__);
469  }
470  if(m_taskAO != TASK_UNDEF) {
471  uInt32 bufsize;
472  CHECK_DAQMX_RET(DAQmxGetBufOutputOnbrdBufSize(m_taskAO, &bufsize));
473  if( !m_pausingBit & (bufsize < 8192uL))
475  i18n("Use the pausing feature for a cheap DAQmx board.") + "\n"
476  + i18n("Look at the port-selection table."), __FILE__, __LINE__);
477  }
478 
479  m_genTotalCount = 0;
480  m_genTotalSamps = 0;
481 
482  const unsigned int cnt_prezeros = 1000;
483  m_genTotalCount += cnt_prezeros;
484  m_genTotalSamps += cnt_prezeros;
485  //prefilling of the buffers.
486  const unsigned int oversamp_ao = lrint(resolution() / resolutionQAM());
487  if(m_taskAO != TASK_UNDEF) {
488  //Pads preceding zeros.
489  CHECK_DAQMX_RET(DAQmxSetWriteRelativeTo(m_taskAO, DAQmx_Val_FirstSample));
490  CHECK_DAQMX_RET(DAQmxSetWriteOffset(m_taskAO, 0));
491  const unsigned int cnt_prezeros_ao = cnt_prezeros * oversamp_ao - 0;
492  m_genAOZeroLevel = shot[ *this].m_genAOZeroLevelNext;
493  std::vector<tRawAOSet> zeros(cnt_prezeros_ao, m_genAOZeroLevel);
494  int32 samps;
495  CHECK_DAQMX_RET(DAQmxWriteBinaryI16(m_taskAO, cnt_prezeros_ao,
496  false, 0.5,
497  DAQmx_Val_GroupByScanNumber,
498  zeros[0].ch,
499  &samps, NULL));
500  CHECK_DAQMX_RET(DAQmxSetWriteRelativeTo(m_taskAO, DAQmx_Val_CurrWritePos));
501  CHECK_DAQMX_RET(DAQmxSetWriteOffset(m_taskAO, 0));
502  }
503  //Pads preceding zeros.
504  std::vector<tRawDO> zeros(cnt_prezeros, 0);
505 
506  CHECK_DAQMX_RET(DAQmxSetWriteRelativeTo(m_taskDO, DAQmx_Val_FirstSample));
507  CHECK_DAQMX_RET(DAQmxSetWriteOffset(m_taskDO, 0));
508  int32 samps;
509  CHECK_DAQMX_RET(DAQmxWriteDigitalU16(m_taskDO, cnt_prezeros,
510  false, 0.0,
511  DAQmx_Val_GroupByScanNumber,
512  &zeros[0],
513  &samps, NULL));
514  CHECK_DAQMX_RET(DAQmxSetWriteRelativeTo(m_taskDO, DAQmx_Val_CurrWritePos));
515  CHECK_DAQMX_RET(DAQmxSetWriteOffset(m_taskDO, 0));
516 
517  //synchronizes with the software trigger.
518  m_softwareTrigger->start(1e3 / resolution());
519 
520  m_totalWrittenSampsDO = cnt_prezeros;
521  m_totalWrittenSampsAO = cnt_prezeros * oversamp_ao;
522 
523  m_isThreadWriterReady = false; //to be set to true in the buffer-writing thread.
524 
525  preparePatternGen(shot, false, 0);
526 
527  CHECK_DAQMX_RET(DAQmxTaskControl(m_taskDO, DAQmx_Val_Task_Commit));
528  if(m_taskDOCtr != TASK_UNDEF)
529  CHECK_DAQMX_RET(DAQmxTaskControl(m_taskDOCtr, DAQmx_Val_Task_Commit));
530  if(m_taskGateCtr != TASK_UNDEF)
531  CHECK_DAQMX_RET(DAQmxTaskControl(m_taskGateCtr, DAQmx_Val_Task_Commit));
532  if(m_taskAO != TASK_UNDEF)
533  CHECK_DAQMX_RET(DAQmxTaskControl(m_taskAO, DAQmx_Val_Task_Commit));
534 
535  //Waits for buffer filling.
536  XTime wait_since(XTime::now());
537  while( !m_isThreadWriterReady) {
538  if(m_threadWriter->isTerminated())
539  return;
540  msecsleep(1);
541  if(XTime::now() - wait_since > 1.0)
543  i18n("Buffer filling encountered time out."), __FILE__, __LINE__);
544  }
545  {
546  //Recovers from open state.
547  char ch[256];
548  CHECK_DAQMX_RET(DAQmxGetTaskChannels(m_taskDO, ch, sizeof(ch)));
549  CHECK_DAQMX_RET(DAQmxSetDOTristate(m_taskDO, ch, false));
550  }
551  // fprintf(stderr, "Prefilling done.\n");
552  //Slaves must start before the master.
553  CHECK_DAQMX_RET(DAQmxStartTask(m_taskDO));
554  if(m_taskGateCtr != TASK_UNDEF)
555  CHECK_DAQMX_RET(DAQmxStartTask(m_taskGateCtr));
556  if(m_taskDOCtr != TASK_UNDEF)
557  CHECK_DAQMX_RET(DAQmxStartTask(m_taskDOCtr));
558 // fprintf(stderr, "Starting AO....\n");
559  if(m_taskAO != TASK_UNDEF)
560  CHECK_DAQMX_RET(DAQmxStartTask(m_taskAO));
561  m_running = true;
562 }
563 void
564 XNIDAQmxPulser::startBufWriter() {
565  //Starting threads that writing buffers concurrently.
566  m_threadWriter.reset(new XThread<XNIDAQmxPulser>(shared_from_this(),
567  &XNIDAQmxPulser::executeWriter));
568  m_threadWriter->resume();
569 }
570 void
571 XNIDAQmxPulser::stopBufWriter() {
572  //Stops threads.
573  if(m_threadWriter) {
574  m_threadWriter->terminate();
575  m_threadWriter->waitFor();
576  m_threadWriter.reset();
577  }
578 }
579 void
580 XNIDAQmxPulser::stopPulseGen() {
581  XScopedLock<XRecursiveMutex> tlock(m_stateLock);
582  stopBufWriter();
583  abortPulseGen();
584 }
585 void
586 XNIDAQmxPulser::abortPulseGen() {
587  XScopedLock<XRecursiveMutex> tlock(m_stateLock);
588  {
589  m_softwareTrigger->stop();
590  if(m_running) {
591  {
592  char ch[256];
593  CHECK_DAQMX_RET(DAQmxGetTaskChannels(m_taskDO, ch, sizeof(ch)));
594  uInt32 num_lines;
595  CHECK_DAQMX_RET(DAQmxGetDONumLines(m_taskDO, ch, &num_lines));
596  //Sets outputs to open state except for the pausing bit.
597  XString chtri;
598  for(unsigned int i = 0; i < num_lines; i++) {
599  if(m_pausingBit &&
600  (lrint(log(m_pausingBit)/log(2.0)) == (int)i))
601  continue;
602  if(chtri.length())
603  chtri = chtri + ",";
604  chtri = chtri + formatString("%s/line%u", ch, i);
605  }
606  CHECK_DAQMX_RET(DAQmxSetDOTristate(m_taskDO, chtri.c_str(), true));
607  }
608  if(m_taskAO != TASK_UNDEF)
609  CHECK_DAQMX_RET(DAQmxStopTask(m_taskAO));
610  if(m_taskDOCtr != TASK_UNDEF)
611  CHECK_DAQMX_RET(DAQmxStopTask(m_taskDOCtr));
612  CHECK_DAQMX_RET(DAQmxStopTask(m_taskDO));
613  if(m_taskGateCtr != TASK_UNDEF) {
614  try {
615  CHECK_DAQMX_RET(DAQmxWaitUntilTaskDone(m_taskGateCtr, 0.1));
616  }
617  catch (XInterface::XInterfaceError &) { } //ignores timeout
618  CHECK_DAQMX_ERROR(DAQmxStopTask(m_taskGateCtr)); //ignores warning
619  }
620  if(m_taskAO != TASK_UNDEF)
621  CHECK_DAQMX_RET(DAQmxTaskControl(m_taskAO, DAQmx_Val_Task_Unreserve));
622  if(m_taskDOCtr != TASK_UNDEF)
623  CHECK_DAQMX_RET(DAQmxTaskControl(m_taskDOCtr, DAQmx_Val_Task_Unreserve));
624  CHECK_DAQMX_RET(DAQmxTaskControl(m_taskDO, DAQmx_Val_Task_Unreserve));
625  if(m_taskGateCtr != TASK_UNDEF)
626  CHECK_DAQMX_RET(DAQmxTaskControl(m_taskGateCtr, DAQmx_Val_Task_Unreserve));
627  }
628 
629  m_running = false;
630  }
631 }
632 
633 void
634 XNIDAQmxPulser::rewindBufPos(double ms_from_gen_pos) {
635  int32 cnt_from_gen_pos = lrint(ms_from_gen_pos / resolution());
636  const unsigned int oversamp_ao = lrint(resolution() / resolutionQAM());
637  uInt64 samp_gen;
638  CHECK_DAQMX_RET(DAQmxGetWriteTotalSampPerChanGenerated(m_taskDO, &samp_gen));
639  if(m_taskAO != TASK_UNDEF) {
640  uInt64 samp_gen_ao, currpos_ao;
641  CHECK_DAQMX_RET(DAQmxGetWriteTotalSampPerChanGenerated(m_taskAO, &samp_gen_ao));
642  samp_gen = std::max(samp_gen_ao / oversamp_ao, samp_gen);
643  }
644  uint64_t count_gen;
645  uint64_t count_old = m_genTotalCount;
646  assert(m_genRestCount == 0);
647  for(auto it = m_queueTimeGenCnt.begin(); it != m_queueTimeGenCnt.end(); ++it) {
648  if(it->second > samp_gen) {
649  count_gen = it->first;
650  break;
651  }
652  }
653  for(auto it = m_queueTimeGenCnt.begin(); it != m_queueTimeGenCnt.end(); ++it) {
654  if(it->first > count_gen + cnt_from_gen_pos) {
655  m_genTotalCount = it->first;
656  m_genTotalSamps = it->second;
657  m_genRestCount = 0;
658  break;
659  }
660  }
661  uint64_t currsamps = m_totalWrittenSampsDO;
662  if(m_taskAO != TASK_UNDEF)
663  currsamps = std::min(currsamps, m_totalWrittenSampsAO / oversamp_ao);
664  if(m_genTotalSamps > currsamps) {
665  //Requested time is beyond the current buffer writing position.
666  for(auto rit = m_queueTimeGenCnt.rbegin(); rit != m_queueTimeGenCnt.rend(); ++rit) {
667  if(rit->second <= currsamps) {
668  m_genTotalCount = rit->first;
669  m_genTotalSamps = rit->second;
670  m_genRestCount = 0;
671  break;
672  }
673  }
674  }
675  if(m_genTotalSamps > currsamps) {
676  m_genTotalSamps = currsamps;
677  m_genTotalCount = count_old;
678  m_genRestCount = 0;
679  }
680 
681  CHECK_DAQMX_RET(DAQmxSetWriteOffset(m_taskDO, -(int32_t)(m_totalWrittenSampsDO - m_genTotalSamps)));
682  if(m_taskAO != TASK_UNDEF) {
683  CHECK_DAQMX_RET(DAQmxSetWriteOffset(m_taskAO, -(int32_t)(m_totalWrittenSampsAO - m_genTotalSamps * oversamp_ao)));
684  }
685  fprintf(stderr, "Rewind: %g,%g,%g,%g,%g\n", (double)samp_gen, (double)m_totalWrittenSampsDO,
686  (double)count_gen,(double)m_genTotalCount, (double)m_genTotalSamps);;
687 
688  m_totalWrittenSampsDO = m_genTotalSamps;
689  m_totalWrittenSampsAO = m_genTotalSamps * oversamp_ao;
690 
691  //Writes dummy data, because DAQmxGetWriteSpaceAvail() cannot be performed after setting offset.
692  const unsigned int cnt_prezeros = 1000;
693  msecsleep(cnt_prezeros * resolution());
694 
695  m_genTotalCount += cnt_prezeros;
696  m_genTotalSamps += cnt_prezeros;
697  //prefilling of the buffers.
698  if(m_taskAO != TASK_UNDEF) {
699  //Pads preceding zeros.
700  const unsigned int oversamp_ao = lrint(resolution() / resolutionQAM());
701  const unsigned int cnt_prezeros_ao = cnt_prezeros * oversamp_ao - 0;
702  std::vector<tRawAOSet> zeros(cnt_prezeros_ao, m_genAOZeroLevel);
703  int32 samps;
704  CHECK_DAQMX_RET(DAQmxWriteBinaryI16(m_taskAO, cnt_prezeros_ao,
705  false, 0.5,
706  DAQmx_Val_GroupByScanNumber,
707  zeros[0].ch,
708  &samps, NULL));
709  CHECK_DAQMX_RET(DAQmxSetWriteRelativeTo(m_taskAO, DAQmx_Val_CurrWritePos));
710  CHECK_DAQMX_RET(DAQmxSetWriteOffset(m_taskAO, 0));
711  }
712  //Pads preceding zeros.
713  std::vector<tRawDO> zeros(cnt_prezeros, 0);
714 
715  int32 samps;
716  CHECK_DAQMX_RET(DAQmxWriteDigitalU16(m_taskDO, cnt_prezeros,
717  false, 0.0,
718  DAQmx_Val_GroupByScanNumber,
719  &zeros[0],
720  &samps, NULL));
721  CHECK_DAQMX_RET(DAQmxSetWriteRelativeTo(m_taskDO, DAQmx_Val_CurrWritePos));
722  CHECK_DAQMX_RET(DAQmxSetWriteOffset(m_taskDO, 0));
723 
724  m_totalWrittenSampsDO += cnt_prezeros;
725  m_totalWrittenSampsAO += cnt_prezeros * oversamp_ao;
726 }
727 void
728 XNIDAQmxPulser::stopPulseGenFreeRunning(unsigned int blankpattern) {
729  XScopedLock<XRecursiveMutex> tlock(m_stateLock);
730  {
731  //clears sent software triggers.
732  m_softwareTrigger->clear();
733  stopBufWriter();
734 
735  //sets position padding=150ms. after the current generating position.
736  rewindBufPos(150.0);
737  preparePatternGen(Snapshot( *this), true, blankpattern);
738  }
739 }
740 void
741 XNIDAQmxPulser::startPulseGenFromFreeRun(const Snapshot &shot) {
742  //clears sent software triggers.
743  m_softwareTrigger->clear();
744  stopBufWriter();
745 
746  //sets position padding=150ms. after the current generating position.
747  rewindBufPos(150.0);
748  preparePatternGen(shot, false, 0);
749 }
750 
752 XNIDAQmxPulser::aoVoltToRaw(const double poly_coeff[NUM_AO_CH][CAL_POLY_ORDER], const std::complex<double> &volt) {
753  const double volts[] = {volt.real(), volt.imag()};
754  tRawAOSet z;
755  for(unsigned int ch = 0; ch < NUM_AO_CH; ch++) {
756  double x = 1.0;
757  double y = 0.0;
758  const double *pco = poly_coeff[ch];
759  for(unsigned int i = 0; i < CAL_POLY_ORDER; i++) {
760  y += ( *pco++) * x;
761  x *= volts[ch];
762  }
763  z.ch[ch] = lrint(y);
764  }
765  return z;
766 }
767 
768 void *
769 XNIDAQmxPulser::executeWriter(const atomic<bool> &terminating) {
770  double dma_do_period = resolution();
771  double dma_ao_period = resolutionQAM();
772  uint64_t written_total_ao = 0, written_total_do = 0;
773 
774  //Starting a child thread generating patterns concurrently.
775  XThread<XNIDAQmxPulser> th_genbuf(shared_from_this(),
776  &XNIDAQmxPulser::executeFillBuffer);
777  th_genbuf.resume();
778 
779  while( !terminating) {
780  const tRawDO *pDO = m_patBufDO.curReadPos();
781  ssize_t samps_do = m_patBufDO.writtenSize();
782  const tRawAOSet *pAO = NULL;
783  ssize_t samps_ao = 0;
784  if(m_taskAO != TASK_UNDEF) {
785  pAO = m_patBufAO.curReadPos();
786  samps_ao = m_patBufAO.writtenSize();
787  }
788  if( !samps_do && !samps_ao) {
789  msecsleep(lrint(std::min(m_transferSizeHintDO * dma_do_period,
790  m_transferSizeHintAO * dma_ao_period) / 2));
791  continue;
792  }
793  try {
794  ssize_t written;
795  if(samps_ao > samps_do) {
796  written = writeToDAQmxAO(pAO, std::min(samps_ao, (ssize_t)m_transferSizeHintAO));
797  if(written) m_patBufAO.finReading(written);
798  written_total_ao += written;
799  }
800  else {
801  written = writeToDAQmxDO(pDO, std::min(samps_do, (ssize_t)m_transferSizeHintDO));
802  if(written) m_patBufDO.finReading(written);
803  written_total_do += written;
804  }
805  if((written_total_do > m_preFillSizeDO) && ( !pAO || (written_total_ao > m_preFillSizeAO)))
806  m_isThreadWriterReady = true; //Count written into the devices has exceeded a certain value.
807  }
808  catch (XInterface::XInterfaceError &e) {
809  e.print(getLabel());
810 
811  m_threadWriter->terminate();
812  XScopedLock<XRecursiveMutex> tlock(m_stateLock);
813  if(m_running) {
814  try {
815  abortPulseGen();
816  }
817  catch (XInterface::XInterfaceError &e) {
818  // e.print(getLabel());
819  }
820  break;
821  }
822  }
823  }
824  m_totalWrittenSampsDO += written_total_do;
825  m_totalWrittenSampsAO += written_total_ao;
826 
827  th_genbuf.terminate();
828  th_genbuf.waitFor();
829  return NULL;
830 }
831 
832 ssize_t
833 XNIDAQmxPulser::writeToDAQmxAO(const tRawAOSet *pAO, ssize_t samps) {
834  uInt32 space;
835  CHECK_DAQMX_RET(DAQmxGetWriteSpaceAvail(m_taskAO, &space));
836  if(space < (uInt32)samps)
837  return 0;
838  int32 written;
839  CHECK_DAQMX_RET(DAQmxWriteBinaryI16(m_taskAO, samps, false, 0.0,
840  DAQmx_Val_GroupByScanNumber,
841  const_cast<tRawAOSet *>(pAO)->ch,
842  &written, NULL));
843  return written;
844 }
845 ssize_t
846 XNIDAQmxPulser::writeToDAQmxDO(const tRawDO *pDO, ssize_t samps) {
847  uInt32 space;
848  CHECK_DAQMX_RET(DAQmxGetWriteSpaceAvail(m_taskDO, &space));
849  if(space < (uInt32)samps)
850  return 0;
851  int32 written;
852  CHECK_DAQMX_RET(DAQmxWriteDigitalU16(m_taskDO, samps, false, 0.0,
853  DAQmx_Val_GroupByScanNumber,
854  const_cast<tRawDO *>(pDO),
855  &written, NULL));
856  return written;
857 }
858 
859 template <bool UseAO>
860 inline bool
862  unsigned int oversamp_ao = lrint(resolution() / resolutionQAM());
863 
864  GenPatternIterator it = m_genLastPatIt;
865  uint32_t pat = it->pattern;
866  uint64_t tonext = m_genRestCount;
867  uint64_t total = m_genTotalCount;
868  unsigned int aoidx = m_genAOIndex;
869  tRawDO pausingbit = m_pausingBit;
870  tRawDO aswbit = m_aswBit;
871  uint64_t pausing_cnt = m_pausingCount;
872  uint64_t pausing_cnt_blank_before = PAUSING_BLANK_BEFORE + PAUSING_BLANK_AFTER;
873  uint64_t pausing_cnt_blank_after = 1;
874  uint64_t pausing_period = pausing_cnt + pausing_cnt_blank_before + pausing_cnt_blank_after;
875  uint64_t pausing_cost = std::max(16uLL, pausing_cnt_blank_before + pausing_cnt_blank_after);
876 
877  shared_ptr<XNIDAQmxInterface::SoftwareTrigger> &vt = m_softwareTrigger;
878 
879  tRawDO *pDO = m_patBufDO.curWritePos();
880  tRawDO *pDOorg = pDO;
881  if( !pDO)
882  return false;
883  tRawAOSet *pAO = (UseAO) ? m_patBufAO.curWritePos() : NULL;
884  if( !pAO && UseAO)
885  return false;
886  tRawAOSet raw_zero = m_genAOZeroLevel;
887  ssize_t capacity = m_patBufDO.chunkSize();
888  if(UseAO)
889  capacity = std::min(capacity, m_patBufAO.chunkSize() / (ssize_t)oversamp_ao);
890  for(unsigned int samps_rest = capacity; samps_rest;) {
891  unsigned int pidx = (pat & PAT_QAM_PULSE_IDX_MASK) / PAT_QAM_PULSE_IDX;
892  //Bits for digital lines.
893  tRawDO patDO = PAT_DO_MASK & pat;
894  //Case: QAM and ASW is off.
895  if(pausingbit && (pidx == 0) && !(pat & aswbit)) {
896  //generates a pausing trigger.
897  assert(tonext > 0);
898  unsigned int lps = (unsigned int)std::min(
899  (uint64_t)(samps_rest / pausing_cost), (tonext - 1) / pausing_period);
900  patDO &= ~pausingbit;
901  if(lps) {
902  tonext -= lps * pausing_period;
903  samps_rest -= lps * pausing_cost;
904  tRawDO patDO_or_p = patDO | pausingbit;
905  for(unsigned int lp = 0; lp < lps; lp++) {
906  for(int i = 0; i < pausing_cnt_blank_before; i++)
907  *pDO++ = patDO_or_p;
908  for(int i = 0; i < pausing_cnt_blank_after; i++)
909  *pDO++ = patDO;
910  }
911  if(UseAO)
912  pAO = fastFill(pAO, raw_zero, lps * oversamp_ao * (pausing_cnt_blank_before + pausing_cnt_blank_after));
913  }
914  assert(tonext > 0);
915  if(samps_rest < pausing_cost)
916  break; //necessary for frequent tagging of buffer.
917  }
918  //number of samples to be written into buffer.
919  unsigned int gen_cnt = std::min((uint64_t)samps_rest, tonext);
920  //writes digital pattern.
921  pDO = fastFill(pDO, patDO, gen_cnt);
922 
923  if(UseAO) {
924  if(pidx == 0) {
925  //writes a blank in analog lines.
926  aoidx = 0;
927  pAO = fastFill(pAO, raw_zero, gen_cnt * oversamp_ao);
928  }
929  else {
930  unsigned int pnum = (pat & PAT_QAM_MASK) / PAT_QAM_PHASE - (PAT_QAM_PULSE_IDX/PAT_QAM_PHASE);
931  assert(pnum < PAT_QAM_PULSE_IDX_MASK / PAT_QAM_PULSE_IDX);
932  if( !m_genPulseWaveAO[pnum].get() ||
933  (m_genPulseWaveAO[pnum]->size() < gen_cnt * oversamp_ao + aoidx))
934  throw XInterface::XInterfaceError(i18n("Invalid pulse patterns."), __FILE__, __LINE__);
935  tRawAOSet *pGenAO = &m_genPulseWaveAO[pnum]->at(aoidx);
936  memcpy(pAO, pGenAO, gen_cnt * oversamp_ao * sizeof(tRawAOSet));
937  pAO += gen_cnt * oversamp_ao;
938  aoidx += gen_cnt * oversamp_ao;
939  }
940  }
941  tonext -= gen_cnt;
942  samps_rest -= gen_cnt;
943  if(tonext == 0) {
944  it++;
945  if(it == m_genPatternList->end()) {
946  it = m_genPatternList->begin();
947 // printf("p.\n");
948  }
949  tonext = it->tonext;
950  vt->changeValue(pat, it->pattern, total);
951  pat = it->pattern;
952  total += tonext;
953  }
954  }
955  m_genRestCount = tonext;
956  m_genLastPatIt = it;
957  m_genAOIndex = aoidx;
958  m_genTotalCount = total;
959  m_genTotalSamps += pDO - pDOorg;
960  //Here ensures the pattern data is ready.
961  m_patBufDO.finWriting(pDO);
962  if(UseAO)
963  m_patBufAO.finWriting(pAO);
964  return true;
965 }
966 void *
967 XNIDAQmxPulser::executeFillBuffer(const atomic<bool> &terminating) {
968  m_queueTimeGenCnt.clear();
969  try {
970  while( !terminating) {
971  bool buffer_not_full;
972  if(m_taskAO != TASK_UNDEF) {
973  buffer_not_full = fillBuffer<true>();
974  }
975  else {
976  buffer_not_full = fillBuffer<false>();
977  }
978  if( !m_queueTimeGenCnt.size() ||
979  (m_genTotalCount - m_genRestCount - m_queueTimeGenCnt.back().first > lrint(20.0 / resolution()))) {
980  m_queueTimeGenCnt.push_back(std::pair<uint64_t, uint64_t>(
981  m_genTotalCount - m_genRestCount, m_genTotalSamps)); //preserves every 20ms.
982  }
983  while(m_genTotalCount - m_genRestCount - m_queueTimeGenCnt.front().first > lrint(20000.0 / resolution())) {
984  m_queueTimeGenCnt.pop_front(); //limits only within last 20s.
985  }
986  if( !buffer_not_full) {
987  //Waiting until previous data have been sent.
988  double dma_do_period = resolution();
989  msecsleep(lrint(m_transferSizeHintDO * dma_do_period / 2));
990  }
991  }
992  m_genTotalCount -= m_genRestCount;
993  m_genRestCount = 0;
994  }
995  catch (XInterface::XInterfaceError &e) {
996  e.print(getLabel() + ": ", __FILE__, __LINE__, 0);
997  }
998  return NULL;
999 }
1000 void
1002  const Snapshot &shot(tr);
1003  tr[ *this].m_genPatternListNext.reset(new std::vector<GenPattern>);
1004  uint32_t pat = shot[ *this].relPatList().back().pattern;
1005  for(Payload::RelPatList::const_iterator it = shot[ *this].relPatList().begin();
1006  it != shot[ *this].relPatList().end(); it++) {
1007  uint64_t tonext = it->toappear;
1008 
1009  GenPattern genpat(pat, tonext);
1010  tr[ *this].m_genPatternListNext->push_back(genpat);
1011  pat = it->pattern;
1012  }
1013 
1014  if(hasQAMPorts()) {
1015  double offset[] = { shot[ *qamOffset1()], shot[ *qamOffset2()]};
1016  double level[] = { shot[ *qamLevel1()], shot[ *qamLevel2()]};
1017  double coeffAO[NUM_AO_CH][CAL_POLY_ORDER];
1018 
1019  for(unsigned int ch = 0; ch < NUM_AO_CH; ch++) {
1020  //arranges range info.
1021  double x = 1.0;
1022  for(unsigned int i = 0; i < CAL_POLY_ORDER; i++) {
1023  coeffAO[ch][i] = m_coeffAODev[ch][i] * x
1024  + ((i == 0) ? offset[ch] : 0);
1025  x *= level[ch];
1026  }
1027  }
1028  tr[ *this].m_genAOZeroLevelNext = aoVoltToRaw(coeffAO, std::complex<double>(0.0));
1029  std::complex<double> c(pow(10.0, shot[ *this].masterLevel() / 20.0), 0);
1030  for(unsigned int i = 0; i < PAT_QAM_PULSE_IDX_MASK/PAT_QAM_PULSE_IDX; i++) {
1031  for(unsigned int qpsk = 0; qpsk < 4; qpsk++) {
1032  const unsigned int pnum = i * (PAT_QAM_PULSE_IDX/PAT_QAM_PHASE) + qpsk;
1033  tr[ *this].m_genPulseWaveNextAO[pnum].reset(new std::vector<tRawAOSet>);
1034  for(std::vector<std::complex<double> >::const_iterator it =
1035  shot[ *this].qamWaveForm(i).begin(); it != shot[ *this].qamWaveForm(i).end(); it++) {
1036  std::complex<double> z( *it * c);
1037  tr[ *this].m_genPulseWaveNextAO[pnum]->push_back(aoVoltToRaw(coeffAO, z));
1038  }
1039  c *= std::complex<double>(0,1);
1040  }
1041  }
1042  }
1043 }
1044 
1045 
1046 void
1047 XNIDAQmxPulser::changeOutput(const Snapshot &shot, bool output, unsigned int blankpattern) {
1048  XScopedLock<XInterface> lock( *interface());
1049  if( !interface()->isOpened())
1050  return;
1051  XScopedLock<XRecursiveMutex> tlock(m_stateLock);
1052  if(output) {
1053  if( !shot[ *this].m_genPatternListNext || shot[ *this].m_genPatternListNext->empty() )
1054  throw XInterface::XInterfaceError(i18n("Pulser Invalid pattern"), __FILE__, __LINE__);
1055  startPulseGen(shot);
1056  }
1057  else {
1058  if(m_running && m_softwareTrigger->isPersistentCoherentMode())
1059  stopPulseGenFreeRunning(blankpattern);
1060  else
1061  stopPulseGen();
1062  }
1063 }

Generated for KAME4 by  doxygen 1.8.3