10#include "../../libMagAOX/libMagAOX.hpp"
11#include "../../magaox_git_version.h"
14#include <condition_variable>
15#include <mx/sigproc/circularBuffer.hpp>
16#include <mx/sigproc/signalWindows.hpp>
18#include <mx/math/ft/fftwEnvironment.hpp>
19#include <mx/math/ft/fftT.hpp>
61 typedef mx::sigproc::circularBufferIndex<realT *, cbIndexT, true>
ampCircBuffT;
132 mx::math::ft::fftT<realT, std::complex<realT>, 1, 0>
m_fft;
338 config.add(
"loop.number",
346 "Loop number, as in aolN" );
348 config.add(
"psds.shmimBase",
356 "The base name for the PSD shmims. If empty, the default, "
357 "then aolN where N is the loop number is used" );
359 config.add(
"psds.shmimTag",
367 "The tag to apply to the front of psds in the shmim name. Default is cl. " );
369 config.add(
"circBuff.fpsDevice",
371 "circBuff.fpsDevice",
377 "Device name for getting fps to set circular buffer length." );
379 config.add(
"circBuff.fpsProperty",
381 "circBuff.fpsProperty",
387 "Property name for getting fps to set circular buffer length. Default is 'fps'." );
389 config.add(
"circBuff.fpsElement",
391 "circBuff.fpsElement",
397 "Property name for getting fps to set circular buffer length. Default is 'current'." );
399 config.add(
"circBuff.fpsTol",
407 "Tolerance for detecting a change in FPS. Default is 0." );
409 config.add(
"circBuff.defaultFPS",
411 "circBuff.defaultFPS",
417 "Default FPS at startup, will enable changing average length with psdTime before INDI available." );
419 config.add(
"circBuff.loopStateDevice",
421 "circBuff.loopStateDevice",
427 "Optional device name providing loop-state gating. If unset, PSDs ignore loop state." );
429 config.add(
"circBuff.loopStateProperty",
431 "circBuff.loopStateProperty",
437 "Optional property name providing loop-state gating. Default is 'loop_state'." );
439 config.add(
"circBuff.loopStateElement",
441 "circBuff.loopStateElement",
447 "Element name interpreted as the closed-loop state. Default is 'toggle'." );
449 config.add(
"circBuff.psdTime",
457 "The length of time over which to calculate PSDs. The default is 1 sec." );
459 config.add(
"circBuff.psdAvgTime",
461 "circBuff.psdAvgTime",
467 "The length of time over which to average PSD estimates. The default is 10 sec." );
469 config.add(
"circBuff.meanTime",
477 "The length of time over which to calculate the detrending mean. The default is 10 sec." );
539 {
__FILE__,
__LINE__,
"FPS source is not configurated (circBuff.fpsDevice)" } );
627 static_cast<void>(
dummy );
634 while(
m_fps.load( std::memory_order_acquire ) <= 0 && !
shutdown() )
649 std::cerr <<
"PSD waiting \n";
677 realT fps =
m_fps.load( std::memory_order_acquire );
708 log<text_log>(
"input circ buff is not long enough for meanTime, truncating to " +
709 std::to_string(
static_cast<double>(
maxMeanSize ) / fps ) +
" sec",
721 mx::sigproc::window::hann(
m_win );
737 m_fftWork = mx::math::ft::fftw_malloc<std::complex<realT>>( (
m_tsSize / 2 + 1 ) );
770 for(
size_t n = 0;
n <
m_psd.size(); ++
n )
790 std::cerr <<
"done restarting\n";
865 static_cast<void>(
dummy );
933 std::cerr <<
"waiting to grow\n";
939 mx::sys::nanoSleep(
stime );
942 std::cerr <<
"all grown. starting to calculate\n";
946 prevSnap = ampCircBuffT::snapshotT();
957 ampCircBuffT::snapshotT
tsSnap;
1013 for(
size_t n = 0;
n <
m_psd.size(); ++
n )
1020 for(
size_t n = 0;
n <
m_psd.size(); ++
n )
1149 mx::sys::microSleep( 0.2 * 1000000.0 /
m_fps.load( std::memory_order_acquire ) );
1168 return sn.maxEntries +
sn.latest -
count;
1251 const std::vector<realT> &meanHeadCache,
1320 uint64_t windowCount,
1321 size_t planeElements )
const
1328 if(
plane ==
nullptr )
1338 const realT *addPlane,
1339 const realT *removePlane,
1340 size_t planeElements )
1430 if( indiTargetUpdate( m_indiP_psdTime,
target,
ipRecv,
true ) < 0 )
1432 log<software_error>( { __FILE__, __LINE__ } );
1436 if( m_psdTime.load( std::memory_order_acquire ) !=
target )
1438 std::lock_guard<std::mutex> guard( m_indiMutex );
1440 m_psdTime.store(
target, std::memory_order_release );
1445 shmimMonitorT::m_restart =
true;
1447 log<text_log>(
"set psdTime to " + std::to_string( m_psdTime.load() ),
logPrio::LOG_NOTICE );
1459 if( indiTargetUpdate( m_indiP_psdAvgTime,
target,
ipRecv,
true ) < 0 )
1461 log<software_error>( { __FILE__, __LINE__ } );
1465 if( m_psdAvgTime.load( std::memory_order_acquire ) !=
target )
1467 std::lock_guard<std::mutex> guard( m_indiMutex );
1469 m_psdAvgTime.store(
target, std::memory_order_release );
1474 shmimMonitorT::m_restart =
true;
1476 log<text_log>(
"set psdAvgTime to " + std::to_string( m_psdAvgTime.load() ),
logPrio::LOG_NOTICE );
1488 if( indiTargetUpdate( m_indiP_meanTime,
target,
ipRecv,
true ) < 0 )
1490 log<software_error>( { __FILE__, __LINE__ } );
1494 if( m_meanTime.load( std::memory_order_acquire ) !=
target )
1496 std::lock_guard<std::mutex> guard( m_indiMutex );
1498 m_meanTime.store(
target, std::memory_order_release );
1503 shmimMonitorT::m_restart =
true;
1505 log<text_log>(
"set meanTime to " + std::to_string( m_meanTime.load() ),
logPrio::LOG_NOTICE );
1515 if(
ipRecv.find( m_fpsElement ) !=
true )
1517 log<software_error>( { __FILE__, __LINE__,
"No current property in fps source." } );
1521 std::lock_guard<std::mutex> guard( m_indiMutex );
1523 realT fps =
ipRecv[m_fpsElement].get<realT>();
1525 if( fabs( fps - m_fps.load( std::memory_order_acquire ) ) > m_fpsTol + .0001 )
1527 m_fps.store( fps, std::memory_order_release );
1531 shmimMonitorT::m_restart =
true;
1542 if(
ipRecv.find( m_loopStateElement ) !=
true )
1544 log<software_error>( { __FILE__, __LINE__,
"No configured loop-state element in loop source." } );
1548 bool loopClosed = (
ipRecv[m_loopStateElement].getSwitchState() == pcf::IndiElement::On );
1550 if( loopClosed != m_loopClosed.load( std::memory_order_acquire ) )
1552 std::lock_guard<std::mutex> guard( m_indiMutex );
1554 m_loopClosed.store( loopClosed, std::memory_order_release );
1555 shmimMonitorT::m_restart =
true;
1557 log<text_log>( std::string(
"loop state is now " ) + ( loopClosed ?
"closed" :
"open" ),
logPrio::LOG_NOTICE );
#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 XWCTk applications.
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.
realT m_psdOverlapFraction
The fraction of the sample time to overlap by.
std::atomic< realT > m_psdAvgTime
The time over which to average PSD estimates. The default is 10 sec.
uint32_t m_rawPSDHistoryNext
Next overflow slot to overwrite in m_rawPSDHistory.
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)
void cacheMeanHead(std::vector< realT > &meanHeadCache, cbIndexT count) const
Cache the oldest slice of the currently loaded mean window for the next rolling-mean update.
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.
INDI_NEWCALLBACK_DECL(modalPSDs, m_indiP_meanTime)
std::thread m_psdThread
The PSD Calculation thread.
pcf::IndiProperty m_indiP_overSize
std::atomic< bool > m_loopClosed
Current closed-loop state used to gate PSD ingestion and processing.
INDI_SETCALLBACK_DECL(modalPSDs, m_indiP_loop)
std::complex< realT > * m_fftWork
INDI_NEWCALLBACK_DECL(modalPSDs, m_indiP_psdTime)
uint32_t publishedRawPSDHistoryDepth() const
Calculate the published raw PSD history depth retained in shared memory.
void recomputeMeanSums(std::vector< double > &meanSums) const
Recompute the per-mode sums for the full mean window from the currently loaded pointers.
realT m_fpsTol
The tolerance for detecting a change in FPS.
friend class modalPSDs_test
cbIndexT desiredMeanSampleCount(realT fps) const
Calculate how many samples are needed for the mean-subtraction window at the current FPS.
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.
cbIndexT requiredInputHistoryDepth() const
Calculate the total input-history depth needed to read both windows safely from the fixed-size circul...
std::string m_loopStateProperty
Optional property name providing loop-state gating updates.
virtual int appStartup()
Startup function.
void rollMeanSums(std::vector< double > &meanSums, const std::vector< realT > &meanHeadCache, cbIndexT advance) const
Update per-mode mean sums using the cached oldest slice and the newest loaded mean-window slice.
bool m_useLoopState
Whether PSD ingestion is gated by an external loop-state property.
std::condition_variable m_psdCond
Coordinates quiescence during restart.
INDI_SETCALLBACK_DECL(modalPSDs, m_indiP_fpsSource)
INDI_NEWCALLBACK_DECL(modalPSDs, m_indiP_psdAvgTime)
uint64_t m_rawPSDHistoryCount
Number of overflow PSD estimates stored since the last restart.
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.
std::string m_loopStateElement
Element name used to interpret closed-loop state.
std::atomic< realT > m_psdTime
The length of time over which to calculate PSDs. The default is 1 sec.
IMAGE * m_rawpsdStream
The ImageStreamIO shared memory buffer to hold the raw psds.
std::vector< realT * > m_tsPtrs
Snapshot of the latest time-series window pointers for PSD computation.
mx::math::ft::fftwEnvironment< realT > m_fftEnv
const realT * rawPSDPlaneByAge(uint64_t age, size_t planeElements) const
Locate a retained raw PSD plane by age, where age 0 is the newest plane.
std::atomic< realT > m_meanTime
The time over which to calculate the mean for detrending. The default is 10 sec.
modalPSDs()
Default c'tor.
static cbIndexT precedingWindowRefEntry(const ampCircBuffT::snapshotT &sn, cbIndexT refEntry, cbIndexT count)
Compute the reference entry for a logical window immediately preceding another logical window.
uint64_t storedRawPSDCount() const
Count how many raw PSD planes are currently retained across the published and overflow histories.
void recomputeAveragedPSDSum(std::vector< double > &avgPsdSum, uint64_t windowCount, size_t planeElements) const
Recompute the averaged-PSD running sum from the newest windowCount retained raw PSD planes.
mx::sigproc::circularBufferIndex< realT *, cbIndexT, true > ampCircBuffT
The amplitude circular buffer type.
virtual void loadConfig()
std::string m_fpsDevice
Device name for getting fps to set circular buffer length.
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.
static cbIndexT latestWindowRefEntry(const ampCircBuffT::snapshotT &sn, cbIndexT count)
Compute the reference entry for the latest logical window while preserving historical semantics.
mx::math::ft::fftT< realT, std::complex< realT >, 1, 0 > m_fft
std::vector< realT * > m_meanPtrs
Snapshot of the latest mean-window pointers for PSD computation.
virtual void setupConfig()
ampCircBuffT m_ampCircBuff
int m_psdThreadPrio
Priority of the PSD Calculation thread.
static void updatePlaneSum(std::vector< double > &planeSum, const realT *addPlane, const realT *removePlane, size_t planeElements)
Add one raw PSD plane and optionally subtract one outgoing raw PSD plane from the running average sum...
std::string m_psdThreadCpuset
The cpuset to use for the PSD Calculation thread.
std::complex< realT > complexT
int allocate(const dev::shmimT &dummy)
int m_nPSDHistory
Minimum number of raw PSD estimates to retain in the history stream.
std::mutex m_psdMutex
Protects restart handoff between allocate() and the PSD thread.
bool m_psdWaiting
Synchronization flag protected by m_psdMutex.
bool acceptLoopStateFrame() const
Determine whether incoming frames should currently be accepted into the PSD history.
int32_t cbIndexT
The index for the circular buffer.
cbIndexT m_tsOverlapSize
The number of samples in the overlap.
int desiredPSDAverageCount() const
Calculate how many raw PSD estimates are needed to cover the requested averaging time.
pcf::IndiProperty m_indiP_psdTime
std::string m_loopStateDevice
Optional device name providing loop-state gating updates.
bool loadPsdInputWindows(ampCircBuffT::snapshotT &sn)
Load the PSD and mean pointer windows from a single validated snapshot.
uint32_t m_rawPSDHistoryDepth
Number of overflow PSD estimates currently allocated in m_rawPSDHistory.
std::vector< realT > m_rawPSDHistory
Heap-backed overflow history for raw PSD estimates beyond the shmim depth.
static cbIndexT circularEntryAdvance(cbIndexT from, cbIndexT to, cbIndexT maxEntries)
Compute the forward logical advance between two circular-buffer reference entries.
pcf::IndiProperty m_indiP_loop
pcf::IndiProperty m_indiP_fpsSource
void psdThreadExec()
PSD Calculation thread function.
bool m_psdThreadInit
Initialization flag for the PSD Calculation thread.
uint32_t rawPSDHistoryDepth() const
Calculate the additional PSD history depth needed beyond the published raw-PSD shmim.
pcf::IndiProperty m_indiP_meanTime
std::atomic< bool > m_psdRestarting
std::string m_fpsProperty
Property name for getting fps to set circular buffer length.
std::atomic< realT > m_fps
The current input frame rate used to size PSD windows.
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.
#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.
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
const pcf::IndiProperty & ipRecv
updateIfChanged(m_indiP_angle, "target", m_angle)
std::unique_lock< std::mutex > lock(m_indiMutex)
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.
@ OPERATING
The device is operating, other than homing.
Software CRITICAL log entry.