10#include "../../libMagAOX/libMagAOX.hpp"
11#include "../../magaox_git_version.h"
13#include <mx/math/fit/fitGaussian.hpp>
14#include <mx/improc/imageFilters.hpp>
82 std::unique_ptr<pcf::IndiProperty>
m_prop;
98 Star & operator=(
Star && ) noexcept = default;
105 throw std::runtime_error(
"attempt to access nullptr prop" );
114 return static_cast<bool>(
m_prop );
122 m_prop = std::make_unique<pcf::IndiProperty>();
181 mx::improc::eigenImage<float>
m_sm;
280 mx::math::fit::fitGaussian2Dsym<float>
m_gfit;
406 "Device name for getting fps if time-based averaging is used. This device should have *.fps.current." );
407 config.add(
"fitter.max_loops",
415 "Setting the number of stars to detect in processImage function." );
416 config.add(
"fitter.zero_area",
424 "Setting the pixel area to zero out after detecting stars in processImage function." );
425 config.add(
"fitter.threshold",
433 "setting how many sigma away from the mean you want to classify a detection." );
434 config.add(
"fitter.fwhm_threshold",
436 "fitter.fwhm_threshold",
442 "minumum fwhm to consider something a star." );
443 config.add(
"acquisition.x_center",
445 "acquisition.x_center",
451 "X value for 'center' of image." );
452 config.add(
"acquisition.y_center",
454 "acquisition.y_center",
460 "Y value for 'center' of image." );
474 "fitter.fwhm_threshold" );
596 static_cast<void>(
dummy );
623 static_cast<void>(
dummy );
646 const float *
src =
static_cast<const float *
>(
curr_src );
688 if( !std::isfinite(
stddev ) ||
stddev <= std::numeric_limits<float>::epsilon() )
694 if( !std::isfinite(
zScore ) )
743 if( !std::isfinite( fwhm ) || !std::isfinite(
maxValue ) )
751 if( !std::isfinite(
m_x ) || !std::isfinite(
m_y ) || !std::isfinite(
starSeeing ) )
807 if( !std::isfinite(
zScore ) )
871 if( !std::isfinite(
star.x ) || !std::isfinite(
star.y ) || !std::isfinite(
star.max ) ||
872 !std::isfinite(
star.fwhm ) || !std::isfinite(
star.seeing ) )
892 double now = mx::sys::get_curr_time();
906 pcf::IndiProperty
ip( pcf::IndiProperty::Number );
907 ip.setDevice(
"tcsi" );
908 ip.setName(
"pyrNudge" );
909 ip.add( pcf::IndiElement(
"y" ) );
911 ip.add( pcf::IndiElement(
"x" ) );
921 static_cast<void>(
dummy );
938 static_cast<void>(
dummy );
946 const float *
src =
static_cast<const float *
>(
curr_src );
976 int starNo =
static_cast<int>( index ) + 1;
1004 if( !std::isfinite(
star.x ) || !std::isfinite(
star.y ) || !std::isfinite(
star.max ) ||
1005 !std::isfinite(
star.fwhm ) || !std::isfinite(
star.seeing ) )
1020 if(
star.hasProp() )
1031 "Star " + std::to_string(
rankIndex ) +
" Properties",
1033 star.prop().add( pcf::IndiElement(
"x" ) );
1035 star.prop().add( pcf::IndiElement(
"y" ) );
1037 star.prop().add( pcf::IndiElement(
"peak" ) );
1038 star.prop()[
"peak"].set(
star.max );
1039 star.prop().add( pcf::IndiElement(
"fwhm" ) );
1040 star.prop()[
"fwhm"].set(
star.fwhm );
1047 if( !
star.hasProp() )
1073 const bool lhsFinite = std::isfinite( lhs.max );
1074 const bool rhsFinite = std::isfinite( rhs.max );
1076 if( lhsFinite != rhsFinite )
1083 return lhs.max > rhs.max;
1098 std::string
desiredName =
"star_" + std::to_string(
n );
1155 if(
ipRecv.find(
"out" ) !=
true )
1160 std::unique_lock<std::mutex>
lock( m_indiMutex );
1162 auto outState =
ipRecv[
"out"].getSwitchState();
1163 bool outIsOn = ( outState == pcf::IndiElement::On );
1164 bool outIsOff = ( outState == pcf::IndiElement::Off );
1166 if( !outIsOn && !outIsOff )
1171 if( m_flipAcqOutStateValid && m_flipAcqOutWasOn && outIsOff )
1176 m_flipAcqOutWasOn = outIsOn;
1177 m_flipAcqOutStateValid =
true;
1186 if(!
ipRecv.find(
"request"))
return 0;
1187 std::unique_lock<std::mutex>
lock(m_indiMutex);
1189 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
1194 else if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::Off)
1199 log<software_error>({__FILE__,__LINE__,
"switch state fall through."});
1207 if(!
ipRecv.find(
"toggle"))
return 0;
1208 std::unique_lock<std::mutex>
lock(m_indiMutex);
1211 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On)
1213 updateSwitchIfChanged(m_indiP_recordSeeing,
"toggle", pcf::IndiElement::On,
INDI_OK);
1216 else if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off)
1218 updateSwitchIfChanged(m_indiP_recordSeeing,
"toggle", pcf::IndiElement::Off,
INDI_IDLE);
1223 log<software_error>({__FILE__,__LINE__,
"switch state fall through."});
1230 if(
ipRecv.getName() != m_indiP_acquire_star.getName() )
1232 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
1238 if( indiTargetUpdate( m_indiP_acquire_star,
target,
ipRecv,
true ) < 0 )
1240 log<software_error>( { __FILE__, __LINE__ } );
1245 std::lock_guard<std::mutex> guard( m_indiMutex );
1246 m_acquire_star =
static_cast<int>(
target );
1249 log<text_log>(
"set acquire_star = " + std::to_string( m_acquire_star ),
logPrio::LOG_NOTICE );
1256 if(
ipRecv.getName() != m_indiP_seeing_star.getName() )
1258 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
1264 if( indiTargetUpdate( m_indiP_seeing_star,
target,
ipRecv,
true ) < 0 )
1266 log<software_error>( { __FILE__, __LINE__ } );
1270 static_cast<void>(
target );
1273 std::lock_guard<std::mutex> guard( m_indiMutex );
1276 if( m_num_stars > 0 )
1286 log<text_log>(
"set seeing_star = " + std::to_string( m_seeing_star ),
logPrio::LOG_NOTICE );
1292 if(
ipRecv.getName() != m_indiP_fpsSource.getName() )
1294 log<software_error>( { __FILE__, __LINE__,
"Invalid INDI property." } );
1298 if(
ipRecv.find(
"current" ) !=
true )
1303 std::lock_guard<std::mutex> guard( m_indiMutex );
1305 realT fps =
ipRecv[
"current"].get<
float>();
1310 shmimMonitorT::m_restart =
true;
#define IMAGESTRUCT_FLOAT
The base-class for XWCTk applications.
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI property element value if it has changed.
int createStandardIndiRequestSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single request element.
stateCodes::stateCodeT state()
Get the current state code.
int registerIndiPropertyNew(pcf::IndiProperty &prop, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for.
int createStandardIndiToggleSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single toggle element.
std::unordered_map< std::string, indiCallBack > m_indiNewCallBacks
Map to hold the NewProperty indiCallBacks for this App, with fast lookup by property name.
indiDriver< MagAOXApp > * m_indiDriver
The INDI driver wrapper. Constructed and initialized by execute, which starts and stops communication...
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
int createROIndiNumber(pcf::IndiProperty &prop, const std::string &propName, const std::string &propLabel="", const std::string &propGroup="")
Create a ReadOnly INDI Number property.
int registerIndiPropertyReadOnly(pcf::IndiProperty &prop)
Register an INDI property which is read only.
std::mutex m_indiMutex
Mutex for locking INDI communications.
int sendNewProperty(const pcf::IndiProperty &ipSend, const std::string &el, const T &newVal)
Send a newProperty command to another device (using the INDI Client interface)
int appStartup()
Startup function.
uint32_t m_width
The width of the images in the stream.
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
int updateINDI()
Update the INDI properties for this device controller.
int appLogic()
Checks the shmimMonitor thread.
uint32_t m_height
The height of the images in the stream.
int appShutdown()
Shuts down the shmimMonitor thread.
uint8_t m_dataType
The ImageStreamIO type code.
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
pcf::IndiProperty m_indiP_seeing_star
mx::improc::eigenImage< float > m_sm
double m_lastLoopExitTelemTime
Last successful loop-exit telemetry dump time in seconds.
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
virtual void loadConfig()
void registerStarProperty(Star &star, std::size_t rankIndex)
Register one tracked star's read-only INDI property using a rank-based label.
mx::improc::eigenImage< float > m_image
dev::shmimMonitor< psfAcq, darkShmimT > darkShmimMonitorT
int processImage(void *curr_src, const dev::shmimT &)
pcf::IndiProperty m_indiP_num_stars
float realT
Floating point type in which to do all calculations.
float fwhm
FWHM in pixels.
virtual int appLogic()
Implementation of the FSM for psfAcq.
std::vector< float > m_first_x_vals
pcf::IndiProperty m_indiP_flipAcqPresetName
Subscription to flipacq.presetName switch updates.
void removeStar(size_t index)
Remove one tracked star and its INDI property/callback registration.
int emitStarTelemetry(const std::vector< starTelemSample > &starTelemetryValues)
Emit one telemetry record per star sample.
uint16_t m_fitCircBuffMaxLength
Maximum length of the latency measurement circular buffers.
float y_pos
Star y position in pixels.
virtual int appStartup()
Startup function.
~psfAcq() noexcept
D'tor, declared and defined for noexcept.
int checkRecordTimes()
No-op scheduler hook; loop-exit logic emits telemetry directly.
mx::improc::eigenImage< float > m_dark
pcf::IndiProperty m_indiP_seeing
pcf::IndiProperty m_indiP_recordSeeing
std::size_t m_nextStarId
Monotonic id counter used to stamp new stars for stable ordering tie-breaks.
float seeing
Seeing estimate in arcseconds.
virtual void setupConfig()
int allocate(const dev::shmimT &)
virtual int appShutdown()
Shutdown the app.
mx::math::fit::fitGaussian2Dsym< float > m_gfit
int recordTelem(const telem_psfacq *telemTag)
Record telemetry for all properties of each detected star.
dev::shmimMonitor< psfAcq > shmimMonitorT
float m_fitCircBuffMaxTime
Maximum time of the latency meaurement circular buffers.
void relabelStarsByBrightness()
Sort tracked stars by brightness and ensure labels are star_0, star_1, ...
float m_pix
Peak pixel value.
pcf::IndiProperty m_indiP_fpsSource
void resetAcq()
Delete star INDI properties and reset tracked acquisition stars.
dev::telemeter< psfAcq > telemeterT
std::vector< float > m_first_y_vals
pcf::IndiProperty m_indiP_acquire_star
std::vector< Star > m_detectedStars
Tracked stars with associated INDI properties.
void unregisterStarProperty(Star &star)
Remove one tracked star's INDI property without erasing the star state.
bool m_flipAcqOutWasOn
Last observed flipacq.presetName.out switch state.
float x_pos
Star x position in pixels.
pcf::IndiProperty m_indiP_restartAcq
bool m_flipAcqOutStateValid
True once m_flipAcqOutWasOn has been initialized from INDI.
Snapshot of one star's telemetry fields for deferred emission.
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
#define INDI_SETCALLBACK_DECL(class, prop)
Declare the callback for a set property request, and declare and define the static wrapper.
#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 INDI_NEWCALLBACK_DECL(class, prop)
Declare the callback for a new property request, and declare and define the static wrapper.
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
const pcf::IndiProperty & ipRecv
float calculateDistance(float x1, float y1, float x2, float y2)
std::unique_lock< std::mutex > lock(m_indiMutex)
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.
bool hasProp() const
Check whether an INDI property has been allocated.
Star(const Star &)=delete
Disable copy constructor because the star owns a unique INDI property instance.
float x
Star row coordinate in image pixel space.
std::size_t id
Monotonic identifier used as a stable tie-breaker across equal-brightness stars.
void deallocate()
Release the star's INDI property.
Star(Star &&) noexcept=default
Enable move constructor.
Star & operator=(const Star &)=delete
Disable copy assignment because the star owns a unique INDI property instance.
int missedFrames
Consecutive frames where this star was not updated.
float y
Star column coordinate in image pixel space.
float max
Peak pixel value of the fitted star.
void allocate()
Allocate the star's INDI property if needed.
float seeing
Derived seeing value in arcseconds.
std::unique_ptr< pcf::IndiProperty > m_prop
Owned INDI property for this star's exported fit values.
pcf::IndiProperty & prop()
Access the owned INDI property.
float fwhm
FWHM returned by the Gaussian fit in pixels.
Star()=default
Default constructor.
static std::string configSection()
static std::string indiPrefix()
A device base class which saves telemetry.
@ OPERATING
The device is operating, other than homing.
Log entry recording psf acquisition per-star properties.
#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.
double variance(double data[], int size, int num_skip=0)