API
modalPSDs.hpp
Go to the documentation of this file.
1 /** \file modalPSDs.hpp
2  * \brief The MagAO-X modalPSDs app header file
3  *
4  * \ingroup modalPSDs_files
5  */
6 
7 #ifndef modalPSDs_hpp
8 #define modalPSDs_hpp
9 
10 
11 #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
12 #include "../../magaox_git_version.h"
13 
14 #include <mx/sigproc/circularBuffer.hpp>
15 #include <mx/sigproc/signalWindows.hpp>
16 
17 #include <mx/math/fft/fftwEnvironment.hpp>
18 #include <mx/math/fft/fft.hpp>
19 
20 /** \defgroup modalPSDs
21  * \brief An application to calculate rolling PSDs of modal amplitudes
22  *
23  * <a href="../handbook/operating/software/apps/modalPSDs.html">Application Documentation</a>
24  *
25  * \ingroup apps
26  *
27  */
28 
29 /** \defgroup modalPSDs_files
30  * \ingroup modalPSDs
31  */
32 
33 namespace MagAOX
34 {
35 namespace app
36 {
37 
38 /// Class for application to calculate rolling PSDs of modal amplitudes.
39 /**
40  * \ingroup modalPSDs
41  */
42 class modalPSDs : public MagAOXApp<true>, public dev::shmimMonitor<modalPSDs>
43 {
44  friend class dev::shmimMonitor<modalPSDs>;
45 
46 public:
47 
48  typedef float realT;
49  typedef std::complex<realT> complexT;
50 
51  /// The base shmimMonitor type
53 
54  /// The amplitude circular buffer type
55  typedef mx::sigproc::circularBufferIndex<float *, unsigned> ampCircBuffT;
56 
57 protected:
58 
59  /** \name Configurable Parameters
60  *@{
61  */
62 
63  std::string m_fpsSource; ///< Device name for getting fps to set circular buffer length. This device should have *.fps.current.
64 
65  realT m_psdTime {1}; ///< The length of time over which to calculate PSDs. The default is 1 sec.
66  realT m_psdAvgTime {10}; ///< The time over which to average PSDs. The default is 10 sec.
67 
68  //realT m_overSize {10}; ///< Multiplicative factor by which to oversize the circular buffer, to give good mean estimates and account for time-to-calculate.
69 
70  realT m_psdOverlapFraction {0.5}; ///< The fraction of the sample time to overlap by.
71 
72  int m_nPSDHistory {100}; //
73 
74 
75  ///@}
76 
77  int m_nModes; ///< the number of modes to calculate PSDs for.
78 
80 
81  //std::vector<ampCircBuffT> m_ampCircBuffs;
82 
83  realT m_fps {0};
84  realT m_df {1};
85 
86  //unsigned m_tsCircBuffLength {4000}; ///< Length of the time-series circular buffers. This is updated by m_fpsSource and m_psdTime.
87 
88  unsigned m_tsSize {2000}; ///< The length of the time series sample over which the PSD is calculated
89  unsigned m_tsOverlapSize {1000}; ///< The number of samples in the overlap
90 
91  std::vector<realT> m_win; ///< The window function. By default this is Hann.
92 
93  realT * m_tsWork {nullptr};
94  size_t m_tsWorkSize {0};
95 
96  std::complex<realT> * m_fftWork {nullptr};
97  size_t m_fftWorkSize {0};
98 
99  std::vector<realT> m_psd;
100 
101  mx::math::fft::fftT< realT, std::complex<realT>, 1, 0> m_fft;
102  mx::math::fft::fftwEnvironment<realT> m_fftEnv;
103 
104  /** \name PSD Calculation Thread
105  * Handling of offloads from the average woofer shape
106  * @{
107  */
108  int m_psdThreadPrio {0}; ///< Priority of the PSD Calculation thread.
109  std::string m_psdThreadCpuset; ///< The cpuset to use for the PSD Calculation thread.
110 
111  std::thread m_psdThread; ///< The PSD Calculation thread.
112 
113  bool m_psdThreadInit {true}; ///< Initialization flag for the PSD Calculation thread.
114 
115  bool m_psdRestarting {true}; ///< Synchronization flag. This will only become false after a successful call to allocate.
116  bool m_psdWaiting {false}; ///< Synchronization flag. This is set to true when the PSD thread is safely waiting for allocation to complete.
117 
118  pid_t m_psdThreadID {0}; ///< PSD Calculation thread PID.
119 
120  pcf::IndiProperty m_psdThreadProp; ///< The property to hold the PSD Calculation thread details.
121 
122  /// PS Calculation thread starter function
123  static void psdThreadStart( modalPSDs * p /**< [in] pointer to this */);
124 
125  /// PSD Calculation thread function
126  /** Runs until m_shutdown is true.
127  */
128  void psdThreadExec();
129 
130  IMAGE * m_freqStream {nullptr}; ///< The ImageStreamIO shared memory buffer to hold the frequency scale
131 
132  mx::improc::eigenImage<realT> m_psdBuffer;
133 
134  IMAGE * m_rawpsdStream {nullptr};
135 
136  IMAGE * m_avgpsdStream {nullptr}; ///< The ImageStreamIO shared memory buffer to hold the average psds
137 
138 public:
139  /// Default c'tor.
140  modalPSDs();
141 
142  /// D'tor, declared and defined for noexcept.
143  ~modalPSDs() noexcept
144  {}
145 
146  virtual void setupConfig();
147 
148  /// Implementation of loadConfig logic, separated for testing.
149  /** This is called by loadConfig().
150  */
151  int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
152 
153  virtual void loadConfig();
154 
155  /// Startup function
156  /**
157  *
158  */
159  virtual int appStartup();
160 
161  /// Implementation of the FSM for modalPSDs.
162  /**
163  * \returns 0 on no critical error
164  * \returns -1 on an error requiring shutdown
165  */
166  virtual int appLogic();
167 
168  /// Shutdown the app.
169  /**
170  *
171  */
172  virtual int appShutdown();
173 
174  //shmimMonitor Interface
175 protected:
176 
177  int allocate( const dev::shmimT & dummy /**< [in] tag to differentiate shmimMonitor parents.*/);
178 
179  int allocatePSDStreams();
180 
181  int processImage( void * curr_src, ///< [in] pointer to start of current frame.
182  const dev::shmimT & dummy ///< [in] tag to differentiate shmimMonitor parents.
183  );
184 
185 
186  //INDI Interface
187 protected:
188 
189  pcf::IndiProperty m_indiP_psdTime;
191 
192  pcf::IndiProperty m_indiP_psdAvgTime;
194 
195  pcf::IndiProperty m_indiP_overSize;
197 
198  pcf::IndiProperty m_indiP_fpsSource;
200 
201  pcf::IndiProperty m_indiP_fps;
202 
203 };
204 
205 modalPSDs::modalPSDs() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
206 {
207 
208  return;
209 }
210 
212 {
214 
215  config.add("circBuff.fpsSource", "", "circBuff.fpsSource", argType::Required, "circBuff", "fpsSource", false, "string", "Device name for getting fps to set circular buffer length. This device should have *.fps.current.");
216  config.add("circBuff.defaultFPS", "", "circBuff.defaultFPS", argType::Required, "circBuff", "defaultFPS", false, "realT", "Default FPS at startup, will enable changing average length with psdTime before INDI available.");
217  config.add("circBuff.psdTime", "", "circBuff.psdTime", argType::Required, "circBuff", "psdTime", false, "realT", "The length of time over which to calculate PSDs. The default is 1 sec.");
218 }
219 
220 int modalPSDs::loadConfigImpl( mx::app::appConfigurator & _config )
221 {
222  shmimMonitorT::loadConfig(_config);
223 
224  _config(m_fpsSource, "circBuff.fpsSource");
225  _config(m_fps, "circBuff.defaultFPS");
226  _config(m_psdTime, "circBuff.psdTime");
227 
228  return 0;
229 }
230 
232 {
233  loadConfigImpl(config);
234 }
235 
237 {
238  createStandardIndiNumber<unsigned>( m_indiP_psdTime, "psdTime", 0, 60, 0.1, "%0.1f");
239  m_indiP_psdTime["current"].set(m_psdTime);
240  m_indiP_psdTime["target"].set(m_psdTime);
241 
243  {
244  log<software_error>({__FILE__,__LINE__});
245  return -1;
246  }
247 
248  createStandardIndiNumber<unsigned>( m_indiP_psdTime, "psdAvgTime", 0, 60, 0.1, "%0.1f");
249  m_indiP_psdTime["current"].set(m_psdAvgTime);
250  m_indiP_psdTime["target"].set(m_psdAvgTime);
251 
253  {
254  log<software_error>({__FILE__,__LINE__});
255  return -1;
256  }
257 
258  if(m_fpsSource != "")
259  {
260  REG_INDI_SETPROP(m_indiP_fpsSource, m_fpsSource, std::string("fps"));
261  }
262 
263  createROIndiNumber( m_indiP_fps, "fps", "current", "Circular Buffer");
264  m_indiP_fps.add(pcf::IndiElement("current"));
265  m_indiP_fps["current"] = m_fps;
266 
268  {
269  log<software_error>({__FILE__,__LINE__});
270  return -1;
271  }
272 
273  if(shmimMonitorT::appStartup() < 0)
274  {
275  return log<software_error,-1>({__FILE__, __LINE__});
276  }
277 
279  {
280  log<software_error>({__FILE__, __LINE__});
281  return -1;
282  }
283 
285 
286  return 0;
287 }
288 
290 {
291  if( shmimMonitorT::appLogic() < 0)
292  {
293  return log<software_error,-1>({__FILE__,__LINE__});
294  }
295 
296  std::unique_lock<std::mutex> lock(m_indiMutex);
297 
298  if(shmimMonitorT::updateINDI() < 0)
299  {
300  log<software_error>({__FILE__, __LINE__});
301  }
302 
303  return 0;
304 }
305 
307 {
309 
310  if(m_psdThread.joinable())
311  {
312  pthread_kill(m_psdThread.native_handle(), SIGUSR1);
313  try
314  {
315  m_psdThread.join(); //this will throw if it was already joined
316  }
317  catch(...)
318  {
319  }
320  }
321 
322  return 0;
323 }
324 
325 int modalPSDs::allocate( const dev::shmimT & dummy)
326 {
327  static_cast<void>(dummy);
328 
329  m_psdRestarting = true;
330 
331  //Prevent reallocation while the psd thread might be calculating
332  while(m_psdWaiting == false && !shutdown()) mx::sys::microSleep(100);
333 
334  if(shutdown()) return 0; //If shutdown() is true then shmimMonitor will cleanup
335 
336  if( m_fps > 0)
337  {
338  //m_tsCircBuffLength = m_fps * m_psdTime * m_overSize;
341  }
342 
343  if(m_tsOverlapSize <= 0 || !std::isnormal(m_tsOverlapSize))
344  {
345  log<software_error>({__FILE__,__LINE__, "bad m_tsOverlapSize value: " + std::to_string(m_tsOverlapSize)});
346  return -1;
347  }
348 
349  //Check for unsupported type (must be realT)
351  {
352  //must be a vector of size 1 on one axis
353  log<software_error>({__FILE__,__LINE__, "unsupported data type: must be realT"});
354  return -1;
355  }
356 
357  //Check for unexpected format
359  {
360  //must be a vector of size 1 on one axis
361  log<software_error>({__FILE__,__LINE__, "unexpected shmim format"});
362  return -1;
363  }
364 
365  std::cerr << "connected to " << shmimMonitorT::m_shmimName << " " << shmimMonitorT::m_width << " " << shmimMonitorT::m_height << " " << shmimMonitorT::m_depth << "\n";
366 
367 
369 
370  //Size the circ buff
372 
373  //Create the window
374  m_win.resize(m_tsSize);
375  mx::sigproc::window::hann(m_win);
376 
377  //Set up the FFT and working memory
378  m_fft.plan(m_tsSize, MXFFT_FORWARD, false);
379 
380  if(m_tsWork) fftw_free(m_tsWork);
381  m_tsWork = mx::math::fft::fftw_malloc<realT>( m_tsSize );
382 
383  if(m_fftWork) fftw_free(m_fftWork);
384  m_fftWork = mx::math::fft::fftw_malloc<std::complex<realT>>( (m_tsSize/2 + 1) );
385 
386  m_psd.resize(m_tsSize/2 + 1);
387 
388  if(m_fps > 0)
389  {
390  m_df = 1.0/(m_tsSize/m_fps);
391  }
392  else
393  {
394  m_df = 1.0/(m_tsSize);
395  }
396 
397 
398  //Create the shared memory images
399  uint32_t imsize[3];
400 
401  //First the frequency
402  imsize[0] = 1;
403  imsize[1] = m_psd.size();
404  imsize[2] = 1;
405 
406  if(m_freqStream)
407  {
408  ImageStreamIO_destroyIm(m_freqStream);
409  free(m_freqStream);
410  }
411  m_freqStream = (IMAGE *) malloc(sizeof(IMAGE));
412 
413  ImageStreamIO_createIm_gpu(m_freqStream, (m_configName + "_freq").c_str(), 3, imsize, IMAGESTRUCT_FLOAT, -1, 1, IMAGE_NB_SEMAPHORE, 0, CIRCULAR_BUFFER | ZAXIS_TEMPORAL, 0);
414  m_freqStream->md->write=1;
415  for(size_t n = 0; n < m_psd.size(); ++n)
416  {
417  m_freqStream->array.F[n] = n * m_df;
418  }
419 
420  //Set the time of last write
421  clock_gettime(CLOCK_REALTIME, &m_freqStream->md->writetime);
422  m_freqStream->md->atime = m_freqStream->md->writetime;
423 
424  //Update cnt1
425  m_freqStream->md->cnt1 = 0;
426 
427  //Update cnt0
428  m_freqStream->md->cnt0 = 0;
429 
430  m_freqStream->md->write=0;
431  ImageStreamIO_sempost(m_freqStream,-1);
432 
433 
435 
436  m_psdRestarting = false;
437 
438 
439 
440  return 0;
441 }
442 
444 {
445  if(m_rawpsdStream)
446  {
447  ImageStreamIO_destroyIm( m_rawpsdStream );
448  free(m_rawpsdStream);
449  }
450 
451  uint32_t imsize[3];
452  imsize[0] = m_psd.size();
453  imsize[1] = m_nModes;
454  imsize[2] = m_nPSDHistory;
455 
456  m_rawpsdStream = (IMAGE *) malloc(sizeof(IMAGE));
457  ImageStreamIO_createIm_gpu(m_rawpsdStream, (m_configName + "_rawpsds").c_str(), 3, imsize, IMAGESTRUCT_FLOAT, -1, 1, IMAGE_NB_SEMAPHORE, 0, CIRCULAR_BUFFER | ZAXIS_TEMPORAL, 0);
458 
459  if(m_avgpsdStream)
460  {
461  ImageStreamIO_destroyIm( m_avgpsdStream );
462  free(m_avgpsdStream);
463  }
464 
465  imsize[0] = m_psd.size();
466  imsize[1] = m_nModes;
467  imsize[2] = 1;
468 
469  m_avgpsdStream = (IMAGE *) malloc(sizeof(IMAGE));
470  ImageStreamIO_createIm_gpu(m_avgpsdStream, (m_configName + "_psds").c_str(), 3, imsize, IMAGESTRUCT_FLOAT, -1, 1, IMAGE_NB_SEMAPHORE, 0, CIRCULAR_BUFFER | ZAXIS_TEMPORAL, 0);
471 
472  m_psdBuffer.resize(m_psd.size(), m_nModes);
473 
474  return 0;
475 }
476 
477 int modalPSDs::processImage( void * curr_src,
478  const dev::shmimT & dummy
479  )
480 {
481  static_cast<void>(dummy);
482 
483  float * f_src = static_cast<float *>(curr_src);
484 
485  m_ampCircBuff.nextEntry(f_src);
486 
487  return 0;
488 }
489 
491 {
492  p->psdThreadExec();
493 }
494 
495 
497 {
498  m_psdThreadID = syscall(SYS_gettid);
499 
500  while( m_psdThreadInit == true && shutdown() == 0)
501  {
502  sleep(1);
503  }
504 
505  while(shutdown() == 0)
506  {
507  if(m_psdRestarting == true || m_ampCircBuff.maxEntries() == 0) m_psdWaiting = true;
508 
509  while((m_psdRestarting == true || m_ampCircBuff.maxEntries() == 0) && !shutdown()) mx::sys::microSleep(100);
510 
511  if(shutdown()) break;
512 
513  m_psdWaiting = false;
514 
515  if(m_ampCircBuff.maxEntries() == 0)
516  {
517  log<software_error>({__FILE__, __LINE__, "amp circ buff has zero size"});
518  return;
519  }
520 
521  std::cerr << "waiting to grow\n";
522  while( m_ampCircBuff.size() < m_ampCircBuff.maxEntries() && m_psdRestarting == false && !shutdown())
523  {
524  //shrinking sleep
525  double stime = (1.0*m_ampCircBuff.maxEntries() - 1.0*m_ampCircBuff.size())/m_fps * 0.5*1e9;
526  mx::sys::nanoSleep(stime);
527  }
528 
529  std::cerr << "all grown. starting to calculate\n";
530 
531  ampCircBuffT::indexT ne0;
532  ampCircBuffT::indexT ne1 = m_ampCircBuff.latest();
533  if(ne1 > m_tsOverlapSize) ne1 -= m_tsSize;
534  else ne1 = m_ampCircBuff.size() + ne1 - m_tsSize;
535 
536  while(m_psdRestarting == false && !shutdown())
537  {
538  //Used to check if we are getting too behind
539  uint64_t mono0 = m_ampCircBuff.mono();
540 
541  //Calc PSDs here
542  ne0 = ne1;
543 
544  std::cerr << "calculating: " << ne0 << " " << m_ampCircBuff.size() << " " << m_tsSize << "\n";
545  double t0 = mx::sys::get_curr_time();
546 
547  for(size_t m = 0; m < shmimMonitorT::m_width*shmimMonitorT::m_height; ++m) //Loop over each mode
548  {
549  //get mean going over entire TS
550  float mn = 0;
551  for(size_t n =0; n < m_ampCircBuff.size(); ++n)
552  {
553  realT mn = 0;
554  mn += m_ampCircBuff[n][m];
555  }
556  mn /= m_ampCircBuff.size();
557 
558  double var = 0;
559  for(size_t n = 0; n < m_tsSize; ++n)
560  {
561  m_tsWork[n] = (m_ampCircBuff.at(ne0,n)[m]-mn);
562  var += pow(m_tsWork[n],2);
563 
564  m_tsWork[n] *= m_win[n];
565  }
566  var /= m_tsSize;
567 
569 
570  double nm = 0;
571  for(size_t n=0; n < m_psd.size(); ++n)
572  {
573  m_psd[n] = norm(m_fftWork[n]);
574  nm += m_psd[n] * m_df;
575  }
576 
577  for(size_t n=0; n < m_psd.size(); ++n)
578  {
579  m_psd[n] *= (var/nm);
580  }
581 
582  //Put it in the buffer for uploading to shmim
583  for(size_t n=0; n < m_psd.size(); ++n) m_psdBuffer(n,m) = m_psd[n];
584 
585  }
586 
587  //------------------------- the raw psds ---------------------------
588  m_rawpsdStream->md->write=1;
589 
590  //Set the time of last write
591  clock_gettime(CLOCK_REALTIME, &m_rawpsdStream->md->writetime);
592  m_rawpsdStream->md->atime = m_rawpsdStream->md->writetime;
593 
594  uint64_t cnt1 = m_rawpsdStream->md->cnt1 + 1;
595  if(cnt1 >= m_rawpsdStream->md->size[2]) cnt1 = 0;
596 
597  //Move to next pointer
598  float * F = m_rawpsdStream->array.F + m_psdBuffer.rows()*m_psdBuffer.cols()*cnt1;
599 
600  memcpy(F, m_psdBuffer.data(), m_psdBuffer.rows()*m_psdBuffer.cols()*sizeof(float));
601 
602  //Update cnt1
603  m_rawpsdStream->md->cnt1 = cnt1;
604 
605  //Update cnt0
606  ++m_rawpsdStream->md->cnt0;
607 
608  m_rawpsdStream->md->write=0;
609  ImageStreamIO_sempost(m_rawpsdStream,-1);
610 
611  //-------------------------- now average the psds ----------------------------
612 
613  int nPSDAverage = (m_psdAvgTime/m_psdTime) / m_psdOverlapFraction;
614 
615  if(nPSDAverage <= 0) nPSDAverage = 1;
616  else if((uint64_t) nPSDAverage > m_rawpsdStream->md->size[2]) nPSDAverage = m_rawpsdStream->md->size[2];
617 
618  //Move to next pointer
619  F = m_rawpsdStream->array.F + m_psdBuffer.rows()*m_psdBuffer.cols()*cnt1;
620 
621  memcpy(m_psdBuffer.data(), F, m_psdBuffer.rows()*m_psdBuffer.cols()*sizeof(float));
622 
623  for(int n =1; n < nPSDAverage; ++n)
624  {
625  if(cnt1 == 0) cnt1 = m_rawpsdStream->md->size[2] - 1;
626  else --cnt1;
627 
628  F = m_rawpsdStream->array.F + m_psdBuffer.rows()*m_psdBuffer.cols()*cnt1;
629 
630  m_psdBuffer += Eigen::Map<Eigen::Array<float,-1,-1>>(F, m_psdBuffer.rows(), m_psdBuffer.cols());
631  }
632 
633  m_psdBuffer /= nPSDAverage;
634 
635  m_avgpsdStream->md->write=1;
636 
637  //Set the time of last write
638  clock_gettime(CLOCK_REALTIME, &m_avgpsdStream->md->writetime);
639  m_avgpsdStream->md->atime = m_avgpsdStream->md->writetime;
640 
641  //Move to next pointer
642  F = m_avgpsdStream->array.F;
643 
644  memcpy(F, m_psdBuffer.data(), m_psdBuffer.rows()*m_psdBuffer.cols()*sizeof(float));
645 
646  //Update cnt1
647  m_avgpsdStream->md->cnt1 = 0;
648 
649  //Update cnt0
650  ++m_avgpsdStream->md->cnt0;
651 
652  m_avgpsdStream->md->write=0;
653  ImageStreamIO_sempost(m_avgpsdStream,-1);
654 
655  double t1 = mx::sys::get_curr_time();
656  std::cerr << "done " << t1-t0 << "\n";
657 
658  //Have to be cycling within the overlap
659  if(m_ampCircBuff.mono() - mono0 >= m_tsOverlapSize)
660  {
661  log<text_log>("PSD calculations getting behind, skipping ahead.", logPrio::LOG_WARNING);
662  ne0 = m_ampCircBuff.latest();
663  if(ne0 > m_tsOverlapSize) ne0 -= m_tsOverlapSize;
664  else ne0 = m_ampCircBuff.size() + ne0 - m_tsOverlapSize;
665  }
666 
667  //Now wait until we get to next one
668  ne1 = ne0 + m_tsOverlapSize;
669  if( ne1 >= m_ampCircBuff.size())
670  {
671  ne1 -= m_ampCircBuff.size();
672  }
673 
674  ampCircBuffT::indexT ce = m_ampCircBuff.latest();
675  //wrapped difference
676  long dn;
677  if( ce >= ne1 ) dn = ce - ne1;
678  else dn = ce + (m_ampCircBuff.size() - ne1);
679 
680  while(dn < m_tsOverlapSize && !shutdown() && m_psdRestarting == false)
681  {
682  double stime = (1.0*dn)/m_fps * 0.5 * 1e9;
683  mx::sys::nanoSleep(stime);
684 
685  ce = m_ampCircBuff.latest();
686 
687  if( ce >= ne1 ) dn = ce - ne1;
688  else dn = ce + (m_ampCircBuff.size() - ne1);
689  }
690  }
691  }
692 }
693 
694 INDI_NEWCALLBACK_DEFN(modalPSDs, m_indiP_psdTime)(const pcf::IndiProperty &ipRecv)
695 {
696  if(ipRecv.getName() != m_indiP_psdTime.getName())
697  {
698  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
699  return -1;
700  }
701 
702  realT target;
703 
704  if( indiTargetUpdate( m_indiP_psdTime, target, ipRecv, true) < 0)
705  {
706  log<software_error>({__FILE__,__LINE__});
707  return -1;
708  }
709 
710  if(m_psdTime != target)
711  {
712  std::lock_guard<std::mutex> guard(m_indiMutex);
713 
714  m_psdTime = target;
715 
716  updateIfChanged(m_indiP_psdTime, "current", m_psdTime, INDI_IDLE);
717  updateIfChanged(m_indiP_psdTime, "target", m_psdTime, INDI_IDLE);
718 
719  shmimMonitorT::m_restart = true;
720 
721  log<text_log>("set psdTime to " + std::to_string(m_psdTime), logPrio::LOG_NOTICE);
722  }
723 
724  return 0;
725 } //INDI_NEWCALLBACK_DEFN(modalPSDs, m_indiP_psdTime)
726 
727 
728 INDI_NEWCALLBACK_DEFN(modalPSDs, m_indiP_psdAvgTime)(const pcf::IndiProperty &ipRecv)
729 {
730  if(ipRecv.getName() != m_indiP_psdAvgTime.getName())
731  {
732  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
733  return -1;
734  }
735 
736  realT target;
737 
738  if( indiTargetUpdate( m_indiP_psdAvgTime, target, ipRecv, true) < 0)
739  {
740  log<software_error>({__FILE__,__LINE__});
741  return -1;
742  }
743 
744  if(m_psdAvgTime != target)
745  {
746  std::lock_guard<std::mutex> guard(m_indiMutex);
747 
748  m_psdAvgTime = target;
749 
750  updateIfChanged(m_indiP_psdTime, "current", m_psdAvgTime, INDI_IDLE);
751  updateIfChanged(m_indiP_psdTime, "target", m_psdAvgTime, INDI_IDLE);
752 
753  log<text_log>("set psdAvgTime to " + std::to_string(m_psdAvgTime), logPrio::LOG_NOTICE);
754  }
755 
756  return 0;
757 } //INDI_NEWCALLBACK_DEFN(modalPSDs, m_indiP_psdTime)
758 
759 INDI_SETCALLBACK_DEFN(modalPSDs, m_indiP_fpsSource )(const pcf::IndiProperty &ipRecv)
760 {
761  if( ipRecv.getName() != m_indiP_fpsSource.getName())
762  {
763  log<software_error>({__FILE__, __LINE__, "Invalid INDI property."});
764  return -1;
765  }
766 
767  if( ipRecv.find("current") != true ) //this isn't valid
768  {
769  log<software_error>({__FILE__, __LINE__, "No current property in fps source."});
770  return 0;
771  }
772 
773  std::lock_guard<std::mutex> guard(m_indiMutex);
774 
775  realT fps = ipRecv["current"].get<realT>();
776 
777  if(fps != m_fps)
778  {
779  m_fps = fps;
780  log<text_log>("set fps to " + std::to_string(m_fps), logPrio::LOG_NOTICE);
781  updateIfChanged(m_indiP_fps, "current", m_fps, INDI_IDLE);
782  shmimMonitorT::m_restart = true;
783  }
784 
785  return 0;
786 
787 } //INDI_SETCALLBACK_DEFN(modalPSDs, m_indiP_fpsSource)
788 
789 } //namespace app
790 } //namespace MagAOX
791 
792 #endif //modalPSDs_hpp
#define IMAGESTRUCT_FLOAT
Definition: ImageStruct.hpp:22
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:75
std::string m_configName
The name of the configuration file (minus .conf).
Definition: MagAOXApp.hpp:88
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2082
int registerIndiPropertyNew(pcf::IndiProperty &prop, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for.
int shutdown()
Get the value of the shutdown flag.
Definition: MagAOXApp.hpp:1146
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
Definition: MagAOXApp.hpp:1590
int createROIndiNumber(pcf::IndiProperty &prop, const std::string &propName, const std::string &propLabel="", const std::string &propGroup="")
Create a ReadOnly INDI Number property.
Definition: MagAOXApp.hpp:2294
int registerIndiPropertyReadOnly(pcf::IndiProperty &prop)
Register an INDI property which is read only.
Definition: MagAOXApp.hpp:2437
std::mutex m_indiMutex
Mutex for locking INDI communications.
Definition: MagAOXApp.hpp:540
int threadStart(std::thread &thrd, bool &thrdInit, pid_t &tpid, pcf::IndiProperty &thProp, int thrdPrio, const std::string &cpuset, const std::string &thrdName, thisPtr *thrdThis, Function &&thrdStart)
Start a thread, using this class's privileges to set priority, etc.
Definition: MagAOXApp.hpp:1950
uint32_t m_depth
The depth of the circular buffer in the stream.
uint32_t m_width
The width of the images in the stream.
int updateINDI()
Update the INDI properties for this device controller.
int appLogic()
Checks the shmimMonitor thread.
uint32_t m_height
The height of the images in the stream.
std::string m_shmimName
The name of the shared memory image, is used in /tmp/<shmimName>.im.shm. Derived classes should set a...
int appShutdown()
Shuts down the shmimMonitor thread.
uint8_t m_dataType
The ImageStreamIO type code.
void setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
void loadConfig(mx::app::appConfigurator &config)
load the configuration system results
Class for application to calculate rolling PSDs of modal amplitudes.
Definition: modalPSDs.hpp:43
realT m_psdOverlapFraction
The fraction of the sample time to overlap by.
Definition: modalPSDs.hpp:70
pcf::IndiProperty m_psdThreadProp
The property to hold the PSD Calculation thread details.
Definition: modalPSDs.hpp:120
pcf::IndiProperty m_indiP_psdAvgTime
Definition: modalPSDs.hpp:192
INDI_NEWCALLBACK_DECL(modalPSDs, m_indiP_overSize)
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
Definition: modalPSDs.hpp:220
pid_t m_psdThreadID
PSD Calculation thread PID.
Definition: modalPSDs.hpp:118
mx::improc::eigenImage< realT > m_psdBuffer
Definition: modalPSDs.hpp:132
std::thread m_psdThread
The PSD Calculation thread.
Definition: modalPSDs.hpp:111
pcf::IndiProperty m_indiP_overSize
Definition: modalPSDs.hpp:195
unsigned m_tsOverlapSize
The number of samples in the overlap.
Definition: modalPSDs.hpp:89
std::complex< realT > * m_fftWork
Definition: modalPSDs.hpp:96
mx::sigproc::circularBufferIndex< float *, unsigned > ampCircBuffT
The amplitude circular buffer type.
Definition: modalPSDs.hpp:55
mx::math::fft::fftwEnvironment< realT > m_fftEnv
Definition: modalPSDs.hpp:102
INDI_NEWCALLBACK_DECL(modalPSDs, m_indiP_psdTime)
static void psdThreadStart(modalPSDs *p)
PS Calculation thread starter function.
Definition: modalPSDs.hpp:490
std::vector< realT > m_win
The window function. By default this is Hann.
Definition: modalPSDs.hpp:91
virtual int appStartup()
Startup function.
Definition: modalPSDs.hpp:236
INDI_SETCALLBACK_DECL(modalPSDs, m_indiP_fpsSource)
INDI_NEWCALLBACK_DECL(modalPSDs, m_indiP_psdAvgTime)
IMAGE * m_avgpsdStream
The ImageStreamIO shared memory buffer to hold the average psds.
Definition: modalPSDs.hpp:136
modalPSDs()
Default c'tor.
Definition: modalPSDs.hpp:205
int m_nModes
the number of modes to calculate PSDs for.
Definition: modalPSDs.hpp:77
mx::math::fft::fftT< realT, std::complex< realT >, 1, 0 > m_fft
Definition: modalPSDs.hpp:101
bool m_psdRestarting
Synchronization flag. This will only become false after a successful call to allocate.
Definition: modalPSDs.hpp:115
virtual void loadConfig()
Definition: modalPSDs.hpp:231
pcf::IndiProperty m_indiP_fps
Definition: modalPSDs.hpp:201
virtual int appLogic()
Implementation of the FSM for modalPSDs.
Definition: modalPSDs.hpp:289
int processImage(void *curr_src, const dev::shmimT &dummy)
Definition: modalPSDs.hpp:477
IMAGE * m_freqStream
The ImageStreamIO shared memory buffer to hold the frequency scale.
Definition: modalPSDs.hpp:130
realT m_psdAvgTime
The time over which to average PSDs. The default is 10 sec.
Definition: modalPSDs.hpp:66
unsigned m_tsSize
The length of the time series sample over which the PSD is calculated.
Definition: modalPSDs.hpp:88
virtual void setupConfig()
Definition: modalPSDs.hpp:211
ampCircBuffT m_ampCircBuff
Definition: modalPSDs.hpp:79
int m_psdThreadPrio
Priority of the PSD Calculation thread.
Definition: modalPSDs.hpp:108
std::string m_psdThreadCpuset
The cpuset to use for the PSD Calculation thread.
Definition: modalPSDs.hpp:109
std::complex< realT > complexT
Definition: modalPSDs.hpp:49
int allocate(const dev::shmimT &dummy)
Definition: modalPSDs.hpp:325
bool m_psdWaiting
Synchronization flag. This is set to true when the PSD thread is safely waiting for allocation to com...
Definition: modalPSDs.hpp:116
pcf::IndiProperty m_indiP_psdTime
Definition: modalPSDs.hpp:189
pcf::IndiProperty m_indiP_fpsSource
Definition: modalPSDs.hpp:198
realT m_psdTime
The length of time over which to calculate PSDs. The default is 1 sec.
Definition: modalPSDs.hpp:65
void psdThreadExec()
PSD Calculation thread function.
Definition: modalPSDs.hpp:496
bool m_psdThreadInit
Initialization flag for the PSD Calculation thread.
Definition: modalPSDs.hpp:113
virtual int appShutdown()
Shutdown the app.
Definition: modalPSDs.hpp:306
~modalPSDs() noexcept
D'tor, declared and defined for noexcept.
Definition: modalPSDs.hpp:143
std::vector< realT > m_psd
Definition: modalPSDs.hpp:99
dev::shmimMonitor< modalPSDs > shmimMonitorT
The base shmimMonitor type.
Definition: modalPSDs.hpp:52
std::string m_fpsSource
Device name for getting fps to set circular buffer length. This device should have *....
Definition: modalPSDs.hpp:63
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
Definition: indiMacros.hpp:207
#define REG_INDI_SETPROP(prop, devName, propName)
Register a SET INDI property with the class, using the standard callback name.
Definition: indiMacros.hpp:264
@ OPERATING
The device is operating, other than homing.
Definition: stateCodes.hpp:50
#define INDI_IDLE
Definition: indiUtils.hpp:28
std::ostream & cerr()
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
Definition: indiUtils.hpp:95
const pcf::IndiProperty & ipRecv
void nanoSleep(unsigned long nsec)
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
Definition: acesxeCtrl.hpp:687
INDI_SETCALLBACK_DEFN(MagAOXApp< _useINDI >, m_indiP_powerChannel)(const pcf
Definition: MagAOXApp.hpp:3195
Definition: dm.hpp:24
constexpr static logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
Definition: logPriority.hpp:43
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
Definition: logPriority.hpp:46
Software ERR log entry.