API
 
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 int m_loopNumber{ 1 };
67
68 std::string m_shmimBase; /**< The base name for the PSD shmims. If empty, the default,
69 then aolN where N is the loop number is used*/
70
71 std::string m_shmimTag{ "cl" }; /**< The tag to apply to the front of psds in the shmim name. Default is cl. */
72
73 std::string m_fpsDevice; ///< Device name for getting fps to set circular buffer length.
74 std::string m_fpsProperty{ "fps" }; ///< Property name for getting fps to set circular buffer length.
75 std::string m_fpsElement{ "current" }; ///< Element name for getting fps to set circular buffer length.
76
77 realT m_fpsTol{ 0 }; ///< The tolerance for detecting a change in FPS.
78
79 realT m_psdTime{ 1 }; ///< The length of time over which to calculate PSDs. The default is 1 sec.
80 realT m_psdAvgTime{ 10 }; ///< The time over which to average PSDs. The default is 10 sec.
81
82 // realT m_overSize {10}; ///< Multiplicative factor by which to oversize the circular buffer, to give good mean
83 // estimates and account for time-to-calculate.
84
85 realT m_psdOverlapFraction{ 0.5 }; ///< The fraction of the sample time to overlap by.
86
87 int m_nPSDHistory{ 100 }; //
88
89 ///@}
90
91 size_t m_nModes{ 0 }; ///< the number of modes to calculate PSDs for.
92
94
95 // std::vector<ampCircBuffT> m_ampCircBuffs;
96
98
99 realT m_df{ 1 };
100
101 cbIndexT m_tsSize{ 2000 }; ///< The length of the time series sample over which the PSD is calculated
102
103 cbIndexT m_tsOverlapSize{ 1000 }; ///< The number of samples in the overlap
104
105 cbIndexT m_meanSize{ 20000 }; ///< The length of the time series over which to calculate the mean
106
107 std::vector<realT> m_win; ///< The window function. By default this is Hann.
108
109 realT *m_tsWork{ nullptr };
110 size_t m_tsWorkSize{ 0 };
111
112 std::complex<realT> *m_fftWork{ nullptr };
113 size_t m_fftWorkSize{ 0 };
114
115 std::vector<realT> m_psd;
116
117 mx::math::ft::fftT<realT, std::complex<realT>, 1, 0> m_fft;
118 mx::math::ft::fftwEnvironment<realT> m_fftEnv;
119
120 /** \name PSD Calculation Thread
121 * Handling of offloads from the average woofer shape
122 * @{
123 */
124 int m_psdThreadPrio{ 0 }; ///< Priority of the PSD Calculation thread.
125
126 std::string m_psdThreadCpuset; ///< The cpuset to use for the PSD Calculation thread.
127
128 std::thread m_psdThread; ///< The PSD Calculation thread.
129
130 bool m_psdThreadInit{ true }; ///< Initialization flag for the PSD Calculation thread.
131
132 bool m_psdRestarting{ true }; /**< Synchronization flag. This will only become false
133 after a successful call to allocate.*/
134
135 bool m_psdWaiting{ false }; ///< Synchronization flag. This is set to true when the PSD thread is safely waiting
136 ///< for allocation to complete.
137
138 pid_t m_psdThreadID{ 0 }; ///< PSD Calculation thread PID.
139
140 pcf::IndiProperty m_psdThreadProp; ///< The property to hold the PSD Calculation thread details.
141
142 /// PS Calculation thread starter function
143 static void psdThreadStart( modalPSDs *p /**< [in] pointer to this */ );
144
145 /// PSD Calculation thread function
146 /** Runs until m_shutdown is true.
147 */
148 void psdThreadExec();
149
150 IMAGE *m_freqStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to hold the frequency scale
151
152 mx::improc::eigenImage<realT> m_psdBuffer;
153
154 IMAGE *m_rawpsdStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to hold the raw psds
155
156 IMAGE *m_avgpsdStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to hold the average psds
157
158 public:
159 /// Default c'tor.
160 modalPSDs();
161
162 /// D'tor, declared and defined for noexcept.
164 {
165 }
166
167 virtual void setupConfig();
168
169 /// Implementation of loadConfig logic, separated for testing.
170 /** This is called by loadConfig().
171 */
172 int loadConfigImpl( mx::app::appConfigurator &_config /**< [in] an application configuration
173 from which to load values*/ );
174
175 virtual void loadConfig();
176
177 /// Startup function
178 /**
179 *
180 */
181 virtual int appStartup();
182
183 /// Implementation of the FSM for modalPSDs.
184 /**
185 * \returns 0 on no critical error
186 * \returns -1 on an error requiring shutdown
187 */
188 virtual int appLogic();
189
190 /// Shutdown the app.
191 /**
192 *
193 */
194 virtual int appShutdown();
195
196 // shmimMonitor Interface
197 protected:
198 int allocate( const dev::shmimT &dummy /**< [in] tag to differentiate shmimMonitor parents.*/ );
199
200 int allocatePSDStreams();
201
202 int processImage( void *curr_src, ///< [in] pointer to start of current frame.
203 const dev::shmimT &dummy ///< [in] tag to differentiate shmimMonitor parents.
204 );
205
206 // INDI Interface
207 protected:
208 pcf::IndiProperty m_indiP_psdTime;
209 pcf::IndiProperty m_indiP_psdAvgTime;
210 pcf::IndiProperty m_indiP_overSize;
211 pcf::IndiProperty m_indiP_fpsSource;
212 pcf::IndiProperty m_indiP_fps;
213
214 public:
219};
220
221modalPSDs::modalPSDs() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
222{
223 return;
224}
225
227{
229
230 config.add( "loop.number",
231 "",
232 "loop.number",
233 argType::Required,
234 "loop",
235 "number",
236 false,
237 "string",
238 "Loop number, as in aolN" );
239
240 config.add( "psds.shmimBase",
241 "",
242 "psds.shmimBase",
243 argType::Required,
244 "psds",
245 "shmimBase",
246 false,
247 "string",
248 "The base name for the PSD shmims. If empty, the default, "
249 "then aolN where N is the loop number is used" );
250
251 config.add( "psds.shmimTag",
252 "",
253 "psds.shmimTag",
254 argType::Required,
255 "psds",
256 "shmimTag",
257 false,
258 "string",
259 "The tag to apply to the front of psds in the shmim name. Default is cl. " );
260
261 config.add( "circBuff.fpsDevice",
262 "",
263 "circBuff.fpsDevice",
264 argType::Required,
265 "circBuff",
266 "fpsDevice",
267 false,
268 "string",
269 "Device name for getting fps to set circular buffer length." );
270
271 config.add( "circBuff.fpsProperty",
272 "",
273 "circBuff.fpsProperty",
274 argType::Required,
275 "circBuff",
276 "fpsProperty",
277 false,
278 "string",
279 "Property name for getting fps to set circular buffer length. Default is 'fps'." );
280
281 config.add( "circBuff.fpsElement",
282 "",
283 "circBuff.fpsElement",
284 argType::Required,
285 "circBuff",
286 "fpsElement",
287 false,
288 "string",
289 "Property name for getting fps to set circular buffer length. Default is 'current'." );
290
291 config.add( "circBuff.fpsTol",
292 "",
293 "circBuff.fpsTol",
294 argType::Required,
295 "circBuff",
296 "fpsTol",
297 false,
298 "float",
299 "Tolerance for detecting a change in FPS. Default is 0." );
300
301 config.add( "circBuff.defaultFPS",
302 "",
303 "circBuff.defaultFPS",
304 argType::Required,
305 "circBuff",
306 "defaultFPS",
307 false,
308 "realT",
309 "Default FPS at startup, will enable changing average length with psdTime before INDI available." );
310
311 config.add( "circBuff.psdTime",
312 "",
313 "circBuff.psdTime",
314 argType::Required,
315 "circBuff",
316 "psdTime",
317 false,
318 "realT",
319 "The length of time over which to calculate PSDs. The default is 1 sec." );
320}
321
322int modalPSDs::loadConfigImpl( mx::app::appConfigurator &_config )
323{
325
326 _config( m_loopNumber, "loop.number" );
327
328 m_shmimBase = "aol" + std::to_string( m_loopNumber );
329 _config( m_shmimBase, "psds.shmimBase" );
330
331 _config( m_shmimTag, "psds.shmimTag" );
332
333 _config( m_fpsDevice, "circBuff.fpsDevice" );
334 _config( m_fpsProperty, "circBuff.fpsProperty" );
335 _config( m_fpsElement, "circBuff.fpsElement" );
336 _config( m_fpsTol, "circBuff.fpsTol" );
337
338 _config( m_psdTime, "circBuff.psdTime" );
339
340 return 0;
341}
342
344{
345 loadConfigImpl( config );
346}
347
349{
350 CREATE_REG_INDI_NEW_NUMBERF( m_indiP_psdTime, "psdTime", 0, 60, 0.1, "%0.1f", "PSD time", "PSD Setup" );
351 m_indiP_psdTime["current"].set( m_psdTime );
352 m_indiP_psdTime["target"].set( m_psdTime );
353
354 CREATE_REG_INDI_NEW_NUMBERU( m_indiP_psdAvgTime, "psdAvgTime", 0, 60, 0.1, "%0.1f", "PSD Avg. Time", "PSD Setup" );
355 m_indiP_psdAvgTime["current"].set( m_psdAvgTime );
356 m_indiP_psdAvgTime["target"].set( m_psdAvgTime );
357
358 if( m_fpsDevice == "" )
359 {
360 return log<software_critical, -1>(
361 { __FILE__, __LINE__, "FPS source is not configurated (circBuff.fpsDevice)" } );
362 }
363
365
366 CREATE_REG_INDI_RO_NUMBER( m_indiP_fps, "fps", "current", "Circular Buffer" );
367 m_indiP_fps.add( pcf::IndiElement( "current" ) );
368 m_indiP_fps["current"] = m_fps;
369
371
378 "psdcalc",
380
382
383 return 0;
384}
385
387{
389
390 XWCAPP_THREAD_CHECK( m_psdThread, "psdcalc" );
391
392 std::unique_lock<std::mutex> lock( m_indiMutex );
393
395
396 return 0;
397}
398
400{
401
403
405
406 if( m_rawpsdStream )
407 {
410 }
411
412 if( m_avgpsdStream )
413 {
416 }
417
418 if( m_freqStream )
419 {
422 }
423
424 if( m_tsWork )
425 {
427 }
428
429 if( m_fftWork )
430 {
432 }
433
434 return 0;
435}
436
438{
439 static_cast<void>( dummy );
440
441 m_psdRestarting = true;
442
443 // Wait for FPS to become not 0
444 // We wait indefinitely, the other process just might not be alive
445 bool logged = false;
446 while( m_fps <= 0 && !shutdown() )
447 {
448 if( !logged ) // log every thirty seconds
449 {
450 log<text_log>( "waiting for FPS...", logPrio::LOG_NOTICE );
451 logged = true;
452 }
453 mx::sys::sleep( 1 );
454 }
455
456 if( m_psdWaiting == false )
457 {
458 std::cerr << "PSD not waiting . . .\n";
459 }
460 // Prevent reallocation while the psd thread might be calculating
461 while( m_psdWaiting == false && !shutdown() )
462 {
463 mx::sys::microSleep( 100 );
464 }
465
466 std::cerr << "PSD waiting \n";
467
468 if( shutdown() )
469 {
470 return 0; // If shutdown() is true then shmimMonitor will cleanup
471 }
472
473 // Check for unsupported type (must be realT)
475 {
476 // must be a vector of size 1 on one axis
477 log<software_error>( { __FILE__, __LINE__, "unsupported data type: must be realT" } );
478 return -1;
479 }
480
481 // Check for unexpected format
483 {
484 // must be a vector of size 1 on one axis
485 log<software_error>( { __FILE__, __LINE__, "unexpected shmim format" } );
486 return -1;
487 }
488
489 std::cerr << "connected to " << shmimMonitorT::m_shmimName << " " << shmimMonitorT::m_width << " "
491
493
495
496 // Adjust length if odd to ensure we get the Nyquist frequency
497 if( m_tsSize % 2 == 1 )
498 {
499 m_tsSize += 1;
500 }
501
503
504 if( m_tsOverlapSize == 0 || !std::isnormal( m_tsOverlapSize ) )
505 {
507 { __FILE__, __LINE__, "bad m_tsOverlapSize value: " + std::to_string( m_tsOverlapSize ) } );
508 return -1;
509 }
510
512
513 if( static_cast<uint32_t>( m_meanSize ) > shmimMonitorT::m_depth )
514 {
515 log<software_error>( { __FILE__, __LINE__, "input circ buff is not long enough for psd avg. time" } );
517 }
518
519 // Size the circ buff
520 // we really want 2*m_meanSize but might not be able to
521 if( 2 * static_cast<uint32_t>( m_meanSize ) > shmimMonitorT::m_depth )
522 {
524 }
525 else
526 {
527 m_ampCircBuff.maxEntries( 2 * m_meanSize );
528 }
529
530 // Create the window
531 m_win.resize( m_tsSize );
532 mx::sigproc::window::hann( m_win );
533
534 // Set up the FFT and working memory
535 m_fft.plan( m_tsSize, mx::math::ft::dir::forward, false );
536
537 if( m_tsWork )
538 {
540 }
541 m_tsWork = mx::math::ft::fftw_malloc<realT>( m_tsSize );
542
543 if( m_fftWork )
544 {
546 }
547
548 m_fftWork = mx::math::ft::fftw_malloc<std::complex<realT>>( ( m_tsSize / 2 + 1 ) );
549
550 m_psd.resize( m_tsSize / 2 + 1 );
551
552 m_df = 1.0 / ( m_tsSize / m_fps );
553
554 // Create the shared memory images
555 uint32_t imsize[3];
556
557 // First the frequency
558 imsize[0] = 1;
559 imsize[1] = m_psd.size();
560 imsize[2] = 1;
561
562 if( m_freqStream )
563 {
566 }
567 m_freqStream = static_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
568
570 ( m_shmimBase + "_freq" ).c_str(),
571 3,
572 imsize,
574 -1,
575 1,
577 0,
579 0 );
580 m_freqStream->md->write = 1;
581 for( size_t n = 0; n < m_psd.size(); ++n )
582 {
583 m_freqStream->array.F[n] = n * m_df;
584 }
585
586 // Set the time of last write
587 clock_gettime( CLOCK_REALTIME, &m_freqStream->md->writetime );
588 m_freqStream->md->atime = m_freqStream->md->writetime;
589
590 // Update cnt1
591 m_freqStream->md->cnt1 = 0;
592
593 // Update cnt0
594 m_freqStream->md->cnt0 = 0;
595
596 m_freqStream->md->write = 0;
598
600
601 std::cerr << "done restarting\n";
602
603 m_psdRestarting = false;
604
605 return 0;
606}
607
609{
610 if( m_rawpsdStream )
611 {
614 }
615
616 uint32_t imsize[3];
617 imsize[0] = m_psd.size();
618 imsize[1] = m_nModes;
620
621 m_rawpsdStream = static_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
622
624 ( m_shmimBase + "_raw_" + m_shmimTag + "psds" ).c_str(),
625 3,
626 imsize,
628 -1,
629 1,
631 0,
633 0 );
634
635 if( m_avgpsdStream )
636 {
639 }
640
641 imsize[0] = m_psd.size();
642 imsize[1] = m_nModes;
643 imsize[2] = 1;
644
645 m_avgpsdStream = static_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
647 ( m_shmimBase + "_" + m_shmimTag + "psds" ).c_str(),
648 3,
649 imsize,
651 -1,
652 1,
654 0,
656 0 );
657
658 m_psdBuffer.resize( m_psd.size(), m_nModes );
659
660 return 0;
661}
662
663int modalPSDs::processImage( void *curr_src, const dev::shmimT &dummy )
664{
665 static_cast<void>( dummy );
666
667 float *f_src = static_cast<float *>( curr_src );
668
669 m_ampCircBuff.nextEntry( f_src );
670
671 return 0;
672}
673
675{
676 p->psdThreadExec();
677}
678
680{
682
683 while( m_psdThreadInit == true && shutdown() == 0 )
684 {
685 sleep( 1 );
686 }
687
688 while( shutdown() == 0 )
689 {
690 if( m_psdRestarting == true || m_ampCircBuff.maxEntries() == 0 )
691 {
692 m_psdWaiting = true;
693 }
694
695 while( ( m_psdRestarting == true || m_ampCircBuff.maxEntries() == 0 ) && !shutdown() )
696 {
697 mx::sys::microSleep( 100 );
698 }
699
700 if( shutdown() )
701 {
702 break;
703 }
704
705 m_psdWaiting = false;
706
707 if( m_ampCircBuff.maxEntries() == 0 )
708 {
709 log<software_error>( { __FILE__, __LINE__, "amp circ buff has zero size" } );
710 return;
711 }
712
713 std::cerr << "waiting to grow\n";
714 while( m_ampCircBuff.size() < m_ampCircBuff.maxEntries() && m_psdRestarting == false && !shutdown() )
715 {
716 // shrinking sleep
717 double stime = ( 1.0 * m_ampCircBuff.maxEntries() - 1.0 * m_ampCircBuff.size() ) / m_fps * 0.5 * 1e9;
718 mx::sys::nanoSleep( stime );
719 }
720
721 std::cerr << "all grown. starting to calculate\n";
722
723 ampCircBuffT::indexT ne0;
724 ampCircBuffT::indexT mne0;
725 ampCircBuffT::indexT ne1 = m_ampCircBuff.latest();
726 if( ne1 >= m_tsSize )
727 {
728 ne1 -= m_tsSize;
729 }
730 else
731 {
732 ne1 = m_ampCircBuff.size() + ne1 - m_tsSize;
733 }
734 // std::cerr << __LINE__ << " " << ne1 << " " << m_tsSize << " " << m_ampCircBuff.size() << '\n';
735
736 while( m_psdRestarting == false && !shutdown() )
737 {
738 // Used to check if we are getting too behind
740
741 // Calc PSDs here
742 ne0 = ne1;
743
744 if( ne0 >= m_meanSize )
745 {
746 mne0 = ne0 - m_meanSize;
747 }
748 else
749 {
750 mne0 = m_ampCircBuff.size() + ne0 - m_meanSize;
751 }
752
753 // std::cerr << "calculating: " << ne0 << " " << " " << m_tsSize << ' ' << m_ampCircBuff.size() << '\n';
754 // double t0 = mx::sys::get_curr_time();
755
756 for( size_t m = 0; m < m_nModes; ++m ) // Loop over each mode
757 {
758 // get mean going over avg time
759 realT mn = 0;
760 for( cbIndexT n = 0; n < m_meanSize; ++n )
761 {
762 mn += ( m_ampCircBuff.at( mne0, n ) )[m];
763 }
764 mn /= m_meanSize;
765
766 double var = 0;
767
768 for( cbIndexT n = 0; n < m_tsSize; ++n )
769 {
770 m_tsWork[n] = ( m_ampCircBuff.at( ne0, n )[m] - mn ); // load mean subtracted chunk
771
772 var += pow( m_tsWork[n], 2 );
773
774 m_tsWork[n] *= m_win[n];
775 }
776 var /= m_tsSize;
777
779
780 double nm = 0;
781 for( size_t n = 0; n < m_psd.size(); ++n )
782 {
783 m_psd[n] = norm( m_fftWork[n] );
784 nm += m_psd[n] * m_df;
785 }
786
787 // Put it in the buffer for uploading to shmim
788 for( size_t n = 0; n < m_psd.size(); ++n )
789 {
790 // m_psd[n] *= ( var / nm );
791 m_psdBuffer( n, m ) = m_psd[n] * ( var / nm );
792 }
793 }
794
795 //------------------------- the raw psds ---------------------------
796 m_rawpsdStream->md->write = 1;
797
798 // Set the time of last write
799 clock_gettime( CLOCK_REALTIME, &m_rawpsdStream->md->writetime );
800 m_rawpsdStream->md->atime = m_rawpsdStream->md->writetime;
801
802 uint64_t cnt1 = m_rawpsdStream->md->cnt1 + 1;
803 if( cnt1 >= m_rawpsdStream->md->size[2] )
804 {
805 cnt1 = 0;
806 }
807
808 // Move to next pointer
809 float *F = m_rawpsdStream->array.F + m_psdBuffer.rows() * m_psdBuffer.cols() * cnt1;
810
811 memcpy( F, m_psdBuffer.data(), m_psdBuffer.rows() * m_psdBuffer.cols() * sizeof( float ) );
812
813 // Update cnt1
814 m_rawpsdStream->md->cnt1 = cnt1;
815
816 // Update cnt0
817 ++m_rawpsdStream->md->cnt0;
818
819 m_rawpsdStream->md->write = 0;
821
822 //-------------------------- now average the psds ----------------------------
823
825
826 if( nPSDAverage <= 0 )
827 {
828 nPSDAverage = 1;
829 }
830 else if( static_cast<uint64_t>( nPSDAverage ) > m_rawpsdStream->md->size[2] )
831 {
832 nPSDAverage = m_rawpsdStream->md->size[2];
833 }
834
835 memcpy( m_psdBuffer.data(), F, m_psdBuffer.rows() * m_psdBuffer.cols() * sizeof( float ) );
836
837 for( int n = 1; n < nPSDAverage; ++n )
838 {
839 if( cnt1 == 0 )
840 {
841 cnt1 = m_rawpsdStream->md->size[2] - 1;
842 }
843 else
844 {
845 --cnt1;
846 }
847
848 F = m_rawpsdStream->array.F + m_psdBuffer.rows() * m_psdBuffer.cols() * cnt1;
849
850 m_psdBuffer += Eigen::Map<Eigen::Array<float, -1, -1>>( F, m_psdBuffer.rows(), m_psdBuffer.cols() );
851 }
852
854
855 m_avgpsdStream->md->write = 1;
856
857 // Set the time of last write
858 clock_gettime( CLOCK_REALTIME, &m_avgpsdStream->md->writetime );
859 m_avgpsdStream->md->atime = m_avgpsdStream->md->writetime;
860
861 // Move to next pointer
862 F = m_avgpsdStream->array.F;
863
864 memcpy( F, m_psdBuffer.data(), m_psdBuffer.rows() * m_psdBuffer.cols() * sizeof( float ) );
865
866 // Update cnt1
867 m_avgpsdStream->md->cnt1 = 0;
868
869 // Update cnt0
870 ++m_avgpsdStream->md->cnt0;
871
872 m_avgpsdStream->md->write = 0;
874
875 // double t1 = mx::sys::get_curr_time();
876 // std::cerr << "done " << t1 - t0 << "\n";
877
878 // Have to be cycling within the overlap
879 if( m_ampCircBuff.mono() - mono0 >= static_cast<uint32_t>( m_tsOverlapSize ) )
880 {
881 log<text_log>( "PSD calculations getting behind, skipping ahead.", logPrio::LOG_WARNING );
882 }
883 else
884 {
885 while( m_ampCircBuff.mono() - mono0 < static_cast<uint32_t>( m_tsOverlapSize ) && !m_psdRestarting )
886 {
887 mx::sys::microSleep( 0.2 * 1000000.0 / m_fps );
888 }
889 }
890
891 if( m_psdRestarting )
892 {
893 continue;
894 }
895
896 ne1 = m_ampCircBuff.latest();
897 if( ne1 > m_tsSize )
898 {
899 ne1 -= m_tsSize;
900 }
901 else
902 {
903 ne1 = m_ampCircBuff.size() + ne1 - m_tsSize;
904 }
905 }
906 }
907}
908
909INDI_NEWCALLBACK_DEFN( modalPSDs, m_indiP_psdTime )( const pcf::IndiProperty &ipRecv )
910{
911 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_psdTime, ipRecv );
912
913 realT target;
914
915 if( indiTargetUpdate( m_indiP_psdTime, target, ipRecv, true ) < 0 )
916 {
917 log<software_error>( { __FILE__, __LINE__ } );
918 return -1;
919 }
920
921 if( m_psdTime != target )
922 {
923 std::lock_guard<std::mutex> guard( m_indiMutex );
924
925 m_psdTime = target;
926
927 updateIfChanged( m_indiP_psdTime, "current", m_psdTime, INDI_IDLE );
928 updateIfChanged( m_indiP_psdTime, "target", m_psdTime, INDI_IDLE );
929
930 shmimMonitorT::m_restart = true;
931
932 log<text_log>( "set psdTime to " + std::to_string( m_psdTime ), logPrio::LOG_NOTICE );
933 }
934
935 return 0;
936} // INDI_NEWCALLBACK_DEFN(modalPSDs, m_indiP_psdTime)
937
938INDI_NEWCALLBACK_DEFN( modalPSDs, m_indiP_psdAvgTime )( const pcf::IndiProperty &ipRecv )
939{
940 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_psdAvgTime, ipRecv );
941
942 realT target;
943
944 if( indiTargetUpdate( m_indiP_psdAvgTime, target, ipRecv, true ) < 0 )
945 {
946 log<software_error>( { __FILE__, __LINE__ } );
947 return -1;
948 }
949
950 if( m_psdAvgTime != target )
951 {
952 std::lock_guard<std::mutex> guard( m_indiMutex );
953
954 m_psdAvgTime = target;
955
956 updateIfChanged( m_indiP_psdAvgTime, "current", m_psdAvgTime, INDI_IDLE );
957 updateIfChanged( m_indiP_psdAvgTime, "target", m_psdAvgTime, INDI_IDLE );
958
959 log<text_log>( "set psdAvgTime to " + std::to_string( m_psdAvgTime ), logPrio::LOG_NOTICE );
960 }
961
962 return 0;
963} // INDI_NEWCALLBACK_DEFN(modalPSDs, m_indiP_psdAvgTime)
964
965INDI_SETCALLBACK_DEFN( modalPSDs, m_indiP_fpsSource )( const pcf::IndiProperty &ipRecv )
966{
967 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_fpsSource, ipRecv );
968
969 if( ipRecv.find( m_fpsElement ) != true ) // this isn't valid
970 {
971 log<software_error>( { __FILE__, __LINE__, "No current property in fps source." } );
972 return 0;
973 }
974
975 std::lock_guard<std::mutex> guard( m_indiMutex );
976
977 realT fps = ipRecv[m_fpsElement].get<realT>();
978
979 if( fabs( fps - m_fps ) > m_fpsTol + .0001)
980 {
981 m_fps = fps;
982 log<text_log>( "set fps to " + std::to_string( m_fps ), logPrio::LOG_NOTICE );
983 updateIfChanged( m_indiP_fps, "current", m_fps, INDI_IDLE );
984
985 shmimMonitorT::m_restart = true;
986 }
987
988 return 0;
989
990} // INDI_SETCALLBACK_DEFN(modalPSDs, m_indiP_fpsSource)
991
992} // namespace app
993} // namespace MagAOX
994
995#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
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.
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:85
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:75
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:77
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.
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:91
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:73
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.
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:80
virtual void setupConfig()
ampCircBuffT m_ampCircBuff
Definition modalPSDs.hpp:93
int m_psdThreadPrio
Priority of the PSD Calculation thread.
std::string m_psdThreadCpuset
The cpuset to use for the PSD Calculation thread.
std::string m_shmimBase
Definition modalPSDs.hpp:68
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.
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:79
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:74
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:26
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.