LCOV - code coverage report
Current view: top level - apps/w2tcsOffloader - w2tcsOffloader.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 0.0 % 115 0
Test Date: 2026-01-03 21:03:39 Functions: 0.0 % 17 0

            Line data    Source code
       1              : /** \file w2tcsOffloader.hpp
       2              :   * \brief The MagAO-X Woofer To Telescope Control System (TCS) offloading manager
       3              :   *
       4              :   * \ingroup app_files
       5              :   */
       6              : 
       7              : #ifndef w2tcsOffloader_hpp
       8              : #define w2tcsOffloader_hpp
       9              : 
      10              : #include <limits>
      11              : 
      12              : #include <mx/improc/eigenCube.hpp>
      13              : #include <mx/improc/eigenImage.hpp>
      14              : 
      15              : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
      16              : #include "../../magaox_git_version.h"
      17              : 
      18              : namespace MagAOX
      19              : {
      20              : namespace app
      21              : {
      22              : 
      23              : /** \defgroup w2tcsOffloader Woofer to TCS Offloading
      24              :   * \brief Monitors the averaged woofer shape, fits Zernikes, and sends it to INDI.
      25              :   *
      26              :   * <a href="../handbook/operating/software/apps/w2tcsOffloader.html">Application Documentation</a>
      27              :   *
      28              :   * \ingroup apps
      29              :   *
      30              :   */
      31              : 
      32              : /** \defgroup w2tcsOffloader_files Woofer to TCS Offloading
      33              :   * \ingroup w2tcsOffloader
      34              :   */
      35              : 
      36              : /** MagAO-X application to control offloading the woofer to the TCS.
      37              :   *
      38              :   * \ingroup w2tcsOffloader
      39              :   *
      40              :   */
      41              : class w2tcsOffloader : public MagAOXApp<true>, public dev::shmimMonitor<w2tcsOffloader>
      42              : {
      43              : 
      44              :    //Give the test harness access.
      45              :    friend class w2tcsOffloader_test;
      46              : 
      47              :    friend class dev::shmimMonitor<w2tcsOffloader>;
      48              : 
      49              :    //The base shmimMonitor type
      50              :    typedef dev::shmimMonitor<w2tcsOffloader> shmimMonitorT;
      51              : 
      52              :    ///Floating point type in which to do all calculations.
      53              :    typedef float realT;
      54              : 
      55              : protected:
      56              : 
      57              :    /** \name Configurable Parameters
      58              :      *@{
      59              :      */
      60              : 
      61              :    std::string m_wZModesPath;
      62              :    std::string m_wMaskPath;
      63              :    std::vector<std::string> m_elNames;
      64              :    std::vector<realT> m_zCoeffs;
      65              :    float m_gain {0.1};
      66              :    int m_nModes {2};
      67              :    float m_norm {1.0};
      68              : 
      69              :    ///@}
      70              : 
      71              :    mx::improc::eigenCube<realT> m_wZModes;
      72              :    mx::improc::eigenImage<realT> m_woofer;
      73              :    mx::improc::eigenImage<realT> m_wMask;
      74              : 
      75              : public:
      76              :    /// Default c'tor.
      77              :    w2tcsOffloader();
      78              : 
      79              :    /// D'tor, declared and defined for noexcept.
      80            0 :    ~w2tcsOffloader() noexcept
      81            0 :    {}
      82              : 
      83              :    virtual void setupConfig();
      84              : 
      85              :    /// Implementation of loadConfig logic, separated for testing.
      86              :    /** This is called by loadConfig().
      87              :      */
      88              :    int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
      89              : 
      90              :    virtual void loadConfig();
      91              : 
      92              :    /// Startup function
      93              :    /**
      94              :      *
      95              :      */
      96              :    virtual int appStartup();
      97              : 
      98              :    /// Implementation of the FSM for w2tcsOffloader.
      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              : 
     112              : 
     113              : 
     114              :    int allocate( const dev::shmimT & dummy /**< [in] tag to differentiate shmimMonitor parents.*/);
     115              : 
     116              :    int processImage( void * curr_src,          ///< [in] pointer to start of current frame.
     117              :                      const dev::shmimT & dummy ///< [in] tag to differentiate shmimMonitor parents.
     118              :                    );
     119              : 
     120              : 
     121              : protected:
     122              : 
     123              :    pcf::IndiProperty m_indiP_gain;
     124              :    pcf::IndiProperty m_indiP_nModes;
     125              :    pcf::IndiProperty m_indiP_zCoeffs;
     126              : 
     127              :    pcf::IndiProperty m_indiP_zero;
     128              : 
     129            0 :    INDI_NEWCALLBACK_DECL(w2tcsOffloader, m_indiP_gain);
     130            0 :    INDI_NEWCALLBACK_DECL(w2tcsOffloader, m_indiP_nModes);
     131            0 :    INDI_NEWCALLBACK_DECL(w2tcsOffloader, m_indiP_zero);
     132            0 :    INDI_NEWCALLBACK_DECL(w2tcsOffloader, m_indiP_zCoeffs);
     133              : };
     134              : 
     135              : inline
     136              : w2tcsOffloader::w2tcsOffloader() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
     137              : {
     138              :    return;
     139              : }
     140              : 
     141              : inline
     142            0 : void w2tcsOffloader::setupConfig()
     143              : {
     144            0 :    shmimMonitorT::setupConfig(config);
     145              : 
     146            0 :    config.add("offload.wZModesPath", "", "offload.wZModesPath", argType::Required, "offload", "wZModesPath", false, "string", "The path to the woofer Zernike modes.");
     147            0 :    config.add("offload.wMaskPath", "", "offload.wMaskPath", argType::Required, "offload", "wMaskPath", false, "string", "Path to the woofer Zernike mode mask.");
     148            0 :    config.add("offload.gain", "", "offload.gain", argType::Required, "offload", "gain", false, "float", "The starting offload gain.  Default is 0.1.");
     149            0 :    config.add("offload.nModes", "", "offload.nModes", argType::Required, "offload", "nModes", false, "int", "Number of modes to offload to the TCS.");
     150            0 : }
     151              : 
     152              : inline
     153            0 : int w2tcsOffloader::loadConfigImpl( mx::app::appConfigurator & _config )
     154              : {
     155              : 
     156            0 :    shmimMonitorT::loadConfig(_config);
     157              : 
     158            0 :    _config(m_wZModesPath, "offload.wZModesPath");
     159            0 :    _config(m_wMaskPath, "offload.wMaskPath");
     160            0 :    _config(m_gain, "offload.gain");
     161            0 :    _config(m_nModes, "offload.nModes");
     162              : 
     163            0 :    return 0;
     164              : }
     165              : 
     166              : inline
     167            0 : void w2tcsOffloader::loadConfig()
     168              : {
     169            0 :    loadConfigImpl(config);
     170            0 : }
     171              : 
     172              : inline
     173            0 : int w2tcsOffloader::appStartup(){
     174              : 
     175            0 :    mx::fits::fitsFile<float> ff;
     176            0 :    mx::error_t errc = ff.read(m_wZModes, m_wZModesPath);
     177            0 :    if(errc != mx::error_t::noerror)
     178              :    {
     179            0 :       return log<text_log,-1>("Could not open mode cube file", logPrio::LOG_ERROR);
     180              :    }
     181              : 
     182            0 :    m_zCoeffs.resize(m_wZModes.planes(), 0);
     183              : 
     184            0 :    errc = ff.read(m_wMask, m_wMaskPath);
     185            0 :    if( errc != mx::error_t::noerror)
     186              :    {
     187            0 :      return log<text_log,-1>("Could not open mode mask file", logPrio::LOG_ERROR);
     188              :    }
     189              : 
     190            0 :    m_norm = m_wMask.sum();
     191              : 
     192            0 :    createStandardIndiNumber<unsigned>( m_indiP_gain, "gain", 0, 1, 0, "%0.2f");
     193            0 :    m_indiP_gain["current"] = m_gain;
     194            0 :    m_indiP_gain["target"] = m_gain;
     195              : 
     196            0 :    if( registerIndiPropertyNew( m_indiP_gain, INDI_NEWCALLBACK(m_indiP_gain)) < 0)
     197              :    {
     198            0 :       log<software_error>({__FILE__,__LINE__});
     199            0 :       return -1;
     200              :    }
     201              : 
     202            0 :    createStandardIndiNumber<unsigned>( m_indiP_nModes, "nModes", 1, std::numeric_limits<unsigned>::max(), 1, "%u");
     203            0 :    m_indiP_nModes["current"] = m_nModes;
     204              : 
     205            0 :    if( registerIndiPropertyNew( m_indiP_nModes, INDI_NEWCALLBACK(m_indiP_nModes)) < 0)
     206              :    {
     207            0 :       log<software_error>({__FILE__,__LINE__});
     208            0 :       return -1;
     209              :    }
     210              : 
     211            0 :    REG_INDI_NEWPROP(m_indiP_zCoeffs, "zCoeffs", pcf::IndiProperty::Number);
     212              : 
     213              : 
     214            0 :    m_elNames.resize(m_zCoeffs.size());
     215            0 :    for(size_t n=0; n < m_zCoeffs.size(); ++n)
     216              :    {
     217              :       //std::string el = std::to_string(n);
     218            0 :       m_elNames[n] = mx::ioutils::convertToString<size_t, 2, '0'>(n);
     219              : 
     220            0 :       m_indiP_zCoeffs.add( pcf::IndiElement(m_elNames[n]) );
     221            0 :       m_indiP_zCoeffs[m_elNames[n]].set(0);
     222              :    }
     223              : 
     224            0 :    if(shmimMonitorT::appStartup() < 0)
     225              :    {
     226            0 :       return log<software_error,-1>({__FILE__, __LINE__});
     227              :    }
     228              : 
     229              : 
     230            0 :    createStandardIndiRequestSw( m_indiP_zero, "zero", "zero loop");
     231            0 :    if( registerIndiPropertyNew( m_indiP_zero, INDI_NEWCALLBACK(m_indiP_zero)) < 0)
     232              :    {
     233            0 :       log<software_error>({__FILE__,__LINE__});
     234            0 :       return -1;
     235              :    }
     236              : 
     237            0 :    state(stateCodes::OPERATING);
     238              : 
     239            0 :    return 0;
     240            0 : }
     241              : 
     242              : inline
     243            0 : int w2tcsOffloader::appLogic()
     244              : {
     245            0 :    if( shmimMonitorT::appLogic() < 0)
     246              :    {
     247            0 :       return log<software_error,-1>({__FILE__,__LINE__});
     248              :    }
     249              : 
     250              : 
     251            0 :    std::unique_lock<std::mutex> lock(m_indiMutex);
     252              : 
     253            0 :    if(shmimMonitorT::updateINDI() < 0)
     254              :    {
     255            0 :       log<software_error>({__FILE__, __LINE__});
     256              :    }
     257              : 
     258              : 
     259            0 :    return 0;
     260            0 : }
     261              : 
     262              : inline
     263            0 : int w2tcsOffloader::appShutdown()
     264              : {
     265            0 :    shmimMonitorT::appShutdown();
     266              : 
     267              : 
     268            0 :    return 0;
     269              : }
     270              : 
     271              : inline
     272            0 : int w2tcsOffloader::allocate(const dev::shmimT & dummy)
     273              : {
     274              :    static_cast<void>(dummy); //be unused
     275              : 
     276              :    //std::unique_lock<std::mutex> lock(m_indiMutex);
     277              : 
     278            0 :    m_woofer.resize(shmimMonitorT::m_width, shmimMonitorT::m_height);
     279              : 
     280              :    //state(stateCodes::OPERATING);
     281              : 
     282            0 :    return 0;
     283              : }
     284              : 
     285              : inline
     286            0 : int w2tcsOffloader::processImage( void * curr_src,
     287              :                                 const dev::shmimT & dummy
     288              :                               )
     289              : {
     290              :    static_cast<void>(dummy); //be unused (what is this?)
     291              : 
     292              :    // Replace this:
     293              :    // project zernikes onto avg image
     294              :    // update INDI properties with coeffs
     295              : 
     296            0 :    for(size_t i=0; i < m_zCoeffs.size(); ++i)
     297              :    {
     298              :     /* update requested nModes and explicitly zero out any
     299              :     modes that shouldn't be offloaded (but might have been
     300              :     previously set)
     301              :     */
     302            0 :     if(i < m_nModes)
     303              :     {
     304              :        float coeff;
     305            0 :        coeff = ( Eigen::Map<mx::improc::eigenImage<realT>>((float *)curr_src, shmimMonitorT::m_width, shmimMonitorT::m_height) * m_wZModes.image(i) * m_wMask).sum() / m_norm;
     306            0 :        m_indiP_zCoeffs[m_elNames[i]] = m_gain * coeff;
     307              :     }
     308              :     else
     309              :     {
     310            0 :       m_indiP_zCoeffs[m_elNames[i]] = 0.;
     311              :     }
     312              :    }
     313              : 
     314            0 :    m_indiP_zCoeffs.setState (pcf::IndiProperty::Ok);
     315            0 :    m_indiDriver->sendSetProperty (m_indiP_zCoeffs);
     316              : 
     317              : 
     318              :    // loop over something like this
     319              :    //z0 = (im * basis.image(0)*mask).sum()/norm;
     320              : 
     321              : 
     322            0 :    return 0;
     323              : }
     324              : 
     325              : // update this: mode coefficients (maybe they shouldn't be settable. How to handle?)
     326              : 
     327            0 : INDI_NEWCALLBACK_DEFN(w2tcsOffloader, m_indiP_gain)(const pcf::IndiProperty &ipRecv)
     328              : {
     329            0 :     INDI_VALIDATE_CALLBACK_PROPS(m_indiP_gain, ipRecv);
     330              : 
     331              :     float target;
     332              : 
     333            0 :     if( indiTargetUpdate( m_indiP_gain, target, ipRecv, true) < 0)
     334              :     {
     335            0 :         log<software_error>({__FILE__,__LINE__});
     336            0 :         return -1;
     337              :     }
     338              : 
     339            0 :     m_gain = target;
     340              : 
     341            0 :     updateIfChanged(m_indiP_gain, "current", m_gain);
     342            0 :     updateIfChanged(m_indiP_gain, "target", m_gain);
     343              : 
     344            0 :     log<text_log>("set gain to " + std::to_string(m_gain), logPrio::LOG_NOTICE);
     345              : 
     346            0 :     return 0;
     347              : }
     348              : 
     349            0 : INDI_NEWCALLBACK_DEFN(w2tcsOffloader, m_indiP_nModes)(const pcf::IndiProperty &ipRecv)
     350              : {
     351            0 :     INDI_VALIDATE_CALLBACK_PROPS(m_indiP_nModes, ipRecv);
     352              : 
     353              :     unsigned target;
     354              : 
     355            0 :     if( indiTargetUpdate( m_indiP_nModes, target, ipRecv, true) < 0)
     356              :     {
     357            0 :         log<software_error>({__FILE__,__LINE__});
     358            0 :         return -1;
     359              :     }
     360              : 
     361            0 :     m_nModes = target;
     362              : 
     363            0 :     updateIfChanged(m_indiP_nModes, "current", m_nModes);
     364            0 :     updateIfChanged(m_indiP_nModes, "target", m_nModes);
     365              : 
     366            0 :     log<text_log>("set nModes to " + std::to_string(m_nModes), logPrio::LOG_NOTICE);
     367              : 
     368            0 :     return 0;
     369              : }
     370              : 
     371            0 : INDI_NEWCALLBACK_DEFN(w2tcsOffloader, m_indiP_zCoeffs)(const pcf::IndiProperty &ipRecv)
     372              : {
     373            0 :     INDI_VALIDATE_CALLBACK_PROPS(m_indiP_zCoeffs, ipRecv);
     374              : 
     375              : 
     376            0 :     for(size_t n=0; n < m_zCoeffs.size(); ++n)
     377              :     {
     378            0 :         if(ipRecv.find(m_elNames[n]))
     379              :         {
     380            0 :             realT zcoeff = ipRecv[m_elNames[n]].get<realT>();
     381            0 :             m_zCoeffs[n] = zcoeff;
     382              :         }
     383              :     }
     384            0 :     return 0;
     385              : 
     386              : 
     387              :    return log<software_error,-1>({__FILE__,__LINE__, "invalid indi property name"});
     388              : }
     389              : 
     390            0 : INDI_NEWCALLBACK_DEFN(w2tcsOffloader, m_indiP_zero)(const pcf::IndiProperty &ipRecv)
     391              : {
     392            0 :     INDI_VALIDATE_CALLBACK_PROPS(m_indiP_zero, ipRecv);
     393              : 
     394              :     float target;
     395              : 
     396            0 :     if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
     397              :     {
     398            0 :         m_woofer.setZero();
     399            0 :         log<text_log>("set zero", logPrio::LOG_NOTICE);
     400              :     }
     401            0 :     return 0;
     402              : }
     403              : 
     404              : } //namespace app
     405              : } //namespace MagAOX
     406              : 
     407              : #endif //w2tcsOffloader_hpp
        

Generated by: LCOV version 2.0-1