LCOV - code coverage report
Current view: top level - apps/kTracker - kTracker.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 14.5 % 62 9
Test Date: 2026-04-15 19:34:29 Functions: 33.3 % 12 4

            Line data    Source code
       1              : /** \file kTracker.hpp
       2              :  * \brief The MagAO-X K-mirror rotation tracker header file
       3              :  *
       4              :  * \ingroup kTracker_files
       5              :  */
       6              : 
       7              : #ifndef kTracker_hpp
       8              : #define kTracker_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 <cmath>
      14              : 
      15              : #include <mx/math/gslInterpolation.hpp>
      16              : #include <mx/ioutils/readColumns.hpp>
      17              : 
      18              : /** \defgroup kTracker
      19              :  * \brief The MagAO-X application to track pupil rotation with the k-mirror.
      20              :  *
      21              :  * <a href="../handbook/operating/software/apps/kTracker.html">Application Documentation</a>
      22              :  *
      23              :  * \ingroup apps
      24              :  *
      25              :  */
      26              : 
      27              : /** \defgroup kTracker_files
      28              :  * \ingroup kTracker
      29              :  */
      30              : 
      31              : namespace MagAOX
      32              : {
      33              : namespace app
      34              : {
      35              : 
      36              : /// The MagAO-X K-mirror tracker.
      37              : /**
      38              :  * \ingroup kTracker
      39              :  */
      40              : class kTracker : public MagAOXApp<true>
      41              : {
      42              : 
      43              :     // Give the test harness access.
      44              :     friend class kTracker_test;
      45              : 
      46              :   protected:
      47              :     /** \name Configurable Parameters
      48              :      *@{
      49              :      */
      50              : 
      51              :     float m_zero{ 0 }; ///< The starting point for the K-mirror at zd = 0.
      52              : 
      53              :     int m_sign{ 1 }; ///< The sign to apply to the zenith distance to rotate the K-mirror.
      54              : 
      55              :     std::string m_devName{ "stagek" }; ///< The device name of the K-mirror stage.  Default is 'stagek'.
      56              :     std::string m_tcsDevName{
      57              :         "tcsi" }; ///< The device name of the TCS interface providing 'teldata.zd'.  Default is 'tcsi'.
      58              : 
      59              :     float m_updateInterval{ 10 }; ///< The interval at which to update positions, in seconds.  Default is 10 secs.
      60              : 
      61              :     ///@}
      62              : 
      63              :     bool m_tracking{ false }; ///< True when automatic K-mirror updates are enabled.
      64              : 
      65              :     float m_zd{ 0 }; ///< The most recent finite zenith distance received from the TCS interface.
      66              : 
      67              :     bool m_haveZD{ false }; ///< True once at least one valid zenith distance has been received.
      68              : 
      69              :     double m_lastUpdate{ 0 }; ///< Timestamp of the last stage command dispatched by the tracker.
      70              : 
      71              :   public:
      72              :     /// Default c'tor.
      73              :     kTracker();
      74              : 
      75              :     /// D'tor, declared and defined for noexcept.
      76            6 :     ~kTracker() noexcept
      77            6 :     {
      78            6 :     }
      79              : 
      80              :     /// Set up configuration entries.
      81              :     virtual void setupConfig();
      82              : 
      83              :     /// Implementation of loadConfig logic, separated for testing.
      84              :     /** This is called by loadConfig().
      85              :      */
      86              :     int loadConfigImpl(
      87              :         mx::app::appConfigurator &_config /**< [in] an application configuration from which to load values*/ );
      88              : 
      89              :     /// Load configuration values.
      90              :     virtual void loadConfig();
      91              : 
      92              :     /// Startup function
      93              :     /**
      94              :      *
      95              :      */
      96              :     virtual int appStartup();
      97              : 
      98              :     /// Implementation of the FSM for kTracker.
      99              :     /**
     100              :      * \returns 0 on no critical error
     101              :      * \returns -1 on an error requiring shutdown
     102              :      */
     103              :     virtual int appLogic();
     104              : 
     105              :     /// Shutdown the app.
     106              :     /**
     107              :      *
     108              :      */
     109              :     virtual int appShutdown();
     110              : 
     111              :     /** @name INDI
     112              :      *
     113              :      * @{
     114              :      */
     115              :   protected:
     116              :     pcf::IndiProperty m_indiP_tracking; ///< The INDI toggle used to enable or disable tracking.
     117              : 
     118              :     pcf::IndiProperty m_indiP_teldata; ///< The subscribed TCS property providing zenith distance updates.
     119              : 
     120              :     pcf::IndiProperty m_indiP_kpos; ///< The outbound K-mirror stage position command property.
     121              : 
     122              :   public:
     123              :     /// Handle new tracking toggle requests.
     124            0 :     INDI_NEWCALLBACK_DECL( kTracker, m_indiP_tracking );
     125              : 
     126              :     /// Handle incoming telescope data updates.
     127            0 :     INDI_SETCALLBACK_DECL( kTracker, m_indiP_teldata );
     128              : 
     129              :     ///@}
     130              : };
     131              : 
     132           42 : kTracker::kTracker() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
     133              : {
     134              : 
     135            6 :     return;
     136            0 : }
     137              : 
     138            0 : void kTracker::setupConfig()
     139              : {
     140            0 :     config.add( "k.zero",
     141              :                 "",
     142              :                 "k.zero",
     143              :                 argType::Required,
     144              :                 "k",
     145              :                 "zero",
     146              :                 false,
     147              :                 "float",
     148              :                 "The k-mirror zero position.  Default is -40.0." );
     149              : 
     150            0 :     config.add( "k.sign",
     151              :                 "",
     152              :                 "k.sign",
     153              :                 argType::Required,
     154              :                 "k",
     155              :                 "sign",
     156              :                 false,
     157              :                 "int",
     158              :                 "The k-mirror rotation sign. Default is +1." );
     159              : 
     160            0 :     config.add( "k.devName",
     161              :                 "",
     162              :                 "k.devName",
     163              :                 argType::Required,
     164              :                 "k",
     165              :                 "devName",
     166              :                 false,
     167              :                 "string",
     168              :                 "The device name of the k-mirrorstage.  Default is 'stagek'" );
     169              : 
     170            0 :     config.add( "tcs.devName",
     171              :                 "",
     172              :                 "tcs.devName",
     173              :                 argType::Required,
     174              :                 "tcs",
     175              :                 "devName",
     176              :                 false,
     177              :                 "string",
     178              :                 "The device name of the TCS Interface providing 'teldata.zd'.  Default is 'tcsi'" );
     179              : 
     180            0 :     config.add( "tracking.updateInterval",
     181              :                 "",
     182              :                 "tracking.updateInterval",
     183              :                 argType::Required,
     184              :                 "tracking",
     185              :                 "updateInterval",
     186              :                 false,
     187              :                 "float",
     188              :                 "The interval at which to update positions, in seconds.  Default is 10 secs." );
     189            0 : }
     190              : 
     191            0 : int kTracker::loadConfigImpl( mx::app::appConfigurator &_config )
     192              : {
     193            0 :     _config( m_zero, "k.zero" );
     194            0 :     _config( m_sign, "k.sign" );
     195            0 :     _config( m_devName, "k.devName" );
     196              : 
     197            0 :     _config( m_tcsDevName, "tcs.devName" );
     198              : 
     199            0 :     _config( m_updateInterval, "tracking.updateInterval" );
     200              : 
     201            0 :     return 0;
     202              : }
     203              : 
     204            0 : void kTracker::loadConfig()
     205              : {
     206            0 :     loadConfigImpl( config );
     207            0 : }
     208              : 
     209            0 : int kTracker::appStartup()
     210              : {
     211              : 
     212            0 :     createStandardIndiToggleSw( m_indiP_tracking, "tracking" );
     213            0 :     registerIndiPropertyNew( m_indiP_tracking, INDI_NEWCALLBACK( m_indiP_tracking ) );
     214              : 
     215            0 :     REG_INDI_SETPROP( m_indiP_teldata, m_tcsDevName, "teldata" );
     216              : 
     217            0 :     m_indiP_kpos = pcf::IndiProperty( pcf::IndiProperty::Number );
     218            0 :     m_indiP_kpos.setDevice( m_devName );
     219            0 :     m_indiP_kpos.setName( "position" );
     220            0 :     m_indiP_kpos.add( pcf::IndiElement( "target" ) );
     221              : 
     222            0 :     state( stateCodes::READY );
     223              : 
     224            0 :     return 0;
     225              : }
     226              : 
     227            0 : int kTracker::appLogic()
     228              : {
     229            0 :     const double now = mx::sys::get_curr_time();
     230              : 
     231            0 :     float k = 0;
     232              : 
     233              :     { // mutex scope
     234            0 :         std::unique_lock<std::mutex> lock( m_indiMutex, std::try_to_lock );
     235              : 
     236            0 :         if( !lock.owns_lock() )
     237              :         {
     238            0 :             return 0;
     239              :         }
     240              : 
     241            0 :         if( !m_tracking )
     242              :         {
     243            0 :             m_lastUpdate = 0;
     244            0 :             return 0;
     245              :         }
     246              : 
     247            0 :         if( !m_haveZD || now - m_lastUpdate <= m_updateInterval )
     248              :         {
     249            0 :             return 0;
     250              :         }
     251              : 
     252            0 :         k            = m_zero + m_sign * 0.5f * m_zd;
     253            0 :         m_lastUpdate = now;
     254            0 :     } // mutex scope
     255              : 
     256            0 :     if( !std::isfinite( k ) )
     257              :     {
     258            0 :         log<software_error>( { __FILE__, __LINE__, "computed non-finite K-mirror target" } );
     259            0 :         return 0;
     260              :     }
     261              : 
     262            0 :     std::cerr << "Sending k-mirror to: " << k << "\n";
     263              : 
     264            0 :     if( sendNewProperty( m_indiP_kpos, "target", k ) < 0 )
     265              :     {
     266            0 :         log<software_error>( { __FILE__, __LINE__, "failed to send K-mirror target" } );
     267              :     }
     268              : 
     269            0 :     return 0;
     270              : }
     271              : 
     272            0 : int kTracker::appShutdown()
     273              : {
     274            0 :     return 0;
     275              : }
     276              : 
     277            3 : INDI_NEWCALLBACK_DEFN( kTracker, m_indiP_tracking )( const pcf::IndiProperty &ipRecv )
     278              : {
     279            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_tracking, ipRecv );
     280              : 
     281              :     if( !ipRecv.find( "toggle" ) )
     282              :         return 0;
     283              : 
     284              :     if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
     285              :     {
     286              :         updateSwitchIfChanged( m_indiP_tracking, "toggle", pcf::IndiElement::On, INDI_IDLE );
     287              : 
     288              :         { // mutex scope
     289              :             std::lock_guard<std::mutex> guard( m_indiMutex );
     290              :             m_tracking   = true;
     291              :             m_lastUpdate = 0;
     292              :         }
     293              : 
     294              :         log<text_log>( "started K-mirror rotation tracking" );
     295              :     }
     296              :     else
     297              :     {
     298              :         updateSwitchIfChanged( m_indiP_tracking, "toggle", pcf::IndiElement::Off, INDI_IDLE );
     299              : 
     300              :         { // mutex scope
     301              :             std::lock_guard<std::mutex> guard( m_indiMutex );
     302              :             m_tracking   = false;
     303              :             m_lastUpdate = 0;
     304              :         }
     305              : 
     306              :         log<text_log>( "stopped K-mirror rotation tracking" );
     307              :     }
     308              : 
     309              :     return 0;
     310              : }
     311              : 
     312            3 : INDI_SETCALLBACK_DEFN( kTracker, m_indiP_teldata )( const pcf::IndiProperty &ipRecv )
     313              : {
     314            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_teldata, ipRecv );
     315              : 
     316              :     if( !ipRecv.find( "zd" ) )
     317              :         return 0;
     318              : 
     319              :     float zd = 0;
     320              : 
     321              :     try
     322              :     {
     323              :         zd = ipRecv["zd"].get<float>();
     324              :     }
     325              :     catch( const std::exception &e )
     326              :     {
     327              :         log<software_error>( { __FILE__, __LINE__, std::string( "exception reading teldata.zd: " ) + e.what() } );
     328              :         return 0;
     329              :     }
     330              :     catch( ... )
     331              :     {
     332              :         log<software_error>( { __FILE__, __LINE__, "unknown exception reading teldata.zd" } );
     333              :         return 0;
     334              :     }
     335              : 
     336              :     if( !std::isfinite( zd ) )
     337              :     {
     338              :         log<software_error>( { __FILE__, __LINE__, "received non-finite teldata.zd" } );
     339              :         return 0;
     340              :     }
     341              : 
     342              :     { // mutex scope
     343              :         std::lock_guard<std::mutex> guard( m_indiMutex );
     344              :         m_zd     = zd;
     345              :         m_haveZD = true;
     346              :     }
     347              : 
     348              :     return 0;
     349              : }
     350              : 
     351              : } // namespace app
     352              : } // namespace MagAOX
     353              : 
     354              : #endif // kTracker_hpp
        

Generated by: LCOV version 2.0-1