API
 
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Loading...
Searching...
No Matches
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#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
11#include "../../magaox_git_version.h"
12
13#include <mx/sigproc/circularBuffer.hpp>
14#include <mx/sigproc/signalWindows.hpp>
15
16#include <mx/math/ft/fftwEnvironment.hpp>
17#include <mx/math/ft/fftT.hpp>
18
19/** \defgroup modalPSDs
20 * \brief An application to calculate rolling PSDs of modal amplitudes
21 *
22 * <a href="../handbook/operating/software/apps/modalPSDs.html">Application Documentation</a>
23 *
24 * \ingroup apps
25 *
26 */
27
28/** \defgroup modalPSDs_files
29 * \ingroup modalPSDs
30 */
31
32namespace MagAOX
33{
34namespace app
35{
36
37/// Class for application to calculate rolling PSDs of modal amplitudes.
38/**
39 * \ingroup modalPSDs
40 */
41class modalPSDs : public MagAOXApp<true>, public dev::shmimMonitor<modalPSDs>
42{
43 // Give the test harness access.
44 friend class modalPSDs_test;
45
46 friend class dev::shmimMonitor<modalPSDs>;
47
48 public:
49 typedef float realT;
50
51 typedef std::complex<realT> complexT;
52
53 typedef int32_t cbIndexT; ///< The index for the circular buffer
54
55 /// The base shmimMonitor type
57
58 /// The amplitude circular buffer type
59 typedef mx::sigproc::circularBufferIndex<realT *, cbIndexT> ampCircBuffT;
60
61 protected:
62 /** \name Configurable Parameters
63 *@{
64 */
65
66 std::string m_fpsDevice; ///< Device name for getting fps to set circular buffer length.
67 std::string m_fpsProperty{ "fps" }; ///< Property name for getting fps to set circular buffer length.
68 std::string m_fpsElement{ "current" }; ///< Element name for getting fps to set circular buffer length.
69
70 realT m_fpsTol{ 0 }; ///< The tolerance for detecting a change in FPS.
71
72 realT m_psdTime{ 1 }; ///< The length of time over which to calculate PSDs. The default is 1 sec.
73 realT m_psdAvgTime{ 10 }; ///< The time over which to average PSDs. The default is 10 sec.
74
75 // realT m_overSize {10}; ///< Multiplicative factor by which to oversize the circular buffer, to give good mean
76 // estimates and account for time-to-calculate.
77
78 realT m_psdOverlapFraction{ 0.5 }; ///< The fraction of the sample time to overlap by.
79
80 int m_nPSDHistory{ 100 }; //
81
82 ///@}
83
84 size_t m_nModes{ 0 }; ///< the number of modes to calculate PSDs for.
85
87
88 // std::vector<ampCircBuffT> m_ampCircBuffs;
89
91
92 realT m_df{ 1 };
93
94 cbIndexT m_tsSize{ 2000 }; ///< The length of the time series sample over which the PSD is calculated
95
96 cbIndexT m_tsOverlapSize{ 1000 }; ///< The number of samples in the overlap
97
98 cbIndexT m_meanSize{ 20000 }; ///< The length of the time series over which to calculate the mean
99
100 std::vector<realT> m_win; ///< The window function. By default this is Hann.
101
102 realT *m_tsWork{ nullptr };
103 size_t m_tsWorkSize{ 0 };
104
105 std::complex<realT> *m_fftWork{ nullptr };
106 size_t m_fftWorkSize{ 0 };
107
108 std::vector<realT> m_psd;
109
110 mx::math::ft::fftT<realT, std::complex<realT>, 1, 0> m_fft;
111 mx::math::ft::fftwEnvironment<realT> m_fftEnv;
112
113 /** \name PSD Calculation Thread
114 * Handling of offloads from the average woofer shape
115 * @{
116 */
117 int m_psdThreadPrio{ 0 }; ///< Priority of the PSD Calculation thread.
118
119 std::string m_psdThreadCpuset; ///< The cpuset to use for the PSD Calculation thread.
120
121 std::thread m_psdThread; ///< The PSD Calculation thread.
122
123 bool m_psdThreadInit{ true }; ///< Initialization flag for the PSD Calculation thread.
124
125 bool m_psdRestarting{ true }; /**< Synchronization flag. This will only become false
126 after a successful call to allocate.*/
127
128 bool m_psdWaiting{ false }; ///< Synchronization flag. This is set to true when the PSD thread is safely waiting
129 ///< for allocation to complete.
130
131 pid_t m_psdThreadID{ 0 }; ///< PSD Calculation thread PID.
132
133 pcf::IndiProperty m_psdThreadProp; ///< The property to hold the PSD Calculation thread details.
134
135 /// PS Calculation thread starter function
136 static void psdThreadStart( modalPSDs *p /**< [in] pointer to this */ );
137
138 /// PSD Calculation thread function
139 /** Runs until m_shutdown is true.
140 */
141 void psdThreadExec();
142
143 IMAGE *m_freqStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to hold the frequency scale
144
145 mx::improc::eigenImage<realT> m_psdBuffer;
146
147 IMAGE *m_rawpsdStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to hold the raw psds
148
149 IMAGE *m_avgpsdStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to hold the average psds
150
151 public:
152 /// Default c'tor.
153 modalPSDs();
154
155 /// D'tor, declared and defined for noexcept.
157 {
158 }
159
160 virtual void setupConfig();
161
162 /// Implementation of loadConfig logic, separated for testing.
163 /** This is called by loadConfig().
164 */
165 int loadConfigImpl( mx::app::appConfigurator &_config /**< [in] an application configuration
166 from which to load values*/ );
167
168 virtual void loadConfig();
169
170 /// Startup function
171 /**
172 *
173 */
174 virtual int appStartup();
175
176 /// Implementation of the FSM for modalPSDs.
177 /**
178 * \returns 0 on no critical error
179 * \returns -1 on an error requiring shutdown
180 */
181 virtual int appLogic();
182
183 /// Shutdown the app.
184 /**
185 *
186 */
187 virtual int appShutdown();
188
189 // shmimMonitor Interface
190 protected:
191 int allocate( const dev::shmimT &dummy /**< [in] tag to differentiate shmimMonitor parents.*/ );
192
193 int allocatePSDStreams();
194
195 int processImage( void *curr_src, ///< [in] pointer to start of current frame.
196 const dev::shmimT &dummy ///< [in] tag to differentiate shmimMonitor parents.
197 );
198
199 // INDI Interface
200 protected:
201 pcf::IndiProperty m_indiP_psdTime;
202 pcf::IndiProperty m_indiP_psdAvgTime;
203 pcf::IndiProperty m_indiP_overSize;
204 pcf::IndiProperty m_indiP_fpsSource;
205 pcf::IndiProperty m_indiP_fps;
206
207 public:
212};
213
214modalPSDs::modalPSDs() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
215{
216 return;
217}
218
220{
222
223 config.add( "circBuff.fpsDevice",
224 "",
225 "circBuff.fpsDevice",
226 argType::Required,
227 "circBuff",
228 "fpsDevice",
229 false,
230 "string",
231 "Device name for getting fps to set circular buffer length." );
232 config.add( "circBuff.fpsProperty",
233 "",
234 "circBuff.fpsProperty",
235 argType::Required,
236 "circBuff",
237 "fpsProperty",
238 false,
239 "string",
240 "Property name for getting fps to set circular buffer length. Default is 'fps'." );
241 config.add( "circBuff.fpsElement",
242 "",
243 "circBuff.fpsElement",
244 argType::Required,
245 "circBuff",
246 "fpsElement",
247 false,
248 "string",
249 "Property name for getting fps to set circular buffer length. Default is 'current'." );
250 config.add( "circBuff.fpsTol",
251 "",
252 "circBuff.fpsTol",
253 argType::Required,
254 "circBuff",
255 "fpsTol",
256 false,
257 "float",
258 "Tolerance for detecting a change in FPS. Default is 0." );
259 config.add( "circBuff.defaultFPS",
260 "",
261 "circBuff.defaultFPS",
262 argType::Required,
263 "circBuff",
264 "defaultFPS",
265 false,
266 "realT",
267 "Default FPS at startup, will enable changing average length with psdTime before INDI available." );
268 config.add( "circBuff.psdTime",
269 "",
270 "circBuff.psdTime",
271 argType::Required,
272 "circBuff",
273 "psdTime",
274 false,
275 "realT",
276 "The length of time over which to calculate PSDs. The default is 1 sec." );
277}
278
279int modalPSDs::loadConfigImpl( mx::app::appConfigurator &_config )
280{
282
283 _config( m_fpsDevice, "circBuff.fpsDevice" );
284 _config( m_fpsProperty, "circBuff.fpsProperty" );
285 _config( m_fpsElement, "circBuff.fpsElement" );
286 _config( m_fpsTol, "circBuff.fpsTol" );
287
288 _config( m_psdTime, "circBuff.psdTime" );
289
290 return 0;
291}
292
294{
295 loadConfigImpl( config );
296}
297
299{
300 CREATE_REG_INDI_NEW_NUMBERF( m_indiP_psdTime, "psdTime", 0, 60, 0.1, "%0.1f", "PSD time", "PSD Setup" );
301 m_indiP_psdTime["current"].set( m_psdTime );
302 m_indiP_psdTime["target"].set( m_psdTime );
303
304 CREATE_REG_INDI_NEW_NUMBERU( m_indiP_psdAvgTime, "psdAvgTime", 0, 60, 0.1, "%0.1f", "PSD Avg. Time", "PSD Setup" );
305 m_indiP_psdAvgTime["current"].set( m_psdAvgTime );
306 m_indiP_psdAvgTime["target"].set( m_psdAvgTime );
307
308 if( m_fpsDevice == "" )
309 {
310 return log<software_critical, -1>(
311 { __FILE__, __LINE__, "FPS source is not configurated (circBuff.fpsDevice)" } );
312 }
313
315
316 CREATE_REG_INDI_RO_NUMBER( m_indiP_fps, "fps", "current", "Circular Buffer" );
317 m_indiP_fps.add( pcf::IndiElement( "current" ) );
318 m_indiP_fps["current"] = m_fps;
319
321
328 "psdcalc",
330
332
333 return 0;
334}
335
337{
339
340 XWCAPP_THREAD_CHECK( m_psdThread, "psdcalc" );
341
342 std::unique_lock<std::mutex> lock( m_indiMutex );
343
345
346 return 0;
347}
348
350{
351
353
355
356 if( m_rawpsdStream )
357 {
360 }
361
362 if( m_avgpsdStream )
363 {
366 }
367
368 if( m_freqStream )
369 {
372 }
373
374 if( m_tsWork )
375 {
377 }
378
379 if( m_fftWork )
380 {
382 }
383
384 return 0;
385}
386
388{
389 static_cast<void>( dummy );
390
391 m_psdRestarting = true;
392
393 // Wait for FPS to become not 0
394 // We wait indefinitely, the other process just might not be alive
395 bool logged = false;
396 while( m_fps <= 0 && !shutdown() )
397 {
398 if( !logged ) // log every thirty seconds
399 {
400 log<text_log>( "waiting for FPS...", logPrio::LOG_NOTICE );
401 logged = true;
402 }
403 mx::sys::sleep( 1 );
404 }
405
406 // Prevent reallocation while the psd thread might be calculating
407 while( m_psdWaiting == false && !shutdown() )
408 {
409 mx::sys::microSleep( 100 );
410 }
411
412 if( shutdown() )
413 {
414 return 0; // If shutdown() is true then shmimMonitor will cleanup
415 }
416
417 // Check for unsupported type (must be realT)
419 {
420 // must be a vector of size 1 on one axis
421 log<software_error>( { __FILE__, __LINE__, "unsupported data type: must be realT" } );
422 return -1;
423 }
424
425 // Check for unexpected format
427 {
428 // must be a vector of size 1 on one axis
429 log<software_error>( { __FILE__, __LINE__, "unexpected shmim format" } );
430 return -1;
431 }
432
433 std::cerr << "connected to " << shmimMonitorT::m_shmimName << " " << shmimMonitorT::m_width << " "
435
437
439
440 // Adjust length if odd to ensure we get the Nyquist frequency
441 if( m_tsSize % 2 == 1 )
442 {
443 m_tsSize += 1;
444 }
445
447
448 if( m_tsOverlapSize == 0 || !std::isnormal( m_tsOverlapSize ) )
449 {
451 { __FILE__, __LINE__, "bad m_tsOverlapSize value: " + std::to_string( m_tsOverlapSize ) } );
452 return -1;
453 }
454
456
457 if( static_cast<uint32_t>(m_meanSize) > shmimMonitorT::m_depth )
458 {
459 log<software_error>( { __FILE__, __LINE__, "input circ buff is not long enough for psd avg. time" } );
461 }
462
463 // Size the circ buff
464 // we really want 2*m_meanSize but might not be able to
465 if( 2 * static_cast<uint32_t>(m_meanSize) > shmimMonitorT::m_depth )
466 {
468 }
469 else
470 {
471 m_ampCircBuff.maxEntries( 2 * m_meanSize );
472 }
473
474 // Create the window
475 m_win.resize( m_tsSize );
476 mx::sigproc::window::hann( m_win );
477
478 // Set up the FFT and working memory
479 m_fft.plan( m_tsSize, mx::math::ft::dir::forward, false );
480
481 if( m_tsWork )
482 {
484 }
485 m_tsWork = mx::math::ft::fftw_malloc<realT>( m_tsSize );
486
487 if( m_fftWork )
488 {
490 }
491
492 m_fftWork = mx::math::ft::fftw_malloc<std::complex<realT>>( ( m_tsSize / 2 + 1 ) );
493
494 m_psd.resize( m_tsSize / 2 + 1 );
495
496 m_df = 1.0 / ( m_tsSize / m_fps );
497
498 // Create the shared memory images
499 uint32_t imsize[3];
500
501 // First the frequency
502 imsize[0] = 1;
503 imsize[1] = m_psd.size();
504 imsize[2] = 1;
505
506 if( m_freqStream )
507 {
510 }
511 m_freqStream = static_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
512
514 ( m_configName + "_freq" ).c_str(),
515 3,
516 imsize,
518 -1,
519 1,
521 0,
523 0 );
524 m_freqStream->md->write = 1;
525 for( size_t n = 0; n < m_psd.size(); ++n )
526 {
527 m_freqStream->array.F[n] = n * m_df;
528 }
529
530 // Set the time of last write
531 clock_gettime( CLOCK_REALTIME, &m_freqStream->md->writetime );
532 m_freqStream->md->atime = m_freqStream->md->writetime;
533
534 // Update cnt1
535 m_freqStream->md->cnt1 = 0;
536
537 // Update cnt0
538 m_freqStream->md->cnt0 = 0;
539
540 m_freqStream->md->write = 0;
542
544
545 m_psdRestarting = false;
546
547 return 0;
548}
549
551{
552 if( m_rawpsdStream )
553 {
556 }
557
558 uint32_t imsize[3];
559 imsize[0] = m_psd.size();
560 imsize[1] = m_nModes;
562
563 m_rawpsdStream = static_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
564
566 ( m_configName + "_rawpsds" ).c_str(),
567 3,
568 imsize,
570 -1,
571 1,
573 0,
575 0 );
576
577 if( m_avgpsdStream )
578 {
581 }
582
583 imsize[0] = m_psd.size();
584 imsize[1] = m_nModes;
585 imsize[2] = 1;
586
587 m_avgpsdStream = static_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
589 ( m_configName + "_psds" ).c_str(),
590 3,
591 imsize,
593 -1,
594 1,
596 0,
598 0 );
599
600 m_psdBuffer.resize( m_psd.size(), m_nModes );
601
602 return 0;
603}
604
605int modalPSDs::processImage( void *curr_src, const dev::shmimT &dummy )
606{
607 static_cast<void>( dummy );
608
609 float *f_src = static_cast<float *>( curr_src );
610
611 m_ampCircBuff.nextEntry( f_src );
612
613 return 0;
614}
615
617{
618 p->psdThreadExec();
619}
620
622{
624
625 while( m_psdThreadInit == true && shutdown() == 0 )
626 {
627 sleep( 1 );
628 }
629
630 while( shutdown() == 0 )
631 {
632 if( m_psdRestarting == true || m_ampCircBuff.maxEntries() == 0 )
633 {
634 m_psdWaiting = true;
635 }
636
637 while( ( m_psdRestarting == true || m_ampCircBuff.maxEntries() == 0 ) && !shutdown() )
638 {
639 mx::sys::microSleep( 100 );
640 }
641
642 if( shutdown() )
643 {
644 break;
645 }
646
647 m_psdWaiting = false;
648
649 if( m_ampCircBuff.maxEntries() == 0 )
650 {
651 log<software_error>( { __FILE__, __LINE__, "amp circ buff has zero size" } );
652 return;
653 }
654
655 // std::cerr << "waiting to grow\n";
656 while( m_ampCircBuff.size() < m_ampCircBuff.maxEntries() && m_psdRestarting == false && !shutdown() )
657 {
658 // shrinking sleep
659 double stime = ( 1.0 * m_ampCircBuff.maxEntries() - 1.0 * m_ampCircBuff.size() ) / m_fps * 0.5 * 1e9;
660 mx::sys::nanoSleep( stime );
661 }
662
663 // std::cerr << "all grown. starting to calculate\n";
664
665 ampCircBuffT::indexT ne0;
666 ampCircBuffT::indexT mne0;
667 ampCircBuffT::indexT ne1 = m_ampCircBuff.latest();
668 if( ne1 >= m_tsSize )
669 {
670 ne1 -= m_tsSize;
671 }
672 else
673 {
674 ne1 = m_ampCircBuff.size() + ne1 - m_tsSize;
675 }
676 // std::cerr << __LINE__ << " " << ne1 << " " << m_tsSize << " " << m_ampCircBuff.size() << '\n';
677
678 while( m_psdRestarting == false && !shutdown() )
679 {
680 // Used to check if we are getting too behind
682
683 // Calc PSDs here
684 ne0 = ne1;
685
686 if( ne0 >= m_meanSize )
687 {
688 mne0 = ne0 - m_meanSize;
689 }
690 else
691 {
692 mne0 = m_ampCircBuff.size() + ne0 - m_meanSize;
693 }
694
695 // std::cerr << "calculating: " << ne0 << " " << " " << m_tsSize << ' ' << m_ampCircBuff.size() << '\n';
696 // double t0 = mx::sys::get_curr_time();
697
698 for( size_t m = 0; m < m_nModes; ++m ) // Loop over each mode
699 {
700 // get mean going over avg time
701 realT mn = 0;
702 for( cbIndexT n = 0; n < m_meanSize; ++n )
703 {
704 mn += ( m_ampCircBuff.at( mne0, n ) )[m];
705 }
706 mn /= m_ampCircBuff.size();
707
708 double var = 0;
709
710 for( cbIndexT n = 0; n < m_tsSize; ++n )
711 {
712 m_tsWork[n] = ( m_ampCircBuff.at( ne0, n )[m] - mn ); // load mean subtracted chunk
713
714 var += pow( m_tsWork[n], 2 );
715
716 m_tsWork[n] *= m_win[n];
717 }
718 var /= m_tsSize;
719
721
722 double nm = 0;
723 for( size_t n = 0; n < m_psd.size(); ++n )
724 {
725 m_psd[n] = norm( m_fftWork[n] );
726 nm += m_psd[n] * m_df;
727 }
728
729 // Put it in the buffer for uploading to shmim
730 for( size_t n = 0; n < m_psd.size(); ++n )
731 {
732 // m_psd[n] *= ( var / nm );
733 m_psdBuffer( n, m ) = m_psd[n] * ( var / nm );
734 }
735 }
736
737 //------------------------- the raw psds ---------------------------
738 m_rawpsdStream->md->write = 1;
739
740 // Set the time of last write
741 clock_gettime( CLOCK_REALTIME, &m_rawpsdStream->md->writetime );
742 m_rawpsdStream->md->atime = m_rawpsdStream->md->writetime;
743
744 uint64_t cnt1 = m_rawpsdStream->md->cnt1 + 1;
745 if( cnt1 >= m_rawpsdStream->md->size[2] )
746 {
747 cnt1 = 0;
748 }
749
750 // Move to next pointer
751 float *F = m_rawpsdStream->array.F + m_psdBuffer.rows() * m_psdBuffer.cols() * cnt1;
752
753 memcpy( F, m_psdBuffer.data(), m_psdBuffer.rows() * m_psdBuffer.cols() * sizeof( float ) );
754
755 // Update cnt1
756 m_rawpsdStream->md->cnt1 = cnt1;
757
758 // Update cnt0
759 ++m_rawpsdStream->md->cnt0;
760
761 m_rawpsdStream->md->write = 0;
763
764 //-------------------------- now average the psds ----------------------------
765
767
768 if( nPSDAverage <= 0 )
769 {
770 nPSDAverage = 1;
771 }
772 else if( static_cast<uint64_t>(nPSDAverage) > m_rawpsdStream->md->size[2] )
773 {
774 nPSDAverage = m_rawpsdStream->md->size[2];
775 }
776
777 memcpy( m_psdBuffer.data(), F, m_psdBuffer.rows() * m_psdBuffer.cols() * sizeof( float ) );
778
779 for( int n = 1; n < nPSDAverage; ++n )
780 {
781 if( cnt1 == 0 )
782 {
783 cnt1 = m_rawpsdStream->md->size[2] - 1;
784 }
785 else
786 {
787 --cnt1;
788 }
789
790 F = m_rawpsdStream->array.F + m_psdBuffer.rows() * m_psdBuffer.cols() * cnt1;
791
792 m_psdBuffer += Eigen::Map<Eigen::Array<float, -1, -1>>( F, m_psdBuffer.rows(), m_psdBuffer.cols() );
793 }
794
796
797 m_avgpsdStream->md->write = 1;
798
799 // Set the time of last write
800 clock_gettime( CLOCK_REALTIME, &m_avgpsdStream->md->writetime );
801 m_avgpsdStream->md->atime = m_avgpsdStream->md->writetime;
802
803 // Move to next pointer
804 F = m_avgpsdStream->array.F;
805
806 memcpy( F, m_psdBuffer.data(), m_psdBuffer.rows() * m_psdBuffer.cols() * sizeof( float ) );
807
808 // Update cnt1
809 m_avgpsdStream->md->cnt1 = 0;
810
811 // Update cnt0
812 ++m_avgpsdStream->md->cnt0;
813
814 m_avgpsdStream->md->write = 0;
816
817 // double t1 = mx::sys::get_curr_time();
818 // std::cerr << "done " << t1 - t0 << "\n";
819
820 // Have to be cycling within the overlap
821 if( m_ampCircBuff.mono() - mono0 >= static_cast<uint32_t>(m_tsOverlapSize) )
822 {
823 log<text_log>( "PSD calculations getting behind, skipping ahead.", logPrio::LOG_WARNING );
824 }
825 else
826 {
827 while( m_ampCircBuff.mono() - mono0 < static_cast<uint32_t>(m_tsOverlapSize) )
828 {
829 mx::sys::microSleep( 0.2 * 1000000.0 / m_fps );
830 }
831 }
832
833 ne1 = m_ampCircBuff.latest();
834 if( ne1 > m_tsSize )
835 {
836 ne1 -= m_tsSize;
837 }
838 else
839 {
840 ne1 = m_ampCircBuff.size() + ne1 - m_tsSize;
841 }
842 }
843 }
844}
845
846INDI_NEWCALLBACK_DEFN( modalPSDs, m_indiP_psdTime )( const pcf::IndiProperty &ipRecv )
847{
848 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_psdTime, ipRecv );
849
850 realT target;
851
852 if( indiTargetUpdate( m_indiP_psdTime, target, ipRecv, true ) < 0 )
853 {
854 log<software_error>( { __FILE__, __LINE__ } );
855 return -1;
856 }
857
858 if( m_psdTime != target )
859 {
860 std::lock_guard<std::mutex> guard( m_indiMutex );
861
862 m_psdTime = target;
863
864 updateIfChanged( m_indiP_psdTime, "current", m_psdTime, INDI_IDLE );
865 updateIfChanged( m_indiP_psdTime, "target", m_psdTime, INDI_IDLE );
866
867 shmimMonitorT::m_restart = true;
868
869 log<text_log>( "set psdTime to " + std::to_string( m_psdTime ), logPrio::LOG_NOTICE );
870 }
871
872 return 0;
873} // INDI_NEWCALLBACK_DEFN(modalPSDs, m_indiP_psdTime)
874
875INDI_NEWCALLBACK_DEFN( modalPSDs, m_indiP_psdAvgTime )( const pcf::IndiProperty &ipRecv )
876{
877 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_psdAvgTime, ipRecv );
878
879 realT target;
880
881 if( indiTargetUpdate( m_indiP_psdAvgTime, target, ipRecv, true ) < 0 )
882 {
883 log<software_error>( { __FILE__, __LINE__ } );
884 return -1;
885 }
886
887 if( m_psdAvgTime != target )
888 {
889 std::lock_guard<std::mutex> guard( m_indiMutex );
890
891 m_psdAvgTime = target;
892
893 updateIfChanged( m_indiP_psdTime, "current", m_psdAvgTime, INDI_IDLE );
894 updateIfChanged( m_indiP_psdTime, "target", m_psdAvgTime, INDI_IDLE );
895
896 log<text_log>( "set psdAvgTime to " + std::to_string( m_psdAvgTime ), logPrio::LOG_NOTICE );
897 }
898
899 return 0;
900} // INDI_NEWCALLBACK_DEFN(modalPSDs, m_indiP_psdAvgTime)
901
902INDI_SETCALLBACK_DEFN( modalPSDs, m_indiP_fpsSource )( const pcf::IndiProperty &ipRecv )
903{
904 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_fpsSource, ipRecv );
905
906 if( ipRecv.find( m_fpsElement ) != true ) // this isn't valid
907 {
908 log<software_error>( { __FILE__, __LINE__, "No current property in fps source." } );
909 return 0;
910 }
911
912 std::lock_guard<std::mutex> guard( m_indiMutex );
913
914 realT fps = ipRecv[m_fpsElement].get<realT>();
915
916 if( fabs( fps - m_fps ) > m_fpsTol )
917 {
918 m_fps = fps;
919 log<text_log>( "set fps to " + std::to_string( m_fps ), logPrio::LOG_NOTICE );
920 updateIfChanged( m_indiP_fps, "current", m_fps, INDI_IDLE );
921 shmimMonitorT::m_restart = true;
922 }
923
924 return 0;
925
926} // INDI_SETCALLBACK_DEFN(modalPSDs, m_indiP_fpsSource)
927
928} // namespace app
929} // namespace MagAOX
930
931#endif // modalPSDs_hpp
#define IMAGESTRUCT_FLOAT
#define XWCAPP_THREAD_CHECK(thrdSt, thrdName)
Error handling wrapper for checking on thread status with tryjoin.
#define XWCAPP_THREAD_START(thrdSt, thrdInit, thrdId, thrdProp, thrdPrio, thrdCpuset, thrdName, thrdStart)
Error handling wrapper for the threadStart function of the XWCApp.
#define XWCAPP_THREAD_STOP(thrdSt)
Error handlng wrapper for stopping a thread.
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.
int shutdown()
Get the value of the shutdown flag.
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
std::mutex m_indiMutex
Mutex for locking INDI communications.
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:42
realT m_psdOverlapFraction
The fraction of the sample time to overlap by.
Definition modalPSDs.hpp:78
pcf::IndiProperty m_psdThreadProp
The property to hold the PSD Calculation thread details.
pcf::IndiProperty m_indiP_psdAvgTime
INDI_NEWCALLBACK_DECL(modalPSDs, m_indiP_overSize)
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
pid_t m_psdThreadID
PSD Calculation thread PID.
mx::improc::eigenImage< realT > m_psdBuffer
std::string m_fpsElement
Element name for getting fps to set circular buffer length.
Definition modalPSDs.hpp:68
std::thread m_psdThread
The PSD Calculation thread.
pcf::IndiProperty m_indiP_overSize
std::complex< realT > * m_fftWork
INDI_NEWCALLBACK_DECL(modalPSDs, m_indiP_psdTime)
realT m_fpsTol
The tolerance for detecting a change in FPS.
Definition modalPSDs.hpp:70
static void psdThreadStart(modalPSDs *p)
PS Calculation thread starter function.
cbIndexT m_tsSize
The length of the time series sample over which the PSD is calculated.
Definition modalPSDs.hpp:94
std::vector< realT > m_win
The window function. By default this is Hann.
virtual int appStartup()
Startup function.
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.
size_t m_nModes
the number of modes to calculate PSDs for.
Definition modalPSDs.hpp:84
IMAGE * m_rawpsdStream
The ImageStreamIO shared memory buffer to hold the raw psds.
mx::sigproc::circularBufferIndex< realT *, cbIndexT > ampCircBuffT
The amplitude circular buffer type.
Definition modalPSDs.hpp:59
mx::math::ft::fftwEnvironment< realT > m_fftEnv
modalPSDs()
Default c'tor.
virtual void loadConfig()
std::string m_fpsDevice
Device name for getting fps to set circular buffer length.
Definition modalPSDs.hpp:66
pcf::IndiProperty m_indiP_fps
virtual int appLogic()
Implementation of the FSM for modalPSDs.
cbIndexT m_meanSize
The length of the time series over which to calculate the mean.
Definition modalPSDs.hpp:98
int processImage(void *curr_src, const dev::shmimT &dummy)
IMAGE * m_freqStream
The ImageStreamIO shared memory buffer to hold the frequency scale.
mx::math::ft::fftT< realT, std::complex< realT >, 1, 0 > m_fft
realT m_psdAvgTime
The time over which to average PSDs. The default is 10 sec.
Definition modalPSDs.hpp:73
virtual void setupConfig()
ampCircBuffT m_ampCircBuff
Definition modalPSDs.hpp:86
int m_psdThreadPrio
Priority of the PSD Calculation thread.
std::string m_psdThreadCpuset
The cpuset to use for the PSD Calculation thread.
std::complex< realT > complexT
Definition modalPSDs.hpp:51
int allocate(const dev::shmimT &dummy)
int32_t cbIndexT
The index for the circular buffer.
Definition modalPSDs.hpp:53
cbIndexT m_tsOverlapSize
The number of samples in the overlap.
Definition modalPSDs.hpp:96
pcf::IndiProperty m_indiP_psdTime
pcf::IndiProperty m_indiP_fpsSource
realT m_psdTime
The length of time over which to calculate PSDs. The default is 1 sec.
Definition modalPSDs.hpp:72
void psdThreadExec()
PSD Calculation thread function.
bool m_psdThreadInit
Initialization flag for the PSD Calculation thread.
std::string m_fpsProperty
Property name for getting fps to set circular buffer length.
Definition modalPSDs.hpp:67
virtual int appShutdown()
Shutdown the app.
~modalPSDs() noexcept
D'tor, declared and defined for noexcept.
std::vector< realT > m_psd
dev::shmimMonitor< modalPSDs > shmimMonitorT
The base shmimMonitor type.
Definition modalPSDs.hpp:56
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#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...
#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...
#define INDI_SETCALLBACK_DEFN(class, prop)
Define the callback for a set property request.
#define REG_INDI_SETPROP(prop, devName, propName)
Register a SET INDI property with the class, using the standard callback name.
#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.
@ OPERATING
The device is operating, other than homing.
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
#define INDI_IDLE
Definition indiUtils.hpp:27
const pcf::IndiProperty & ipRecv
updateIfChanged(m_indiP_angle, "target", m_angle)
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition dm.hpp:24
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
static constexpr logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
#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.
Software CRITICAL log entry.