10#include "../../libMagAOX/libMagAOX.hpp"
11#include "../../magaox_git_version.h"
149 std::chrono::time_point<std::chrono::high_resolution_clock>
m_time_start;
449 return static_cast<double>( t.tv_sec ) * 1
e9 +
static_cast<double>( t.tv_nsec );
457 t.tv_nsec =
static_cast<long>(
ns -
static_cast<double>( t.tv_sec ) * 1
e9 );
459 if( t.tv_nsec >= 1000000000L )
461 t.tv_nsec -= 1000000000L;
467 t.tv_nsec += 1000000000L;
547 config.add(
"fps.device",
555 "Device name for getting fps to set circular buffer length." );
557 config.add(
"fps.property",
565 "Property name for getting fps to set circular buffer length. Default is 'fps'." );
567 config.add(
"fps.element",
575 "Property name for getting fps to set circular buffer length. Default is 'current'." );
577 config.add(
"fps.tol",
585 "Tolerance for detecting a change in FPS. Default is 0." );
587 config.add(
"synchro.shmimName",
595 "The ImageStreamIO stream used to synchronize acquisition. Default is timer-driven operation." );
597 config.add(
"synchro.postDelay",
605 "Signed phase offset in microseconds added to synchronized delay-model predictions. Default is 0." );
607 config.add(
"synchro.dtTransfer_ns",
609 "synchro.dtTransfer_ns",
615 "Transfer-latency term in nanoseconds for synchronized delay modeling. Default is 3000." );
617 config.add(
"synchro.wfsProcess_ns",
619 "synchro.wfsProcess_ns",
625 "WFS processing-latency term in nanoseconds for synchronized delay modeling. Default is 51500." );
627 config.add(
"synchro.dtF_ns",
635 "Filter/transport-latency term in nanoseconds for synchronized delay modeling. Default is 10000." );
637 config.add(
"synchro.wfsRead_ns",
639 "synchro.wfsRead_ns",
645 "WFS read-latency term in nanoseconds for synchronized delay modeling. Default is 276100." );
647 config.add(
"synchro.delayLockAbsThreshold_ns",
649 "synchro.delayLockAbsThreshold_ns",
652 "delayLockAbsThreshold_ns",
655 "Absolute synchronized phase-error threshold in nanoseconds for delay-lock diagnostics. Default is 50000." );
657 config.add(
"synchro.delayLockFracThreshold",
659 "synchro.delayLockFracThreshold",
662 "delayLockFracThreshold",
665 "Fractional synchronized phase-error threshold for delay-lock diagnostics. Default is 0.1." );
667 config.add(
"synchro.cadenceGuard_ns",
669 "synchro.cadenceGuard_ns",
675 "Reserved nanoseconds in each synchronized cycle for non-delay work. Default is 20000." );
677 config.add(
"synchro.alpha",
685 "Global EMA coefficient for synchronized timing smoothers. Default is 0.01." );
687 config.add(
"numChannels.device",
689 "numChannels.device",
695 "Setting the number of channels needed to readout accelerometers" );
697 config.add(
"numChannels.property",
699 "numChannels.property",
705 "Property name for getting numChannels to set circular buffer length. Default is 'numChannels'." );
707 config.add(
"numChannels.element",
709 "numChannels.element",
715 "Property name for getting numChannels to set circular buffer length. Default is 'current'." );
717 config.add(
"numChannels.tol",
725 "Tolerance for detecting a change in numChannels. Default is 0." );
727 config.add(
"framegrabber.cpuset",
729 "framegrabber.cpuset",
735 "The cpuset to assign the framegrabber thread to." );
910 elevatedPrivileges
elPriv(
this );
1012 {
"avg_read_latency_us",
1014 "synchro_delay_target_us",
1017 "delay_phase_error_us",
1020 "avg_non_delay_service_us",
1022 "read_latency_error_us",
1023 "avg_semaphore_period_us",
1024 "channel_readout_us",
1025 "trigger_interval_us",
1028 "sync_frames_received",
1029 "sync_frames_written",
1030 "sync_frames_dropped",
1031 "sync_frame_id_gap_count",
1032 "sync_producer_frame_id",
1033 "sync_producer_frame_delta",
1196 m_time_start = std::chrono::high_resolution_clock::now();
1381 auto now = std::chrono::high_resolution_clock::now();
1399 const auto readStart = std::chrono::high_resolution_clock::now();
1407 const auto readEnd = std::chrono::high_resolution_clock::now();
1409 std::chrono::duration_cast<std::chrono::nanoseconds>(
readEnd -
readStart ).count() );
1417 mx::sys::nanoSleep( 1000 );
1603 const auto readStart = std::chrono::high_resolution_clock::now();
1612 const auto readEnd = std::chrono::high_resolution_clock::now();
1614 std::chrono::duration_cast<std::chrono::nanoseconds>(
readEnd -
readStart ).count() );
1712 if(
ipRecv.getName() != m_indiP_fps.getName() )
1714 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
1720 if( indiTargetUpdate( m_indiP_fps,
target,
ipRecv,
true ) < 0 )
1722 log<software_error>( { __FILE__, __LINE__ } );
1728 m_trigger = 1e9f / m_fps;
1729 nano_sec_target = 1e9f / m_fps;
1731 log<text_log>(
"set fps = " + std::to_string( m_fps ) );
1738 if(
ipRecv.getName() != m_indiP_alpha.getName() )
1740 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
1745 if( indiTargetUpdate( m_indiP_alpha,
target,
ipRecv,
true ) < 0 )
1747 log<software_error>( { __FILE__, __LINE__ } );
1762 log<text_log>(
"set alpha = " + std::to_string( m_alpha ) );
1769 if(
ipRecv.getName() != m_indiP_synchroDelay.getName() )
1771 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
1776 if( indiTargetUpdate( m_indiP_synchroDelay,
target,
ipRecv,
true ) < 0 )
1778 log<software_error>( { __FILE__, __LINE__ } );
1782 m_synchroPostDelay =
target;
1783 if( m_wfsPeriodMeasured_ns > 0.0 )
1785 double t_delay_ns = std::fmod( m_delayModel_ns + 1e3 *
static_cast<double>( m_synchroPostDelay ), m_wfsPeriodMeasured_ns );
1786 if( t_delay_ns < 0.0 )
1788 t_delay_ns += m_wfsPeriodMeasured_ns;
1791 m_synchroDelayTarget =
static_cast<float>( t_delay_ns );
1795 m_synchroDelayTarget = 1e3f *
static_cast<float>( m_synchroPostDelay );
1797 m_synchroDelay = m_synchroDelayTarget;
1798 m_delayApplied_ns =
static_cast<double>( m_synchroDelay );
1800 log<text_log>(
"set synchroDelay offset = " + std::to_string( m_synchroPostDelay ) +
" us" );
1808 if(
ipRecv.find( m_fpsElement ) !=
true )
1810 log<software_error>( { __FILE__, __LINE__,
"No current property in fps source." } );
1818 m_trigger = 1e9f / m_fps;
1819 nano_sec_target = 1e9f / m_fps;
1821 log<text_log>(
"set fps from " + m_fpsDevice +
" = " + std::to_string( m_fps ) );
1828 if(
ipRecv.getName() != m_indiP_numChannels.getName() )
1830 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
1836 if( indiTargetUpdate( m_indiP_numChannels, ch_target,
ipRecv,
true ) < 0 )
1838 log<software_error>( { __FILE__, __LINE__ } );
1842 m_numChannels = ch_target;
1844 log<text_log>(
"set numChannels = " + std::to_string( m_numChannels ));
1852 if(
ipRecv.find( m_numChannelsElement ) !=
true )
1854 log<software_error>( { __FILE__, __LINE__,
"No current property in numChannels source." } );
1858 float ch_target =
ipRecv[m_numChannelsElement].get<
float>();
1860 m_numChannels = ch_target;
1862 log<text_log>(
"set numChannels from " + m_numChannelsDevice +
" = " + std::to_string( m_numChannels ));
unsigned short read(const std::uint8_t channel, const Mode m=Mode::SINGLE) const
The base-class for XWCTk applications.
stateCodes::stateCodeT state()
Get the current state code.
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
timespec m_currImageTimestamp
The timestamp of the current image.
uint32_t m_width
The width of the image, once deinterlaced etc.
int recordFGTimings(bool force=false)
std::string m_fgCpuset
The cpuset to assign the framegrabber thread to. Not used if empty, the default.
uint8_t m_dataType
The ImageStreamIO type code.
bool m_reconfig
Flag to set if a camera reconfiguration requires a framegrabber reset.
uint32_t m_height
The height of the image, once deinterlaced etc.
bool m_firstTriggerTime
Tracks first-trigger initialization for synchronized trigger-interval measurement.
bool synchroStreamStale()
Check whether the synchronization stream has disappeared or been recreated.
bool m_firstTimerTrigger
Tracks first-trigger initialization for timer-mode trigger-interval measurement.
virtual int appStartup()
Start the application and initialize its INDI and hardware state.
int startAcquisition()
Prepare the selected acquisition mode to begin producing samples.
dev::frameGrabber< mcp3208Ctrl > frameGrabberT
float m_gain
The simple integrator gain used for timer and synchro delay control.
double m_nonDelayService_ns
Measured wake-to-return service time minus applied delay for the latest cycle.
uint64_t m_lastProducerCnt0
Previous producer frame counter from the synchronization stream metadata.
pcf::IndiProperty m_indiP_numChannels
float m_fpsTol
The tolerance used when monitoring fps metadata changes.
friend class mcp3208Ctrl_test
float nano_sec_target
The timer-mode target interval in nanoseconds.
double m_synchroDtF_ns
Filter/transport-latency term in the synchronized delay model.
void closeSynchroStream()
Release the synchronization semaphore claim and close the synchronization stream.
bool m_firstProducerSample
Tracks first-sample initialization for producer-period estimation.
uint64_t m_syncFramesDropped
Count of missing producer frame IDs inferred from positive counter gaps.
int loadImageIntoStream(void *dest)
Copy the current MCP3208 values into the output image buffer.
double m_delayLockAbsThreshold_ns
Absolute phase-error threshold for declaring delay lock.
float m_trigger
The timer-mode read interval in nanoseconds.
INDI_SETCALLBACK_DECL(mcp3208Ctrl, m_indiP_fpsSource)
Handle updates from the configured external fps source.
virtual int appShutdown()
Shutdown the application and release synchronization resources.
double m_delayPhaseError_ns
Wrapped phase error between applied and modeled synchronized delay.
void updateTriggerTiming(const timespec &atime)
Update synchronized trigger timing from the current semaphore arrival.
INDI_NEWCALLBACK_DECL(mcp3208Ctrl, m_indiP_synchroDelay)
Handle updates to the synchronized-mode signed phase offset property.
float m_synchroDelay
The commanded pre-read delay in synchronized mode, in nanoseconds.
double m_avgProducerPeriod_ns
Exponential moving-average producer period estimate from metadata.
int acquireTimerAndCheckValid()
Acquire one frame using the internal timer loop.
std::string m_synchroShmimName
The synchronization ImageStreamIO stream name; empty selects timer mode.
int loadConfigImpl(mx::app::appConfigurator &_config)
Load configuration values into the application state.
ino_t m_synchroStreamInode
Cached inode used to detect synchronization stream recreation.
pcf::IndiProperty m_indiP_fpsSource
INDI property subscription used to follow an external fps source.
virtual int appLogic()
Execute one iteration of the application FSM.
int checkRecordTimes()
Check whether framegrabber timing telemetry should be recorded this cycle.
bool m_firstNonDelayService
Tracks first-sample initialization for non-delay service-time averaging.
timespec m_lastProducerAtime
Previous producer atime from the synchronization stream metadata.
timespec m_lastTriggerTime
Previous trigger timestamp used to compute the synchronized trigger interval.
bool m_firstSemaphore
Tracks first-arrival initialization for semaphore period estimation.
void updateTimingDiagnosticsIndi()
Publish acquisition timing diagnostics to the INDI read-only property.
pcf::IndiProperty m_indiP_timingDiag
INDI property exposing runtime timing diagnostics for acquisition health checks.
uint64_t m_syncProducerFrameDelta
Latest producer-frame ID delta between consecutive synchronized wakes.
void delayBeforeRead()
Apply the current controlled delay between semaphore wake and ADC read.
double m_wfs_fps
WFS frame rate estimate used for timing prediction; initialized from configured fps before callbacks.
float fps()
Report the current acquisition rate metadata to the framegrabber.
virtual void loadConfig()
Load the configured application state.
int acquireSynchroAndCheckValid()
Acquire one frame using the synchronization semaphore.
int readChannelValue(int channel, uint16_t &value)
Read one MCP3208 channel value.
virtual void setupConfig()
Register configuration entries for the application and helper devices.
~mcp3208Ctrl() noexcept
Destroy the application.
int waitOnSemaphore(sem_t *sem, timespec &ts)
Wait on the claimed synchronization semaphore until the supplied timeout.
int m_numChannels
The number of MCP3208 channels read into each output frame.
double m_delayBudget_ns
Maximum delay allowed this cycle after cadence budgeting.
uint64_t m_localFrameSeq
Monotonic local acquisition sequence incremented on each published frame.
uint64_t m_syncFramesReceived
Count of synchronized frames received from semaphore wakes.
int numChannels()
Report the configured channel count metadata to the framegrabber.
float m_numChannelsTol
The tolerance for detecting a change in numChannels.
static constexpr bool c_frameGrabber_flippable
std::chrono::time_point< std::chrono::high_resolution_clock > m_time_start
The timer-mode reference point for the next internal acquisition cycle.
int getRealtime(timespec &ts)
Read the current realtime clock value.
double m_delayApplied_ns
Delay applied to the most recent synchronized ADC read.
int m_synchroSemaphoreNumber
The claimed semaphore slot for synchronization waits.
int recordTelem(const telem_fgtimings *telem)
Record framegrabber timing telemetry.
MCP3208Lib::MCP3208 m_adc
The active MCP3208 device interface used for hardware access.
std::string m_fpsElement
Property element containing the fps value.
dev::telemeter< mcp3208Ctrl > telemeterT
uint64_t m_syncFrameIdGapCount
Number of producer-frame gap events where ID delta exceeded one.
double m_synchroWfsRead_ns
WFS read-latency term in the synchronized delay model.
double m_synchroWfsProcess_ns
WFS processing-latency term in the synchronized delay model.
uint64_t m_lastSyncProducerFrameId
Previous producer frame ID used to detect synchronized frame-ID gaps.
pcf::IndiProperty m_indiP_fps
INDI property exposing the local fps target.
pcf::IndiProperty m_indiP_alpha
INDI property exposing the global EMA alpha used by timing smoothers.
double m_delayLockFracThreshold
Fractional period phase-error threshold for declaring delay lock.
pcf::IndiProperty m_indiP_numChannelsSource
uint64_t m_syncFramesWritten
Count of synchronized frames published to the output stream.
int configureAcquisition()
Configure the output image geometry and acquisition mode.
float m_alpha
Global exponential moving-average coefficient applied to timing smoothers.
int acquireAndCheckValid()
Acquire one sample and indicate whether it should be published.
double m_wfsPeriodMeasured_ns
Measured semaphore period used as the synchronized delay-model WFS period.
INDI_SETCALLBACK_DECL(mcp3208Ctrl, m_indiP_numChannelsSource)
INDI_NEWCALLBACK_DECL(mcp3208Ctrl, m_indiP_alpha)
Handle updates to the global EMA alpha property.
IMAGE m_synchroStream
The opened synchronization stream used for semaphore-triggered reads.
int reconfig()
Reset synchronization resources before the next acquisition configuration.
pcf::IndiProperty m_indiP_synchroDelay
INDI property exposing the synchronized-mode signed phase offset in microseconds.
static double timespecToNs(const timespec &t)
Convert a timespec timestamp to nanoseconds.
std::string m_numChannelsProperty
Property name for getting numChannels to set circular buffer length.
double m_producerPeriodInst_ns
Instantaneous producer period estimate from metadata timestamps.
bool m_firstReadLatency
Tracks first-arrival initialization for semaphore-to-read latency estimation.
double m_avgReadLatency_ns
Exponential moving-average estimate of semaphore-to-read latency in nanoseconds.
double m_avgSemaphorePeriod_ns
Exponential moving-average estimate of semaphore period in nanoseconds.
double m_cadenceGuard_ns
Reserved per-frame margin protecting synchronized cadence from overruns.
float m_synchroDelayTarget
The synchronized-mode effective delay target in nanoseconds after applying signed offset and wrap.
int m_synchroPostDelay
Signed microsecond phase offset added to synchronized delay-model predictions.
timespec m_triggerTime
Computed trigger timestamp aligned to the estimated WFS integration midpoint.
std::string m_fpsProperty
Property name providing external fps metadata.
INDI_NEWCALLBACK_DECL(mcp3208Ctrl, m_indiP_fps)
Handle updates to the local fps target property.
std::string m_numChannelsDevice
Device name for getting numChannels to set circular buffer length.
double m_delayModel_ns
Current modulo-wrapped delay predicted by the synchronized phase model.
float m_fps
The target acquisition rate in frames per second.
timespec m_atime
The most recent semaphore-arrival timestamp on the local realtime clock.
std::string m_numChannelsElement
Element name for getting numChannels to set circular buffer length.
int openSynchroStream()
Open the synchronization stream when semaphore-driven acquisition is enabled.
mcp3208Ctrl()
Construct the application with the current repository version metadata.
double m_avgNonDelayService_ns
Exponential moving-average of non-delay synchronized service time.
std::vector< uint16_t > m_values
The most recently read MCP3208 channel values published to the output stream.
timespec m_lastAtime
The previous semaphore-arrival timestamp used for period estimation.
sem_t * m_synchroSemaphore
Cached pointer to the claimed synchronization semaphore.
INDI_NEWCALLBACK_DECL(mcp3208Ctrl, m_indiP_numChannels)
double m_delayLock
Delay-lock state exported to diagnostics as 1.0 (locked) or 0.0 (unlocked).
uint64_t m_syncProducerFrameId
Latest producer frame ID (cnt0) observed on the synchronization stream.
double m_delayCapped
Delay-cap state exported as 1.0 when cadence budgeting limits the applied delay.
static timespec nsToTimespec(double ns)
Convert nanoseconds to a normalized timespec value.
int claimSynchroSemaphore()
Claim a semaphore slot from the synchronization stream.
void updateSynchroDelayController(double desiredDelay_ns)
Update the synchronized delay command with anti-windup and physical bounds.
MCP3208Lib::MCP3208 adc
Secondary MCP3208 handle retained with the legacy class state.
double m_channelReadoutTime_ns
Measured duration of reading all configured channels in nanoseconds.
std::string m_fpsDevice
Device name providing external fps metadata for framegrabber sizing.
double m_triggerInterval_ns
Measured interval between consecutive trigger events in nanoseconds.
bool m_synchroStreamOpen
Tracks whether the synchronization stream is currently open.
bool m_syncProducerFrameValid
Tracks whether producer frame-ID gap tracking has a valid prior sample.
double m_synchroDtTransfer_ns
Transfer-latency term in the synchronized delay model.
#define FRAMEGRABBER_SETUP_CONFIG(cfig)
Call frameGrabberT::setupConfig with error checking for frameGrabber.
#define FRAMEGRABBER_APP_LOGIC
Call frameGrabberT::appLogic with error checking for frameGrabber.
#define FRAMEGRABBER_APP_SHUTDOWN
Call frameGrabberT::appShutdown with error checking for frameGrabber.
#define FRAMEGRABBER_UPDATE_INDI
Call frameGrabberT::updateINDI with error checking for frameGrabber.
#define FRAMEGRABBER_LOAD_CONFIG(cfig)
Call frameGrabberT::loadConfig with error checking for frameGrabber.
#define FRAMEGRABBER_APP_STARTUP
Call frameGrabberT::appStartup with error checking for frameGrabber.
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#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.
const pcf::IndiProperty & ipRecv
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
static constexpr logPrioT LOG_INFO
Informational. The info log level is the lowest level recorded during normal operations.
A device base class which saves telemetry.
int telem(const typename telT::messageT &msg)
Make a telemetry recording.
Software CRITICAL log entry.
Log entry recording framegrabber timings.
#define TELEMETER_APP_LOGIC
Call telemeter::appLogic with error checking.
#define TELEMETER_LOAD_CONFIG(cfig)
Call telemeter::loadConfig with error checking.
#define TELEMETER_APP_STARTUP
Call telemeter::appStartup with error checking.
#define TELEMETER_SETUP_CONFIG(cfig)
Call telemeter::setupConfig with error checking.
#define TELEMETER_APP_SHUTDOWN
Call telemeter::appShutdown with error checking.