triggerthread.cc
Upload User: liguoxi
Upload Date: 2008-08-02
Package Size: 1125k
Code Size: 15k
Category:

USB develop

Development Platform:

Unix_Linux

  1. #include "../oconfig.h"
  2. #include "../core/triggerthread.h"
  3. #include <Qt/qapplication.h>
  4. #include <unistd.h>
  5. template<typename T>inline T MAX(T a,T b)
  6. {  return(a>b ? a : b);  }
  7. DataBuffer::IData *TriggerThread::_DoDownSampling(Channel *chan,
  8. const DataBuffer *db)
  9. {
  10. // FIXME: We may end up with one sample per DataBuffer if we have 
  11. //        a downsampling factor which is greater than the (standard) 
  12. //        buffer size. 
  13. size_t buflen=db->length();
  14. size_t input_samples = chan->avg_nsamp+buflen;
  15. size_t output_samples = input_samples/tp.downsampling_fact;
  16. DataBuffer::IData *dest=NULL;
  17. if(output_samples)
  18. {
  19. // Warn user. This is the result of the FIXME above!
  20. if(output_samples<10)
  21. {  fprintf(stderr,"output_samples very low (%u)...n",output_samples);  }
  22. dest = new(output_samples) DataBuffer::IData(output_samples);
  23. }
  24. // else: idata stays NULL. 
  25. int nsamp=chan->avg_nsamp;
  26. int maxsamp=(int)tp.downsampling_fact;
  27. assert(nsamp<maxsamp);
  28. size_t dest_i=0;
  29. const unsigned char *data=(const unsigned char*)db->buf();
  30. if(tp.downsampling_avg)
  31. {
  32. int accu=chan->avg_accu;
  33. for(size_t i=0; i<buflen; i++)
  34. {
  35. accu+=(int)(data[i]);
  36. if(++nsamp==maxsamp)
  37. {
  38. dest->buf[dest_i++]=(unsigned char)(accu/nsamp);
  39. accu=0;
  40. nsamp=0;
  41. }
  42. }
  43. chan->avg_accu=accu;
  44. }
  45. else
  46. {
  47. size_t i=maxsamp-nsamp-1;
  48. for(; i<buflen; i+=maxsamp)
  49. {  dest->buf[dest_i++]=data[i];  }
  50. //fprintf(stderr,"dest_i=%u, dest->len=%un",dest_i,dest->len);
  51. nsamp=i-buflen;
  52. }
  53. chan->avg_nsamp=nsamp;
  54. assert(!dest || dest_i==dest->len);
  55. return(dest);
  56. }
  57. TriggerThread::NBState TriggerThread::_GetNextBuffer(StreamBuffer *store_here)
  58. {
  59. iterate:;
  60. // Get data from queue or wait for new data. 
  61. NBState rv=NB_OK;
  62. bool buf_ok=0;
  63. mutex.lock();
  64. for(;;)
  65. {
  66. // Do not change the order here!
  67. if(do_quit)
  68. {  rv=NB_Quit;  break;  }
  69. if(do_restart)
  70. {  rv=NB_Restart;  do_restart=0;  break;  }
  71. // If we have data in the queue, process it. 
  72. if(!bq.IsEmpty())
  73. {
  74. bq.PopTail(store_here);
  75. buf_ok=1;
  76. stream_tot_size-=store_here->dbA.length()+store_here->dbB.length();
  77. //char c='0'+(char)trig_state;  write(1,&c,1);
  78. if(trigger_now)
  79. {  rv=NB_TriggerNow;  trigger_now=0;  seen_gui_ack=0;  }
  80. if(seen_gui_ack)
  81. {  rv=NB_GUIAck;  seen_gui_ack=0;  }
  82. //else rv=NB_OK...
  83. break;
  84. }
  85. bool cond_rv=wcond.wait(&mutex,/*msec_timeout=*/ULONG_MAX);
  86. // cond_rv=true if woken up and false on timeout. 
  87. }
  88. mutex.unlock();
  89. // Do down-sampling if needed. 
  90. if(buf_ok && tp.downsampling_fact>1)
  91. {
  92. DataBuffer::IData *nbA=_DoDownSampling(&A,&store_here->dbA);
  93. DataBuffer::IData *nbB=_DoDownSampling(&B,&store_here->dbB);
  94. // If we do SOO much downsampling that the buffer is empty, 
  95. // get a new one. 
  96. assert(!nbA==!nbB);
  97. if(!nbA)
  98. {  goto iterate;  }
  99. // Otherwise, use down-sampled buffers. 
  100. store_here->dbA=DataBuffer(nbA);
  101. store_here->dbB=DataBuffer(nbB);
  102. }
  103. return(rv);
  104. }
  105. size_t TriggerThread::_TrigScanSignal(size_t bufi,const DataBuffer *db,
  106. int slope_mul)
  107. {
  108. Channel *chan=NULL;
  109. switch(tp.trig_src)
  110. {
  111. case TS_None:  break;  // chan stays NULL
  112. case TS_ChanA:  chan=&A;  break;
  113. case TS_ChanB:  chan=&B;  break;
  114. default:  assert(0);
  115. }
  116. if(!chan)
  117. {  return(db->length());  }
  118. switch(chan->cmode)
  119. {
  120. case CM_Off:  return(db->length());
  121. case CM_Analog:
  122. {
  123. // Analog trigger. 
  124. size_t buflen=db->length();
  125. const unsigned char *data=(unsigned char*)db->buf();
  126. int lvl=tp.trig_level+127;
  127. // slope=+1 -> trigger if v>level
  128. // slope=-1 -> trigger if v<level
  129. if(tp.trig_slope*slope_mul>0)
  130. {
  131. for(size_t i=bufi; i<buflen; i++)
  132. {
  133. int v=(int)data[i];
  134. if(v>lvl)  return(i);
  135. }
  136. }
  137. else if(tp.trig_slope*slope_mul<0)
  138. {
  139. for(size_t i=bufi; i<buflen; i++)
  140. {
  141. int v=(int)data[i];
  142. if(v<lvl)  return(i);
  143. }
  144. }
  145. else assert(0);
  146. } break;
  147. case CM_Digital:
  148. fprintf(stderr,"Digital trigger not yet supported.n");
  149. return(db->length());
  150. default:  assert(0);
  151. }
  152. return(db->length());
  153. }
  154. void TriggerThread::_AppendSampleBuf(Channel *chan,const DataBuffer *db)
  155. {
  156. if(chan->cmode==CM_Off)
  157. {
  158. chan->sb.clear();
  159. return;
  160. }
  161. chan->sb.append(*db);
  162. }
  163. void TriggerThread::_SamplingDone()
  164. {
  165. // Okay, so we need to transfer the data to the GUI thread for 
  166. // display. 
  167. fprintf(stderr,"Delivering result (%u, %u netto samples (should be %u),"
  168. "st=%u,%u)... ",
  169. A.sb.NettoSamples(),B.sb.NettoSamples(),tp.nsamples,
  170. A.sb.SkipTail(),B.sb.SkipTail());
  171. if(data_receiver)
  172. {
  173. // We clear our buffers after the copy so that we don't get problems 
  174. // when continuing to sample and throwing in data when cut_head is set..
  175. SampleDataEvent *ev = new SampleDataEvent();
  176. ev->sbA.ShallowCopy(A.sb);  A.sb.clear();
  177. ev->sbB.ShallowCopy(B.sb);  B.sb.clear();
  178. QApplication::postEvent(data_receiver,ev);
  179. }
  180. fprintf(stderr,"donen");
  181. }
  182. void TriggerThread::_SendTrigStateEvent()
  183. {
  184. if(data_receiver)
  185. {
  186. TriggerStateEvent *ev = new TriggerStateEvent();
  187. ev->trig_state=trig_state;
  188. QApplication::postEvent(data_receiver,ev);
  189. }
  190. }
  191. void TriggerThread::_ResetRestartTrigger()
  192. {
  193. trig_state=TS_Arming;  _SendTrigStateEvent();
  194. A.avg_nsamp=0;  A.avg_accu=0;  A.sb.clear();
  195. B.avg_nsamp=0;  B.avg_accu=0;  B.sb.clear();
  196. fprintf(stderr,"Trigger: restartn");
  197. }
  198. void TriggerThread::_RunTriggerThread()
  199. {
  200. // Trigger states: 
  201. //  TS_Arming:
  202. //     copy samples to arm sample buffer for triggering
  203. //  TS_WaitForTrigger0: 
  204. //     sample input and wait for signal to go below trigger level 
  205. //     (when triggering on rising edge; otherwise wait to go above 
  206. //     trigger level) 
  207. //  TS_WaitForTrigger1: 
  208. //     sample input and wait for signal to go above trigger level 
  209. //     (when triggering on rising edge; otherwise wait to go below 
  210. //     trigger level) and fire trigger in that case (-> TS_Triggered)
  211. //  TS_Triggered:
  212. //     simply copy data until samples are done
  213. //  TS_HoldOff
  214. //     discart data for holdoff time
  215. //  TS_WaitACK
  216. //     wait for ACK by GUI thread so that we don'r run too fast!
  217. // Current buffer (already dequeued). 
  218. StreamBuffer curr;
  219. // bufi: Index in current buffer. 
  220. //       If bufi==buffer length, get new buffer. 
  221. size_t bufi=0;
  222. // Buffer length. 
  223. size_t buflen=0;
  224. for(;;)
  225. {
  226. restart:;
  227. // Get next buffer or wait for it. 
  228. bool do_trig_now=0;
  229. bool got_gui_ack=0;
  230. if(bufi==buflen)  // ==curr.dbB.length() ALWAYS
  231. {
  232. NBState nbs=_GetNextBuffer(&curr);
  233. switch(nbs)
  234. {
  235. case NB_OK:       break;
  236. case NB_TriggerNow:  do_trig_now=1;  break;
  237. case NB_GUIAck:      got_gui_ack=1;  break;
  238. case NB_Quit:     return;
  239. case NB_Restart:
  240. {
  241. _CopyNewParam();
  242. _ResetRestartTrigger();
  243. goto restart;
  244. }
  245. default: assert(0); break;
  246. }
  247. bufi=0;
  248. buflen=curr.dbA.length();
  249. assert(buflen);
  250. assert(buflen==curr.dbB.length());
  251. //write(1,"u",1);
  252. // For now, we put everything into the sample buffer (unless the 
  253. // channel is switched off) and trim it afterwards if needed. 
  254. _AppendSampleBuf(&A,&curr.dbA);
  255. _AppendSampleBuf(&B,&curr.dbB);
  256. // Make sure we don't collect too much data: 
  257. //fprintf(stderr,"TrimMin(%u+%u)n",tp.trig_h_pos,buflen);
  258. switch(trig_state)
  259. {
  260. case TS_Arming:           // fall 
  261. case TS_WaitForTrigger0:  //   through
  262. case TS_WaitForTrigger1:  //     here
  263. if(A.cmode!=CM_Off)
  264. {  A.sb.TrimMin(tp.trig_h_pos+buflen);  }
  265. if(B.cmode!=CM_Off)
  266. {  B.sb.TrimMin(tp.trig_h_pos+buflen);  }
  267. break;
  268. case TS_Triggered:
  269. case TS_HoldOff:
  270. case TS_WaitACK:
  271. if(A.cmode!=CM_Off)
  272. {  A.sb.TrimMin(tp.nsamples+buflen+A.sb.SkipTail()+A.sb.CutHead());  }
  273. if(B.cmode!=CM_Off)
  274. {  B.sb.TrimMin(tp.nsamples+buflen+B.sb.SkipTail()+B.sb.CutHead());  }
  275. break;
  276. default:  assert(0);
  277. }
  278. //write(1,"v",1);
  279. // See if trigger is finally armed. 
  280. // This is the case when all enabled channels have enough samples 
  281. // and at least one channel IS enabled. 
  282. if(trig_state==TS_Arming)
  283. {
  284. if((A.cmode || B.cmode) && 
  285. (A.cmode==CM_Off || A.sb.NSamples()>=tp.trig_h_pos) && 
  286. (B.cmode==CM_Off || B.sb.NSamples()>=tp.trig_h_pos) )
  287. {
  288. // Make sure we consume all the samples up to the armed 
  289. // point. 
  290. // NOTE: The MAX should just clip off zero!
  291. size_t nsamp=MAX(A.sb.NSamples(),B.sb.NSamples());
  292. assert(!A.sb.NSamples() || !B.sb.NSamples() || 
  293. A.sb.NSamples()==B.sb.NSamples());
  294. size_t beyond = nsamp-tp.trig_h_pos;
  295. if(beyond<buflen)
  296. {  bufi=buflen-beyond;  }
  297. trig_state=TS_WaitForTrigger0;  _SendTrigStateEvent();
  298. }
  299. else
  300. {
  301. // Otherwise, go on arming...
  302. bufi=buflen;
  303. }
  304. }
  305. }
  306. //{ char c='a'+(char)trig_state;  write(1,&c,1);  }
  307. if(trig_state==TS_WaitACK)
  308. {
  309. if(got_gui_ack || do_trig_now)
  310. {
  311. _ResetRestartTrigger();  // Will start with TS_Arming. 
  312. }
  313. else
  314. {
  315. // Discard. 
  316. bufi=buflen;
  317. }
  318. }
  319. // Depending on the trigger state, we do nothing or search for 
  320. // the trigger signal. 
  321. // If do_trig_now is set, trigger at bufi. 
  322. if(do_trig_now)
  323. {
  324. if(trig_state==TS_Triggered || trig_state==TS_Arming)
  325. {  do_trig_now=0;  }  // ignore
  326. }
  327. else if(trig_state==TS_WaitForTrigger0 || 
  328.    trig_state==TS_WaitForTrigger1 )
  329. {
  330. DataBuffer *trig_db=NULL;
  331. switch(tp.trig_src)
  332. {
  333. case TS_ChanA:  trig_db=&curr.dbA;  break;
  334. case TS_ChanB:  trig_db=&curr.dbB;  break;
  335. default:
  336. /* trig_db stays NULL esp. for TS_None. */
  337. bufi=buflen;
  338. break;
  339. }
  340. if(trig_db && trig_state==TS_WaitForTrigger0)
  341. {
  342. bufi=_TrigScanSignal(bufi,trig_db,-1);
  343. if(bufi<buflen)
  344. {  trig_state=TS_WaitForTrigger1;  }
  345. }
  346. /*no else*/if(trig_db && trig_state==TS_WaitForTrigger1)
  347. {
  348. // Scan to see if we can get triggered finally :)
  349. bufi=_TrigScanSignal(bufi,trig_db,+1);
  350. if(bufi<buflen)
  351. {  do_trig_now=1;  }
  352. }
  353. }
  354. //write(1,"E",1);
  355. // See if we're triggered now. 
  356. if(do_trig_now)
  357. {
  358. assert(trig_state!=TS_Arming);
  359. // Since we want to trigger at bufi, trim the sample buffer 
  360. // in a way that exactly trig_h_pos-many samples are left of 
  361. // bufi, or more exactly, that the bufi is the trig_h_pos-th 
  362. // sample. 
  363. if(A.cmode!=CM_Off)
  364. {  A.sb.TrimTriggered(tp.trig_h_pos,bufi);  }
  365. if(B.cmode!=CM_Off)
  366. {  B.sb.TrimTriggered(tp.trig_h_pos,bufi);  }
  367. // Go into triggered state. Trigger at bufi. 
  368. trig_state=TS_Triggered;  _SendTrigStateEvent();
  369. fprintf(stderr,"Trigger: triggered (st=%u,%u)n",
  370. A.sb.SkipTail(),B.sb.SkipTail());
  371. }
  372. //write(1,"F",1);
  373. if(trig_state==TS_Triggered)
  374. {
  375. // Just check when we're done. 
  376. ssize_t rvA=-1,rvB=-1;
  377. if(A.cmode!=CM_Off)
  378. {  rvA=A.sb.CutToLength(tp.nsamples);  assert(rvA!=-2);  }
  379. if(B.cmode!=CM_Off)
  380. {  rvB=B.sb.CutToLength(tp.nsamples);  assert(rvB!=-2);  }
  381. if(rvA>=0 || rvB>=0)
  382. {
  383. assert(A.cmode==CM_Off || rvA>=0);
  384. assert(B.cmode==CM_Off || rvB>=0);
  385. if(A.cmode!=CM_Off && B.cmode!=CM_Off) assert(rvA==rvB);
  386. if(A.cmode!=CM_Off)  bufi=rvA;
  387. else if(B.cmode!=CM_Off)  bufi=rvB;
  388. else assert(0);  // <-- bufi=buflen;
  389. // So, we're now done. 
  390. _SamplingDone();
  391. trig_state=TS_HoldOff;  _SendTrigStateEvent();
  392. }
  393. else
  394. {  bufi=buflen;  }
  395. }
  396. //write(1,"G",1);
  397. if(trig_state==TS_HoldOff)
  398. {
  399. // FIXME: Should process holdoff. 
  400. bufi=buflen;
  401. // For single trigger, we stay in HoldOff until reset. 
  402. if(tp.trig_mode!=TM_Single)
  403. {
  404. // When holdoff done: 
  405. trig_state=TS_WaitACK;  _SendTrigStateEvent();
  406. _ResetRestartTrigger();  // Will start with TS_Arming. 
  407. }
  408. else
  409. {  bufi=buflen;  }
  410. }
  411. //write(1,"x",1);
  412. }
  413. }
  414. void TriggerThread::run()
  415. {
  416. fprintf(stderr,"Trigger thread runningn");
  417. USBIOThread::usb_thread->RegisterStreamHandler(this);
  418. thread_running.post();
  419. _RunTriggerThread();
  420. USBIOThread::usb_thread->UnregisterStreamHandler(this);
  421. fprintf(stderr,"Trigger thread exitingn");
  422. }
  423. void TriggerThread::_CopyNewParam()
  424. {
  425. param_mutex.lock();
  426. tp=new_param;  // C++-assignment. 
  427. fprintf(stderr,"Trigger: copying new params. ns=%un",tp.nsamples);
  428. A.cmode=new_param.cmodeA;
  429. B.cmode=new_param.cmodeB;
  430. param_mutex.unlock();
  431. }
  432. void TriggerThread::QuitThread()
  433. {
  434. mutex.lock();
  435. do_quit=1;
  436. mutex.unlock();
  437. wcond.wakeOne();
  438. }
  439. void TriggerThread::TriggerNow()
  440. {
  441. mutex.lock();
  442. trigger_now=1;
  443. mutex.unlock();
  444. wcond.wakeOne();
  445. }
  446. void TriggerThread::TriggerReset()
  447. {
  448. mutex.lock();
  449. do_restart=1;  // Well, this may not be prefect...
  450. mutex.unlock();
  451. wcond.wakeOne();
  452. }
  453. void TriggerThread::GUIDataAck()
  454. {
  455. mutex.lock();
  456. seen_gui_ack=1;
  457. mutex.unlock();
  458. wcond.wakeOne();
  459. }
  460. void TriggerThread::_SignalTrigReStart()
  461. {
  462. mutex.lock();
  463. do_restart=1;
  464. mutex.unlock();
  465. wcond.wakeOne();
  466. }
  467. void TriggerThread::SetTriggerHPosAndSamples(size_t h_pos,size_t nsamples)
  468. {
  469. param_mutex.lock();
  470. new_param.trig_h_pos=h_pos;
  471. new_param.nsamples=nsamples;
  472. assert(h_pos<nsamples);
  473. param_mutex.unlock();
  474. _SignalTrigReStart();
  475. }
  476. void TriggerThread::SetTriggerLevelAndSource(int level,TriggerSource tsrc)
  477. {
  478. param_mutex.lock();
  479. new_param.trig_level=level;
  480. new_param.trig_src=tsrc;
  481. param_mutex.unlock();
  482. _SignalTrigReStart();
  483. }
  484. void TriggerThread::SetTriggerModeAndSlope(TriggerMode tmode,int slope)
  485. {
  486. param_mutex.lock();
  487. new_param.trig_mode=tmode;
  488. new_param.trig_slope=slope;
  489. param_mutex.unlock();
  490. _SignalTrigReStart();
  491. }
  492. void TriggerThread::SetChannelMode(int chan_num,ChannelMode cmode)
  493. {
  494. //Channel *c=chan_num==1 ? &A : &B;
  495. param_mutex.lock();
  496.      if(chan_num==1) new_param.cmodeA=cmode;
  497. else if(chan_num==2) new_param.cmodeB=cmode;
  498. else assert(0);
  499. param_mutex.unlock();
  500. _SignalTrigReStart();
  501. }
  502. void TriggerThread::SetHorizDownsampling(int fact,bool average)
  503. {
  504. param_mutex.lock();
  505. new_param.downsampling_fact=fact;
  506. new_param.downsampling_avg=average;
  507. param_mutex.unlock();
  508. _SignalTrigReStart();
  509. }
  510. void TriggerThread::FeedBuffer(const DataBuffer &A,const DataBuffer &B)
  511. {
  512. // This function is executed in USBIOThread context and must be as fast 
  513. // as possible. We just enqueue the buffers; NO PROCESSING WHATSOEVER. 
  514. size_t tlen=A.length()+B.length();
  515. assert(A.length()==B.length());
  516. mutex.lock();
  517. if(stream_tot_size+tlen<stream_buf_max_size)
  518. {
  519. bq.PushHead(StreamBuffer(A,B));
  520. stream_tot_size+=tlen;
  521. //write(1,"+",1);
  522. // There is only ONE thread waiting by construction so we don't 
  523. // have to wake ALL. 
  524. wcond.wakeOne();
  525. }
  526. else
  527. {
  528. ++stream_dropped_buffers;
  529. fprintf(stderr,"OOPS: Dropping buffers.n");
  530. }
  531. mutex.unlock();
  532. }
  533. TriggerThread::TriggerThread(QObject *_data_receiver) : 
  534. QThread(),
  535. USBIOThread::StreamHandler(),
  536. mutex(),
  537. wcond(),
  538. bq(32,16),
  539. A(),B(),
  540. tp(),
  541. new_param(),
  542. param_mutex()
  543. {
  544. mutex.lock();
  545. data_receiver=_data_receiver;
  546. stream_tot_size=0;
  547. stream_dropped_buffers=0;
  548. stream_buf_max_size=16*1024*1024;
  549. do_quit=0;
  550. trigger_now=0;
  551. seen_gui_ack=0;
  552. do_restart=0;
  553. mutex.unlock();
  554. trig_state=TS_Arming;
  555. }
  556. TriggerThread::~TriggerThread()
  557. {
  558. data_receiver=NULL;
  559. }
  560. //------------------------------------------------------------------------------
  561. TriggerThread::Channel::Channel() : 
  562. sb()
  563. {
  564. cmode=CM_Off;
  565. avg_nsamp=0;
  566. avg_accu=0;
  567. }
  568. TriggerThread::TrigParam::TrigParam()
  569. {
  570. trig_h_pos=0;
  571. nsamples=1000;
  572. trig_level=0;
  573. trig_src=TS_None;
  574. trig_mode=TM_Norm;
  575. trig_slope=+1;
  576. downsampling_fact=1;
  577. downsampling_avg=0;
  578. }
  579. TriggerThread::TrigParamExt::TrigParamExt() : 
  580. TrigParam()
  581. {
  582. cmodeA=CM_Off;
  583. cmodeB=CM_Off;
  584. }