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  //Give the test harness access.
45  friend class modalPSDs_test;
46 
47  friend class dev::shmimMonitor<modalPSDs>;
48 
49 public:
50 
51  typedef float realT;
52  typedef std::complex<realT> complexT;
53 
54  /// The base shmimMonitor type
56 
57  /// The amplitude circular buffer type
58  typedef mx::sigproc::circularBufferIndex<float*, unsigned> ampCircBuffT;
59 
60 protected:
61 
62  /** \name Configurable Parameters
63  *@{
64  */
65 
66  std::string m_fpsSource; ///< Device name for getting fps to set circular buffer length. This device should have *.fps.current.
67 
68  realT m_psdTime{ 1 }; ///< The length of time over which to calculate PSDs. The default is 1 sec.
69  realT m_psdAvgTime{ 10 }; ///< The time over which to average PSDs. The default is 10 sec.
70 
71  //realT m_overSize {10}; ///< Multiplicative factor by which to oversize the circular buffer, to give good mean estimates and account for time-to-calculate.
72 
73  realT m_psdOverlapFraction{ 0.5 }; ///< The fraction of the sample time to overlap by.
74 
75  int m_nPSDHistory{ 100 }; //
76 
77 
78  ///@}
79 
80  int m_nModes{ 0 }; ///< the number of modes to calculate PSDs for.
81 
83 
84  //std::vector<ampCircBuffT> m_ampCircBuffs;
85 
86  realT m_fps{ 0 };
87  realT m_df{ 1 };
88 
89  //unsigned m_tsCircBuffLength {4000}; ///< Length of the time-series circular buffers. This is updated by m_fpsSource and m_psdTime.
90 
91  unsigned m_tsSize{ 2000 }; ///< The length of the time series sample over which the PSD is calculated
92  unsigned m_tsOverlapSize{ 1000 }; ///< The number of samples in the overlap
93 
94  std::vector<realT> m_win; ///< The window function. By default this is Hann.
95 
96  realT* m_tsWork{ nullptr };
97  size_t m_tsWorkSize{ 0 };
98 
99  std::complex<realT>* m_fftWork{ nullptr };
100  size_t m_fftWorkSize{ 0 };
101 
102  std::vector<realT> m_psd;
103 
104  mx::math::fft::fftT< realT, std::complex<realT>, 1, 0> m_fft;
105  mx::math::fft::fftwEnvironment<realT> m_fftEnv;
106 
107  /** \name PSD Calculation Thread
108  * Handling of offloads from the average woofer shape
109  * @{
110  */
111  int m_psdThreadPrio{ 0 }; ///< Priority of the PSD Calculation thread.
112  std::string m_psdThreadCpuset; ///< The cpuset to use for the PSD Calculation thread.
113 
114  std::thread m_psdThread; ///< The PSD Calculation thread.
115 
116  bool m_psdThreadInit{ true }; ///< Initialization flag for the PSD Calculation thread.
117 
118  bool m_psdRestarting{ true }; ///< Synchronization flag. This will only become false after a successful call to allocate.
119  bool m_psdWaiting{ false }; ///< Synchronization flag. This is set to true when the PSD thread is safely waiting for allocation to complete.
120 
121  pid_t m_psdThreadID{ 0 }; ///< PSD Calculation thread PID.
122 
123  pcf::IndiProperty m_psdThreadProp; ///< The property to hold the PSD Calculation thread details.
124 
125  /// PS Calculation thread starter function
126  static void psdThreadStart(modalPSDs* p /**< [in] pointer to this */);
127 
128  /// PSD Calculation thread function
129  /** Runs until m_shutdown is true.
130  */
131  void psdThreadExec();
132 
133  IMAGE* m_freqStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to hold the frequency scale
134 
135  mx::improc::eigenImage<realT> m_psdBuffer;
136 
137  IMAGE* m_rawpsdStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to hold the raw psds
138 
139  IMAGE* m_avgpsdStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to hold the average psds
140 
141 public:
142  /// Default c'tor.
143  modalPSDs();
144 
145  /// D'tor, declared and defined for noexcept.
146  ~modalPSDs() noexcept
147  {}
148 
149  virtual void setupConfig();
150 
151  /// Implementation of loadConfig logic, separated for testing.
152  /** This is called by loadConfig().
153  */
154  int loadConfigImpl(mx::app::appConfigurator& _config /**< [in] an application configuration from which to load values*/);
155 
156  virtual void loadConfig();
157 
158  /// Startup function
159  /**
160  *
161  */
162  virtual int appStartup();
163 
164  /// Implementation of the FSM for modalPSDs.
165  /**
166  * \returns 0 on no critical error
167  * \returns -1 on an error requiring shutdown
168  */
169  virtual int appLogic();
170 
171  /// Shutdown the app.
172  /**
173  *
174  */
175  virtual int appShutdown();
176 
177  //shmimMonitor Interface
178 protected:
179 
180  int allocate(const dev::shmimT& dummy /**< [in] tag to differentiate shmimMonitor parents.*/);
181 
182  int allocatePSDStreams();
183 
184  int processImage( void* curr_src, ///< [in] pointer to start of current frame.
185  const dev::shmimT& dummy ///< [in] tag to differentiate shmimMonitor parents.
186  );
187 
188 
189  //INDI Interface
190 protected:
191 
192  pcf::IndiProperty m_indiP_psdTime;
193  pcf::IndiProperty m_indiP_psdAvgTime;
194  pcf::IndiProperty m_indiP_overSize;
195  pcf::IndiProperty m_indiP_fpsSource;
196  pcf::IndiProperty m_indiP_fps;
197 
198 public:
199 
204 
205 };
206 
207 modalPSDs::modalPSDs() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
208 {
209  return;
210 }
211 
213 {
215 
216  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.");
217  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.");
218  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.");
219 }
220 
221 int modalPSDs::loadConfigImpl(mx::app::appConfigurator& _config)
222 {
223  SHMIMMONITOR_LOAD_CONFIG(_config);
224 
225  _config(m_fpsSource, "circBuff.fpsSource");
226  _config(m_fps, "circBuff.defaultFPS");
227  _config(m_psdTime, "circBuff.psdTime");
228 
229  return 0;
230 }
231 
233 {
234  loadConfigImpl(config);
235 }
236 
238 {
239  CREATE_REG_INDI_NEW_NUMBERF(m_indiP_psdTime, "psdTime", 0, 60, 0.1, "%0.1f", "PSD time", "PSD Setup");
240  m_indiP_psdTime["current"].set(m_psdTime);
241  m_indiP_psdTime["target"].set(m_psdTime);
242 
243  CREATE_REG_INDI_NEW_NUMBERU(m_indiP_psdAvgTime, "psdAvgTime", 0, 60, 0.1, "%0.1f", "PSD Avg. Time", "PSD Setup");
244  m_indiP_psdAvgTime["current"].set(m_psdAvgTime);
245  m_indiP_psdAvgTime["target"].set(m_psdAvgTime);
246 
247  if (m_fpsSource != "")
248  {
249  REG_INDI_SETPROP(m_indiP_fpsSource, m_fpsSource, std::string("fps"));
250  }
251 
252  CREATE_REG_INDI_RO_NUMBER(m_indiP_fps, "fps", "current", "Circular Buffer");
253  m_indiP_fps.add(pcf::IndiElement("current"));
254  m_indiP_fps["current"] = m_fps;
255 
256 
258 
260 
262 
263  return 0;
264 }
265 
267 {
269 
270  XWCAPP_THREAD_CHECK(m_psdThread, "psdcalc");
271 
272  std::unique_lock<std::mutex> lock(m_indiMutex);
273 
275 
276  return 0;
277 }
278 
280 {
282 
284 
285  if (m_rawpsdStream)
286  {
287  ImageStreamIO_destroyIm(m_rawpsdStream);
288  free(m_rawpsdStream);
289  }
290 
291  if (m_avgpsdStream)
292  {
293  ImageStreamIO_destroyIm(m_avgpsdStream);
294  free(m_avgpsdStream);
295  }
296 
297  if (m_freqStream)
298  {
299  ImageStreamIO_destroyIm(m_freqStream);
300  free(m_freqStream);
301  }
302 
303  if (m_tsWork) fftw_free(m_tsWork);
304  if (m_fftWork) fftw_free(m_fftWork);
305 
306  return 0;
307 }
308 
310 {
311  static_cast<void>(dummy);
312 
313  m_psdRestarting = true;
314 
315  //Prevent reallocation while the psd thread might be calculating
316  while (m_psdWaiting == false && !shutdown()) mx::sys::microSleep(100);
317 
318  if (shutdown()) return 0; //If shutdown() is true then shmimMonitor will cleanup
319 
320  if (m_fps > 0)
321  {
322  //m_tsCircBuffLength = m_fps * m_psdTime * m_overSize;
325  }
326 
327  if (m_tsOverlapSize == 0 || !std::isnormal(m_tsOverlapSize))
328  {
329  log<software_error>({ __FILE__,__LINE__, "bad m_tsOverlapSize value: " + std::to_string(m_tsOverlapSize) });
330  return -1;
331  }
332 
333  //Check for unsupported type (must be realT)
335  {
336  //must be a vector of size 1 on one axis
337  log<software_error>({ __FILE__,__LINE__, "unsupported data type: must be realT" });
338  return -1;
339  }
340 
341  //Check for unexpected format
343  {
344  //must be a vector of size 1 on one axis
345  log<software_error>({ __FILE__,__LINE__, "unexpected shmim format" });
346  return -1;
347  }
348 
349  std::cerr << "connected to " << shmimMonitorT::m_shmimName << " " << shmimMonitorT::m_width << " " << shmimMonitorT::m_height << " " << shmimMonitorT::m_depth << "\n";
350 
351 
353 
354  //Size the circ buff
356 
357  //Create the window
358  m_win.resize(m_tsSize);
359  mx::sigproc::window::hann(m_win);
360 
361  //Set up the FFT and working memory
362  m_fft.plan(m_tsSize, MXFFT_FORWARD, false);
363 
364  if (m_tsWork) fftw_free(m_tsWork);
365  m_tsWork = mx::math::fft::fftw_malloc<realT>(m_tsSize);
366 
367  if (m_fftWork) fftw_free(m_fftWork);
368  m_fftWork = mx::math::fft::fftw_malloc<std::complex<realT>>((m_tsSize / 2 + 1));
369 
370  m_psd.resize(m_tsSize / 2 + 1);
371 
372  if (m_fps > 0)
373  {
374  m_df = 1.0 / (m_tsSize / m_fps);
375  }
376  else
377  {
378  m_df = 1.0 / (m_tsSize);
379  }
380 
381  //Create the shared memory images
382  uint32_t imsize[3];
383 
384  //First the frequency
385  imsize[0] = 1;
386  imsize[1] = m_psd.size();
387  imsize[2] = 1;
388 
389  if (m_freqStream)
390  {
391  ImageStreamIO_destroyIm(m_freqStream);
392  free(m_freqStream);
393  }
394  m_freqStream = static_cast<IMAGE*>(malloc(sizeof(IMAGE)));
395 
396  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);
397  m_freqStream->md->write = 1;
398  for (size_t n = 0; n < m_psd.size(); ++n)
399  {
400  m_freqStream->array.F[n] = n * m_df;
401  }
402 
403  //Set the time of last write
404  clock_gettime(CLOCK_REALTIME, &m_freqStream->md->writetime);
405  m_freqStream->md->atime = m_freqStream->md->writetime;
406 
407  //Update cnt1
408  m_freqStream->md->cnt1 = 0;
409 
410  //Update cnt0
411  m_freqStream->md->cnt0 = 0;
412 
413  m_freqStream->md->write = 0;
414  ImageStreamIO_sempost(m_freqStream, -1);
415 
417 
418  m_psdRestarting = false;
419 
420  return 0;
421 }
422 
424 {
425  if (m_rawpsdStream)
426  {
427  ImageStreamIO_destroyIm(m_rawpsdStream);
428  free(m_rawpsdStream);
429  }
430 
431  uint32_t imsize[3];
432  imsize[0] = m_psd.size();
433  imsize[1] = m_nModes;
434  imsize[2] = m_nPSDHistory;
435 
436  m_rawpsdStream = static_cast<IMAGE*>(malloc(sizeof(IMAGE)));
437 
438  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);
439 
440  if (m_avgpsdStream)
441  {
442  ImageStreamIO_destroyIm(m_avgpsdStream);
443  free(m_avgpsdStream);
444  }
445 
446  imsize[0] = m_psd.size();
447  imsize[1] = m_nModes;
448  imsize[2] = 1;
449 
450  m_avgpsdStream = static_cast<IMAGE*>(malloc(sizeof(IMAGE)));
451  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);
452 
453  m_psdBuffer.resize(m_psd.size(), m_nModes);
454 
455  return 0;
456 }
457 
458 int modalPSDs::processImage( void* curr_src,
459  const dev::shmimT& dummy
460  )
461 {
462  static_cast<void>(dummy);
463 
464  float* f_src = static_cast<float*>(curr_src);
465 
466  m_ampCircBuff.nextEntry(f_src);
467 
468  return 0;
469 }
470 
472 {
473  p->psdThreadExec();
474 }
475 
476 
478 {
479  m_psdThreadID = syscall(SYS_gettid);
480 
481  while (m_psdThreadInit == true && shutdown() == 0)
482  {
483  sleep(1);
484  }
485 
486  while (shutdown() == 0)
487  {
488  if (m_psdRestarting == true || m_ampCircBuff.maxEntries() == 0) m_psdWaiting = true;
489 
490  while ((m_psdRestarting == true || m_ampCircBuff.maxEntries() == 0) && !shutdown()) mx::sys::microSleep(100);
491 
492  if (shutdown()) break;
493 
494  m_psdWaiting = false;
495 
496  if (m_ampCircBuff.maxEntries() == 0)
497  {
498  log<software_error>({ __FILE__, __LINE__, "amp circ buff has zero size" });
499  return;
500  }
501 
502  std::cerr << "waiting to grow\n";
503  while (m_ampCircBuff.size() < m_ampCircBuff.maxEntries() && m_psdRestarting == false && !shutdown())
504  {
505  //shrinking sleep
506  double stime = (1.0 * m_ampCircBuff.maxEntries() - 1.0 * m_ampCircBuff.size()) / m_fps * 0.5 * 1e9;
507  mx::sys::nanoSleep(stime);
508  }
509 
510  std::cerr << "all grown. starting to calculate\n";
511 
512  ampCircBuffT::indexT ne0;
513  ampCircBuffT::indexT ne1 = m_ampCircBuff.latest();
514  if (ne1 > m_tsOverlapSize) ne1 -= m_tsSize;
515  else ne1 = m_ampCircBuff.size() + ne1 - m_tsSize;
516 
517  while (m_psdRestarting == false && !shutdown())
518  {
519  //Used to check if we are getting too behind
520  uint64_t mono0 = m_ampCircBuff.mono();
521 
522  //Calc PSDs here
523  ne0 = ne1;
524 
525  std::cerr << "calculating: " << ne0 << " " << m_ampCircBuff.size() << " " << m_tsSize << "\n";
526  double t0 = mx::sys::get_curr_time();
527 
528  for (size_t m = 0; m < shmimMonitorT::m_width * shmimMonitorT::m_height; ++m) //Loop over each mode
529  {
530  //get mean going over entire TS
531  realT mn = 0;
532  for (size_t n = 0; n < m_ampCircBuff.size(); ++n)
533  {
534  mn += m_ampCircBuff[n][m];
535  }
536  mn /= m_ampCircBuff.size();
537 
538  double var = 0;
539  for (size_t n = 0; n < m_tsSize; ++n)
540  {
541  m_tsWork[n] = (m_ampCircBuff.at(ne0, n)[m] - mn); //load mean subtracted chunk
542  var += pow(m_tsWork[n], 2);
543 
544  m_tsWork[n] *= m_win[n];
545  }
546  var /= m_tsSize;
547 
549 
550  double nm = 0;
551  for (size_t n = 0; n < m_psd.size(); ++n)
552  {
553  m_psd[n] = norm(m_fftWork[n]);
554  nm += m_psd[n] * m_df;
555  }
556 
557  for (size_t n = 0; n < m_psd.size(); ++n)
558  {
559  m_psd[n] *= (var / nm);
560  }
561 
562  //Put it in the buffer for uploading to shmim
563  for (size_t n = 0; n < m_psd.size(); ++n) m_psdBuffer(n, m) = m_psd[n];
564 
565  }
566 
567  //------------------------- the raw psds ---------------------------
568  m_rawpsdStream->md->write = 1;
569 
570  //Set the time of last write
571  clock_gettime(CLOCK_REALTIME, &m_rawpsdStream->md->writetime);
572  m_rawpsdStream->md->atime = m_rawpsdStream->md->writetime;
573 
574  uint64_t cnt1 = m_rawpsdStream->md->cnt1 + 1;
575  if (cnt1 >= m_rawpsdStream->md->size[2]) cnt1 = 0;
576 
577  //Move to next pointer
578  float* F = m_rawpsdStream->array.F + m_psdBuffer.rows() * m_psdBuffer.cols() * cnt1;
579 
580  memcpy(F, m_psdBuffer.data(), m_psdBuffer.rows() * m_psdBuffer.cols() * sizeof(float));
581 
582  //Update cnt1
583  m_rawpsdStream->md->cnt1 = cnt1;
584 
585  //Update cnt0
586  ++m_rawpsdStream->md->cnt0;
587 
588  m_rawpsdStream->md->write = 0;
589  ImageStreamIO_sempost(m_rawpsdStream, -1);
590 
591  //-------------------------- now average the psds ----------------------------
592 
593  int nPSDAverage = (m_psdAvgTime / m_psdTime) / m_psdOverlapFraction;
594 
595  if (nPSDAverage <= 0) nPSDAverage = 1;
596  else if ((uint64_t)nPSDAverage > m_rawpsdStream->md->size[2]) nPSDAverage = m_rawpsdStream->md->size[2];
597 
598  //Move to next pointer
599  F = m_rawpsdStream->array.F + m_psdBuffer.rows() * m_psdBuffer.cols() * cnt1;
600 
601  memcpy(m_psdBuffer.data(), F, m_psdBuffer.rows() * m_psdBuffer.cols() * sizeof(float));
602 
603  for (int n = 1; n < nPSDAverage; ++n)
604  {
605  if (cnt1 == 0) cnt1 = m_rawpsdStream->md->size[2] - 1;
606  else --cnt1;
607 
608  F = m_rawpsdStream->array.F + m_psdBuffer.rows() * m_psdBuffer.cols() * cnt1;
609 
610  m_psdBuffer += Eigen::Map<Eigen::Array<float, -1, -1>>(F, m_psdBuffer.rows(), m_psdBuffer.cols());
611  }
612 
613  m_psdBuffer /= nPSDAverage;
614 
615  m_avgpsdStream->md->write = 1;
616 
617  //Set the time of last write
618  clock_gettime(CLOCK_REALTIME, &m_avgpsdStream->md->writetime);
619  m_avgpsdStream->md->atime = m_avgpsdStream->md->writetime;
620 
621  //Move to next pointer
622  F = m_avgpsdStream->array.F;
623 
624  memcpy(F, m_psdBuffer.data(), m_psdBuffer.rows() * m_psdBuffer.cols() * sizeof(float));
625 
626  //Update cnt1
627  m_avgpsdStream->md->cnt1 = 0;
628 
629  //Update cnt0
630  ++m_avgpsdStream->md->cnt0;
631 
632  m_avgpsdStream->md->write = 0;
633  ImageStreamIO_sempost(m_avgpsdStream, -1);
634 
635  double t1 = mx::sys::get_curr_time();
636  std::cerr << "done " << t1 - t0 << "\n";
637 
638  //Have to be cycling within the overlap
639  if (m_ampCircBuff.mono() - mono0 >= m_tsOverlapSize)
640  {
641  log<text_log>("PSD calculations getting behind, skipping ahead.", logPrio::LOG_WARNING);
642  ne0 = m_ampCircBuff.latest();
643  if (ne0 > m_tsOverlapSize) ne0 -= m_tsOverlapSize;
644  else ne0 = m_ampCircBuff.size() + ne0 - m_tsOverlapSize;
645  }
646 
647  //Now wait until we get to next one
648  ne1 = ne0 + m_tsOverlapSize;
649  if (ne1 >= m_ampCircBuff.size())
650  {
651  ne1 -= m_ampCircBuff.size();
652  }
653 
654  ampCircBuffT::indexT ce = m_ampCircBuff.latest();
655  //wrapped difference
656  long dn;
657  if (ce >= ne1) dn = ce - ne1;
658  else dn = ce + (m_ampCircBuff.size() - ne1);
659 
660  while (dn < m_tsOverlapSize && !shutdown() && m_psdRestarting == false)
661  {
662  double stime = (1.0 * dn) / m_fps * 0.5 * 1e9;
663  mx::sys::nanoSleep(stime);
664 
665  ce = m_ampCircBuff.latest();
666 
667  if (ce >= ne1) dn = ce - ne1;
668  else dn = ce + (m_ampCircBuff.size() - ne1);
669  }
670  }
671  }
672 }
673 
674 INDI_NEWCALLBACK_DEFN(modalPSDs, m_indiP_psdTime)(const pcf::IndiProperty& ipRecv)
675 {
676  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_psdTime, ipRecv);
677 
678  realT target;
679 
680  if (indiTargetUpdate(m_indiP_psdTime, target, ipRecv, true) < 0)
681  {
682  log<software_error>({ __FILE__,__LINE__ });
683  return -1;
684  }
685 
686  if (m_psdTime != target)
687  {
688  std::lock_guard<std::mutex> guard(m_indiMutex);
689 
690  m_psdTime = target;
691 
692  updateIfChanged(m_indiP_psdTime, "current", m_psdTime, INDI_IDLE);
693  updateIfChanged(m_indiP_psdTime, "target", m_psdTime, INDI_IDLE);
694 
695  shmimMonitorT::m_restart = true;
696 
697  log<text_log>("set psdTime to " + std::to_string(m_psdTime), logPrio::LOG_NOTICE);
698  }
699 
700  return 0;
701 } //INDI_NEWCALLBACK_DEFN(modalPSDs, m_indiP_psdTime)
702 
703 INDI_NEWCALLBACK_DEFN(modalPSDs, m_indiP_psdAvgTime)(const pcf::IndiProperty& ipRecv)
704 {
705  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_psdAvgTime, ipRecv);
706 
707  realT target;
708 
709  if (indiTargetUpdate(m_indiP_psdAvgTime, target, ipRecv, true) < 0)
710  {
711  log<software_error>({ __FILE__,__LINE__ });
712  return -1;
713  }
714 
715  if (m_psdAvgTime != target)
716  {
717  std::lock_guard<std::mutex> guard(m_indiMutex);
718 
719  m_psdAvgTime = target;
720 
721  updateIfChanged(m_indiP_psdTime, "current", m_psdAvgTime, INDI_IDLE);
722  updateIfChanged(m_indiP_psdTime, "target", m_psdAvgTime, INDI_IDLE);
723 
724  log<text_log>("set psdAvgTime to " + std::to_string(m_psdAvgTime), logPrio::LOG_NOTICE);
725  }
726 
727  return 0;
728 } //INDI_NEWCALLBACK_DEFN(modalPSDs, m_indiP_psdAvgTime)
729 
730 INDI_SETCALLBACK_DEFN(modalPSDs, m_indiP_fpsSource)(const pcf::IndiProperty& ipRecv)
731 {
732  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_fpsSource, ipRecv);
733 
734  if (ipRecv.find("current") != true) //this isn't valid
735  {
736  log<software_error>({ __FILE__, __LINE__, "No current property in fps source." });
737  return 0;
738  }
739 
740  std::lock_guard<std::mutex> guard(m_indiMutex);
741 
742  realT fps = ipRecv["current"].get<realT>();
743 
744  if (fps != m_fps)
745  {
746  m_fps = fps;
747  log<text_log>("set fps to " + std::to_string(m_fps), logPrio::LOG_NOTICE);
748  updateIfChanged(m_indiP_fps, "current", m_fps, INDI_IDLE);
749  shmimMonitorT::m_restart = true;
750  }
751 
752  return 0;
753 
754 } //INDI_SETCALLBACK_DEFN(modalPSDs, m_indiP_fpsSource)
755 
756 } //namespace app
757 } //namespace MagAOX
758 
759 #endif //modalPSDs_hpp
#define IMAGESTRUCT_FLOAT
Definition: ImageStruct.hpp:22
#define XWCAPP_THREAD_CHECK(thrdSt, thrdName)
Error handling wrapper for checking on thread status with tryjoin.
Definition: MagAOXApp.hpp:3541
#define XWCAPP_THREAD_START(thrdSt, thrdInit, thrdId, thrdProp, thrdPrio, thrdCpuset, thrdName, thrdStart)
Error handling wrapper for the threadStart function of the XWCApp.
Definition: MagAOXApp.hpp:3527
#define XWCAPP_THREAD_STOP(thrdSt)
Error handlng wrapper for stopping a thread.
Definition: MagAOXApp.hpp:3561
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:73
std::string m_configName
The name of the configuration file (minus .conf).
Definition: MagAOXApp.hpp:83
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2297
int shutdown()
Get the value of the shutdown flag.
Definition: MagAOXApp.hpp:1182
std::mutex m_indiMutex
Mutex for locking INDI communications.
Definition: MagAOXApp.hpp:545
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.
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...
uint8_t m_dataType
The ImageStreamIO type code.
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:73
pcf::IndiProperty m_psdThreadProp
The property to hold the PSD Calculation thread details.
Definition: modalPSDs.hpp:123
pcf::IndiProperty m_indiP_psdAvgTime
Definition: modalPSDs.hpp:193
INDI_NEWCALLBACK_DECL(modalPSDs, m_indiP_overSize)
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
Definition: modalPSDs.hpp:221
pid_t m_psdThreadID
PSD Calculation thread PID.
Definition: modalPSDs.hpp:121
mx::improc::eigenImage< realT > m_psdBuffer
Definition: modalPSDs.hpp:135
std::thread m_psdThread
The PSD Calculation thread.
Definition: modalPSDs.hpp:114
pcf::IndiProperty m_indiP_overSize
Definition: modalPSDs.hpp:194
unsigned m_tsOverlapSize
The number of samples in the overlap.
Definition: modalPSDs.hpp:92
std::complex< realT > * m_fftWork
Definition: modalPSDs.hpp:99
mx::math::fft::fftwEnvironment< realT > m_fftEnv
Definition: modalPSDs.hpp:105
mx::sigproc::circularBufferIndex< float *, unsigned > ampCircBuffT
The amplitude circular buffer type.
Definition: modalPSDs.hpp:58
INDI_NEWCALLBACK_DECL(modalPSDs, m_indiP_psdTime)
static void psdThreadStart(modalPSDs *p)
PS Calculation thread starter function.
Definition: modalPSDs.hpp:471
std::vector< realT > m_win
The window function. By default this is Hann.
Definition: modalPSDs.hpp:94
virtual int appStartup()
Startup function.
Definition: modalPSDs.hpp:237
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:139
IMAGE * m_rawpsdStream
The ImageStreamIO shared memory buffer to hold the raw psds.
Definition: modalPSDs.hpp:137
modalPSDs()
Default c'tor.
Definition: modalPSDs.hpp:207
int m_nModes
the number of modes to calculate PSDs for.
Definition: modalPSDs.hpp:80
mx::math::fft::fftT< realT, std::complex< realT >, 1, 0 > m_fft
Definition: modalPSDs.hpp:104
bool m_psdRestarting
Synchronization flag. This will only become false after a successful call to allocate.
Definition: modalPSDs.hpp:118
virtual void loadConfig()
Definition: modalPSDs.hpp:232
pcf::IndiProperty m_indiP_fps
Definition: modalPSDs.hpp:196
virtual int appLogic()
Implementation of the FSM for modalPSDs.
Definition: modalPSDs.hpp:266
int processImage(void *curr_src, const dev::shmimT &dummy)
Definition: modalPSDs.hpp:458
IMAGE * m_freqStream
The ImageStreamIO shared memory buffer to hold the frequency scale.
Definition: modalPSDs.hpp:133
realT m_psdAvgTime
The time over which to average PSDs. The default is 10 sec.
Definition: modalPSDs.hpp:69
unsigned m_tsSize
The length of the time series sample over which the PSD is calculated.
Definition: modalPSDs.hpp:91
virtual void setupConfig()
Definition: modalPSDs.hpp:212
ampCircBuffT m_ampCircBuff
Definition: modalPSDs.hpp:82
int m_psdThreadPrio
Priority of the PSD Calculation thread.
Definition: modalPSDs.hpp:111
std::string m_psdThreadCpuset
The cpuset to use for the PSD Calculation thread.
Definition: modalPSDs.hpp:112
std::complex< realT > complexT
Definition: modalPSDs.hpp:52
int allocate(const dev::shmimT &dummy)
Definition: modalPSDs.hpp:309
bool m_psdWaiting
Synchronization flag. This is set to true when the PSD thread is safely waiting for allocation to com...
Definition: modalPSDs.hpp:119
pcf::IndiProperty m_indiP_psdTime
Definition: modalPSDs.hpp:192
pcf::IndiProperty m_indiP_fpsSource
Definition: modalPSDs.hpp:195
realT m_psdTime
The length of time over which to calculate PSDs. The default is 1 sec.
Definition: modalPSDs.hpp:68
void psdThreadExec()
PSD Calculation thread function.
Definition: modalPSDs.hpp:477
bool m_psdThreadInit
Initialization flag for the PSD Calculation thread.
Definition: modalPSDs.hpp:116
virtual int appShutdown()
Shutdown the app.
Definition: modalPSDs.hpp:279
~modalPSDs() noexcept
D'tor, declared and defined for noexcept.
Definition: modalPSDs.hpp:146
std::vector< realT > m_psd
Definition: modalPSDs.hpp:102
dev::shmimMonitor< modalPSDs > shmimMonitorT
The base shmimMonitor type.
Definition: modalPSDs.hpp:55
std::string m_fpsSource
Device name for getting fps to set circular buffer length. This device should have *....
Definition: modalPSDs.hpp:66
#define CREATE_REG_INDI_NEW_NUMBERU(prop, name, min, max, step, format, label, group)
Create and register a NEW INDI property as a standard number as unsigned int, using the standard call...
Definition: indiMacros.hpp:363
#define CREATE_REG_INDI_NEW_NUMBERF(prop, name, min, max, step, format, label, group)
Create and register a NEW INDI property as a standard number as float, using the standard callback na...
Definition: indiMacros.hpp:309
#define REG_INDI_SETPROP(prop, devName, propName)
Register a SET INDI property with the class, using the standard callback name.
Definition: indiMacros.hpp:282
#define CREATE_REG_INDI_RO_NUMBER(prop, name, label, group)
Create and register a RO INDI property as a number, using the standard callback name.
Definition: indiMacros.hpp:385
@ OPERATING
The device is operating, other than homing.
Definition: stateCodes.hpp:55
#define INDI_IDLE
Definition: indiUtils.hpp:28
std::ostream & cerr()
INDI_VALIDATE_CALLBACK_PROPS(function, ipRecv)
void nanoSleep(unsigned long nsec)
const pcf::IndiProperty & ipRecv
Definition: MagAOXApp.hpp:3434
INDI_SETCALLBACK_DEFN(adcTracker, m_indiP_teldata)(const pcf
Definition: adcTracker.hpp:461
updateIfChanged(m_indiP_angle, "target", m_angle)
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
Definition: acesxeCtrl.hpp:687
std::unique_lock< std::mutex > lock(m_indiMutex)
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
#define SHMIMMONITOR_APP_SHUTDOWN
Call shmimMonitorT::appShutdown with error checking for shmimMonitor.
#define SHMIMMONITOR_APP_LOGIC
Call shmimMonitorT::appLogic with error checking for shmimMonitor.
#define SHMIMMONITOR_APP_STARTUP
Call shmimMonitorT::appStartup with error checking for shmimMonitor.
#define SHMIMMONITOR_LOAD_CONFIG(cfig)
Call shmimMonitorT::loadConfig with error checking for shmimMonitor.
#define SHMIMMONITOR_UPDATE_INDI
Call shmimMonitorT::updateINDI with error checking for shmimMonitor.
#define SHMIMMONITOR_SETUP_CONFIG(cfig)
Call shmimMonitorT::setupConfig with error checking for shmimMonitor.