11#include "../../libMagAOX/libMagAOX.hpp"
12#include "../../magaox_git_version.h"
16#include <mx/math/gslInterpolator.hpp>
17#include <mx/ioutils/readColumns.hpp>
94 mx::math::gslInterpolator<mx::math::gsl_interp_linear<double>>
96 mx::math::gslInterpolator<mx::math::gsl_interp_linear<double>>
141 mx::app::appConfigurator &
_config );
258 config.add(
"adcs.lookupFile",
266 "The name of the file, in the calib directory, containing the adc lookup table. Default is "
267 "'adc_lookup_table.txt'." );
269 config.add(
"adcs.adc1zero",
277 "The starting point for ADC 1. Default is 0." );
279 config.add(
"adcs.adc1lupsign",
287 "The sign to apply for the LUP values for ADC 1. Default is +1." );
289 config.add(
"adcs.adc2zero",
297 "The starting point for ADC 2. Default is 0." );
299 config.add(
"adcs.adc2lupsign",
307 "The sign to apply for the LUP values for ADC 2. Default is +1." );
309 config.add(
"adcs.deltaAngle",
317 "The offset angle to apply to the looked-up values, applied to both. Default is 0." );
319 config.add(
"adcs.adc1delta",
327 "The offset angle to apply to the looked-up value for ADC 1, applied in addition to deltaAngle. "
330 config.add(
"adcs.adc2delta",
338 "The offset angle to apply to the looked-up value for ADC 2, applied in addition to deltaAngle. "
341 config.add(
"adcs.minZD",
349 "The minimum zenith distance at which to interpolate and move the ADCs. Default is 5.1" );
351 config.add(
"adcs.adc1DevName",
359 "The device name of the ADC 1 stage. Default is 'stageadc1'" );
361 config.add(
"adcs.adc2DevName",
369 "The device name of the ADC 2 stage. Default is 'stageadc2'" );
371 config.add(
"tcs.devName",
379 "The device name of the TCS Interface providing 'teldata.zd'. Default is 'tcsi'" );
381 config.add(
"tracking.updateInterval",
383 "tracking.updateInterval",
389 "The interval at which to update positions, in seconds. Default is 10 secs." );
429 mx::error_t::noerror )
448 return log<
software_critical, -1>(
"non-finite lookup table value at row " + std::to_string(
n ) +
" in " +
454 return log<
software_critical, -1>(
"lookup table zenith distances must be strictly increasing in " +
460 " entries spanning ZD 0 to " + std::to_string(
m_lupZD.back() ) );
466 catch(
const std::exception &
e )
552 const double now = mx::sys::get_curr_time();
554 bool tracking =
false;
572 if( !
lock.owns_lock() )
618 if( !std::isfinite( zd ) )
632 else if( zd >=
minZD )
639 catch(
const std::exception &
e )
657 if( !std::isfinite(
adc1 ) || !std::isfinite(
adc2 ) )
706 return static_cast<float>( std::fabs(
m_terpADC1( zd ) ) );
711 return static_cast<float>( std::fabs(
m_terpADC2( zd ) ) );
716 return ipRecv[
"zd"].get<
float>();
721 if( !prop.find(
"state" ) )
726 pcf::IndiElement::SwitchStateType
newVal =
on ? pcf::IndiElement::On : pcf::IndiElement::Off;
734 prop[
"state"].setSwitchState(
newVal );
735 prop.setState(
state );
736 prop.setTimeStamp( pcf::TimeStamp() );
748 if(
haveZD && std::isfinite( zd ) )
754 else if( zd >
maxZD )
781 log<text_log>(
"ADC tracker below minZD: zd=" + std::to_string( zd ) +
" minZD=" + std::to_string(
minZD ),
786 log<text_log>(
"ADC tracker above maxZD: zd=" + std::to_string( zd ) +
" maxZD=" + std::to_string(
maxZD ),
797 if( !
ipRecv.find(
"toggle" ) )
800 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On )
802 updateSwitchIfChanged( m_indiP_tracking,
"toggle", pcf::IndiElement::On,
INDI_IDLE );
805 std::lock_guard<std::mutex> guard( m_indiMutex );
810 log<text_log>(
"started ADC rotation tracking" );
814 updateSwitchIfChanged( m_indiP_tracking,
"toggle", pcf::IndiElement::Off,
INDI_IDLE );
817 std::lock_guard<std::mutex> guard( m_indiMutex );
820 m_lastLoggedZDLimitState = zdLimitState::unknown;
823 log<text_log>(
"stopped ADC rotation tracking" );
837 if( indiTargetUpdate( m_indiP_deltaAngle,
target,
ipRecv ) < 0 )
843 std::lock_guard<std::mutex> guard( m_indiMutex );
849 log<text_log>(
"set deltaAngle to " + std::to_string( m_deltaAngle ) );
862 if( indiTargetUpdate( m_indiP_deltaADC1,
target,
ipRecv ) < 0 )
868 std::lock_guard<std::mutex> guard( m_indiMutex );
874 log<text_log>(
"set deltaADC1 to " + std::to_string( m_adc1delta ) );
887 if( indiTargetUpdate( m_indiP_deltaADC2,
target,
ipRecv ) < 0 )
893 std::lock_guard<std::mutex> guard( m_indiMutex );
899 log<text_log>(
"set deltaADC2 to " + std::to_string( m_adc2delta ) );
915 if( indiTargetUpdate( m_indiP_minZD,
target,
ipRecv ) < 0 )
921 std::lock_guard<std::mutex> guard( m_indiMutex );
927 haveZD = m_lookupReady && m_haveZD;
930 log<text_log>(
"set minZD to " + std::to_string( m_minZD ) );
933 updateZDLimitState( haveZD, zd,
target, maxZD );
942 if( !
ipRecv.find(
"zd" ) )
946 bool lookupReady =
false;
954 catch(
const std::exception &e )
956 log<software_error>( std::string(
"exception reading teldata.zd: " ) + e.what() );
961 log<software_error>(
"unknown exception reading teldata.zd" );
965 if( !std::isfinite( zd ) )
967 log<software_error>(
"received non-finite teldata.zd" );
972 std::lock_guard<std::mutex> guard( m_indiMutex );
975 lookupReady = m_lookupReady;
980 updateZDLimitState( lookupReady, zd, minZD, maxZD );
997 static bool tracking =
false;
1001 static float minZD = 0;
The base-class for XWCTk applications.
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.
indiDriver< MagAOXApp > * m_indiDriver
The INDI driver wrapper. Constructed and initialized by execute, which starts and stops communication...
std::string m_calibDir
The path to calibration files for MagAOX.
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.
std::string configName()
Get the config name.
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)
bool m_tracking
True when automatic ADC updates are enabled.
virtual float interpolateADC1(float zd)
Interpolate the ADC 1 lookup-table value for a zenith distance.
int recordTelem(const telem_adctrack *)
pcf::IndiProperty m_indiP_deltaADC1
The user offset applied only to ADC 1.
virtual int appShutdown()
Shutdown the app.
zdLimitState m_zdLimitState
Current ZD limit state reflected in the status properties.
float m_maxZD
The maximum zenith distance represented by the loaded lookup table.
std::vector< double > m_lupZD
Lookup-table zenith-distance samples.
std::vector< double > m_lupADC1
Lookup-table ADC 1 offsets corresponding to m_lupZD.
pcf::IndiProperty m_indiP_belowMinZD
Status switch indicating that the current ZD is below minZD.
int recordADCTrack(bool force=false)
virtual void setupInterpolators()
Initialize the ADC lookup interpolators from the loaded lookup-table data.
virtual float extractZD(const pcf::IndiProperty &ipRecv)
Extract the zenith distance from the incoming TCS INDI property.
zdLimitState m_lastLoggedZDLimitState
Last ZD limit state already announced by appLogic threshold-crossing warnings.
void logZDLimitCrossing(zdLimitState state, float zd, float minZD, float maxZD)
Emit a one-time threshold-crossing warning when appLogic enters a new out-of-range state.
dev::telemeter< adcTracker > telemeterT
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
float m_deltaAngle
The offset angle to apply to the looked-up values, applied to both. Default is 0.
pcf::IndiProperty m_indiP_deltaADC2
The user offset applied only to ADC 2.
pcf::IndiProperty m_indiP_teldata
The subscribed TCS property providing zenith distance updates.
INDI_NEWCALLBACK_DECL(adcTracker, m_indiP_tracking)
Handle new tracking toggle requests.
INDI_NEWCALLBACK_DECL(adcTracker, m_indiP_deltaADC2)
Handle new ADC 2 delta-angle requests.
~adcTracker() noexcept
D'tor, declared and defined for noexcept.
std::string m_adc2DevName
The device name of the ADC 2 stage. Default is 'stageadc2'.
virtual int sendADC1Position(float adc1)
Send the ADC 1 target command.
INDI_SETCALLBACK_DECL(adcTracker, m_indiP_teldata)
Handle incoming telescope data updates.
pcf::IndiProperty m_indiP_tracking
The INDI toggle used to enable or disable tracking.
double m_lastUpdate
Timestamp of the last ADC command dispatched by the tracker.
void updateStatusSwitch(pcf::IndiProperty &prop, bool on)
Update a local status switch and publish it if INDI is active.
mx::math::gslInterpolator< mx::math::gsl_interp_linear< double > > m_terpADC1
ADC 1 interpolator built from the lookup table.
bool m_haveZD
True once at least one valid zenith distance has been received.
float m_zd
The most recent finite zenith distance received from the TCS interface.
pcf::IndiProperty m_indiP_deltaAngle
The shared user offset applied to both ADC targets.
std::string m_adc1DevName
The device name of the ADC 1 stage. Default is 'stageadc1'.
pcf::IndiProperty m_indiP_adc2pos
The outbound ADC 2 stage position command property.
@ aboveMax
The current ZD is above maxZD.
@ inRange
The current ZD is within the usable lookup-table range.
@ unknown
No valid range status is currently available.
@ belowMin
The current ZD is below minZD.
INDI_NEWCALLBACK_DECL(adcTracker, m_indiP_deltaAngle)
Handle new shared delta-angle requests.
zdLimitState updateZDLimitState(bool haveZD, float zd, float minZD, float maxZD)
Update the min/max ZD status properties and return the resulting ZD limit state.
friend class adcTracker_test
bool m_lookupReady
True once the ADC lookup table has been validated and the interpolators are ready.
virtual void setupConfig()
Set up configuration entries.
virtual float interpolateADC2(float zd)
Interpolate the ADC 2 lookup-table value for a zenith distance.
pcf::IndiProperty m_indiP_minZD
The user-configurable minimum zenith distance for interpolation.
std::string m_tcsDevName
The device name of the TCS interface providing 'teldata.zd'. Default is 'tcsi'.
pcf::IndiProperty m_indiP_aboveMaxZD
Status switch indicating that the current ZD is above maxZD.
mx::math::gslInterpolator< mx::math::gsl_interp_linear< double > > m_terpADC2
ADC 2 interpolator built from the lookup table.
float m_updateInterval
The interval at which to update positions, in seconds. Default is 10 secs.
float m_adc1zero
The starting point for ADC 1. Default is 0.
adcTracker()
Default c'tor.
virtual int appStartup()
Startup function.
virtual int sendADC2Position(float adc2)
Send the ADC 2 target command.
int m_adc1lupsign
The sign to apply to the lookup table value for ADC 1.
float m_adc2zero
The starting point for ADC 2. Default is 0.
pcf::IndiProperty m_indiP_adc1pos
The outbound ADC 1 stage position command property.
INDI_NEWCALLBACK_DECL(adcTracker, m_indiP_minZD)
Handle new minimum-zenith-distance requests.
int m_adc2lupsign
The sign to apply to the lookup table value for ADC 2.
std::vector< double > m_lupADC2
Lookup-table ADC 2 offsets corresponding to m_lupZD.
virtual int appLogic()
Implementation of the FSM for adcTracker.
float m_minZD
The minimum zenith distance at which to interpolate and move the ADCs. Default is 5....
INDI_NEWCALLBACK_DECL(adcTracker, m_indiP_deltaADC1)
Handle new ADC 1 delta-angle requests.
virtual void loadConfig()
Load configuration values.
#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_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_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_WARNING
A condition has occurred which may become an error, but the process continues.
A device base class which saves telemetry.
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
@ READY
The device is ready for operation, but is not operating.
Software CRITICAL log entry.
Log entry recording ADC tracker operator-adjustable state.
#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.