LCOV - code coverage report
Current view: top level - apps/adcTracker - adcTracker.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 13.1 % 122 16
Test Date: 2026-01-03 21:03:39 Functions: 40.0 % 20 8

            Line data    Source code
       1              : /** \file adcTracker.hpp
       2              :   * \brief The MagAO-X ADC Tracker header file
       3              :   *
       4              :   * \ingroup adcTracker_files
       5              :   */
       6              : 
       7              : #ifndef adcTracker_hpp
       8              : #define adcTracker_hpp
       9              : 
      10              : 
      11              : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
      12              : #include "../../magaox_git_version.h"
      13              : 
      14              : #include <mx/math/gslInterpolator.hpp>
      15              : #include <mx/ioutils/readColumns.hpp>
      16              : 
      17              : /** \defgroup adcTracker
      18              :   * \brief The MagAO-X application to track sky rotation with the atmospheric dispersion corrector.
      19              :   *
      20              :   * <a href="../handbook/operating/software/apps/adcTracker.html">Application Documentation</a>
      21              :   *
      22              :   * \ingroup apps
      23              :   *
      24              :   */
      25              : 
      26              : /** \defgroup adcTracker_files
      27              :   * \ingroup adcTracker
      28              :   */
      29              : 
      30              : namespace MagAOX
      31              : {
      32              : namespace app
      33              : {
      34              : 
      35              : /// The MagAO-X ADC Tracker
      36              : /**
      37              :   * \ingroup adcTracker
      38              :   */
      39              : class adcTracker : public MagAOXApp<true>
      40              : {
      41              : 
      42              :    //Give the test harness access.
      43              :    friend class adcTracker_test;
      44              : 
      45              : protected:
      46              : 
      47              :    /** \name Configurable Parameters
      48              :      *@{
      49              :      */
      50              : 
      51              :    //here add parameters which will be config-able at runtime
      52              :    std::string m_lookupFile {"adc_lookup_table.txt"}; ///< The name of the file, in the calib directory, containing the adc lookup table.  Default is 'adc_lookup_table.txt'.
      53              : 
      54              : 
      55              :    float m_adc1zero {0}; ///< The starting point for ADC 1. Default is 0.
      56              : 
      57              :    int m_adc1lupsign {1}; ///< The sign to apply to the lookup table value for ADC 1
      58              : 
      59              :    float m_adc2zero {0}; ///< The starting point for ADC 2. Default is 0.
      60              : 
      61              :    int m_adc2lupsign {1}; ///< The sign to apply to the lookup table value for ADC 2
      62              : 
      63              :    float m_deltaAngle {0}; ///< The offset angle to apply to the looked-up values, applied to both.  Default is 0.
      64              : 
      65              :    float m_adc1delta {0}; ///< The offset angle to apply to the looked-up value for ADC 1, applied in addition to deltaAngle.  Default is 0.
      66              : 
      67              :    float m_adc2delta {0}; ///< The offset angle to apply to the looked-up value for ADC 2, applied in addition to deltaAngle.  Default is 0.
      68              : 
      69              :    float m_minZD {5.1}; ///< "The minimum zenith distance at which to interpolate and move the ADCs.  Default is 0.
      70              : 
      71              : 
      72              :    std::string m_adc1DevName {"stageadc1"}; ///< The device name of the ADC 1 stage.  Default is 'stageadc1'
      73              :    std::string m_adc2DevName {"stageadc2"}; ///< The device name of the ADC 2 stage.  Default is 'stageadc2'
      74              : 
      75              :    std::string m_tcsDevName {"tcsi"}; ///< The device name of the TCS Interface providing 'teldata.zd'.  Default is 'tcsi'
      76              : 
      77              :    float m_updateInterval {10};
      78              : 
      79              :    ///@}
      80              : 
      81              :    float m_maxZD {60};
      82              : 
      83              :    std::vector<double> m_lupZD;
      84              :    std::vector<double> m_lupADC1;
      85              :    std::vector<double> m_lupADC2;
      86              : 
      87              :    mx::math::gslInterpolator<mx::math::gsl_interp_linear<double>> m_terpADC1;
      88              :    mx::math::gslInterpolator<mx::math::gsl_interp_linear<double>> m_terpADC2;
      89              : 
      90              :    bool m_tracking {false};
      91              : 
      92              :    float m_zd {0};
      93              : 
      94              : public:
      95              :    /// Default c'tor.
      96              :    adcTracker();
      97              : 
      98              :    /// D'tor, declared and defined for noexcept.
      99           18 :    ~adcTracker() noexcept
     100           18 :    {}
     101              : 
     102              :    virtual void setupConfig();
     103              : 
     104              :    /// Implementation of loadConfig logic, separated for testing.
     105              :    /** This is called by loadConfig().
     106              :      */
     107              :    int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
     108              : 
     109              :    virtual void loadConfig();
     110              : 
     111              :    /// Startup function
     112              :    /**
     113              :      *
     114              :      */
     115              :    virtual int appStartup();
     116              : 
     117              :    /// Implementation of the FSM for adcTracker.
     118              :    /**
     119              :      * \returns 0 on no critical error
     120              :      * \returns -1 on an error requiring shutdown
     121              :      */
     122              :    virtual int appLogic();
     123              : 
     124              :    /// Shutdown the app.
     125              :    /**
     126              :      *
     127              :      */
     128              :    virtual int appShutdown();
     129              : 
     130              : 
     131              :    /** @name INDI
     132              :      *
     133              :      * @{
     134              :      */
     135              : protected:
     136              : 
     137              :    pcf::IndiProperty m_indiP_tracking;
     138              : 
     139              :    pcf::IndiProperty m_indiP_deltaAngle;
     140              :    pcf::IndiProperty m_indiP_deltaADC1;
     141              :    pcf::IndiProperty m_indiP_deltaADC2;
     142              : 
     143              :    pcf::IndiProperty m_indiP_minZD;
     144              : 
     145              : 
     146              :    pcf::IndiProperty m_indiP_teldata;
     147              : 
     148              : 
     149              :    pcf::IndiProperty m_indiP_adc1pos;
     150              :    pcf::IndiProperty m_indiP_adc2pos;
     151              : 
     152              : public:
     153            0 :    INDI_NEWCALLBACK_DECL(adcTracker, m_indiP_tracking);
     154              : 
     155            0 :    INDI_NEWCALLBACK_DECL(adcTracker, m_indiP_deltaAngle);
     156            0 :    INDI_NEWCALLBACK_DECL(adcTracker, m_indiP_deltaADC1);
     157            0 :    INDI_NEWCALLBACK_DECL(adcTracker, m_indiP_deltaADC2);
     158              : 
     159            0 :    INDI_NEWCALLBACK_DECL(adcTracker, m_indiP_minZD);
     160              : 
     161            0 :    INDI_SETCALLBACK_DECL(adcTracker, m_indiP_teldata);
     162              : 
     163              : 
     164              : 
     165              :    ///@}
     166              : };
     167              : 
     168          198 : adcTracker::adcTracker() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
     169              : {
     170              : 
     171           18 :    return;
     172            0 : }
     173              : 
     174            0 : void adcTracker::setupConfig()
     175              : {
     176            0 :    config.add("adcs.lookupFile", "", "adcs.lookupFile", argType::Required, "adcs", "lookupFile", false, "string", "The name of the file, in the calib directory, containing the adc lookup table.  Default is 'adc_lookup_table.txt'.");
     177              : 
     178            0 :    config.add("adcs.adc1zero", "", "adcs.adc1zero", argType::Required, "adcs", "adc1zero", false, "float", "The starting point for ADC 1. Default is 0.");
     179              : 
     180            0 :    config.add("adcs.adc1lupsign", "", "adcs.adc1lupsign", argType::Required, "adcs", "adc1lupsign", false, "int", "The sign to apply for the LUP values for ADC 1. Default is +1.");
     181              : 
     182            0 :    config.add("adcs.adc2zero", "", "adcs.adc2zero", argType::Required, "adcs", "adc2zero", false, "float", "The starting point for ADC 2. Default is 0.");
     183              : 
     184            0 :    config.add("adcs.adc2lupsign", "", "adcs.adc2lupsign", argType::Required, "adcs", "adc2lupsign", false, "int", "The sign to apply for the LUP values for ADC 2. Default is +1.");
     185              : 
     186            0 :    config.add("adcs.deltaAngle", "", "adcs.deltaAngle", argType::Required, "adcs", "deltaAngle", false, "float", "The offset angle to apply to the looked-up values, applied to both.  Default is 0.");
     187              : 
     188            0 :    config.add("adcs.adc1delta", "", "adcs.adc1delta", argType::Required, "adcs", "adc1delta", false, "float", "The offset angle to apply to the looked-up value for ADC 1, applied in addition to deltaAngle.  Default is 0.");
     189              : 
     190            0 :    config.add("adcs.adc2delta", "", "adcs.adc2delta", argType::Required, "adcs", "adc2delta", false, "float", "The offset angle to apply to the looked-up value for ADC 2, applied in addition to deltaAngle.  Default is 0.");
     191              : 
     192            0 :    config.add("adcs.minZD", "", "adcs.minZD", argType::Required, "adcs", "minZD", false, "float", "The minimum zenith distance at which to interpolate and move the ADCs.  Default is 5.1");
     193              : 
     194            0 :    config.add("adcs.adc1DevName", "", "adcs.adc1devName", argType::Required, "adcs", "adc1DevName", false, "string", "The device name of the ADC 1 stage.  Default is 'stageadc1'");
     195              : 
     196            0 :    config.add("adcs.adc2DevName", "", "adcs.adc2devName", argType::Required, "adcs", "adc2DevName", false, "string", "The device name of the ADC 2 stage.  Default is 'stageadc2'");
     197              : 
     198            0 :    config.add("tcs.devName", "", "tcs.devName", argType::Required, "tcs", "devName", false, "string", "The device name of the TCS Interface providing 'teldata.zd'.  Default is 'tcsi'");
     199              : 
     200            0 :    config.add("tracking.updateInterval", "", "tracking.updateInterval", argType::Required, "tracking", "updateInterval", false, "float", "The interval at which to update positions, in seconds.  Default is 10 secs.");
     201            0 : }
     202              : 
     203            0 : int adcTracker::loadConfigImpl( mx::app::appConfigurator & _config )
     204              : {
     205            0 :    _config(m_lookupFile, "adcs.lookupFile");
     206            0 :    _config(m_adc1zero, "adcs.adc1zero");
     207            0 :    _config(m_adc1lupsign, "adcs.adc1lupsign");
     208            0 :    _config(m_adc2zero, "adcs.adc2zero");
     209            0 :    _config(m_adc2lupsign, "adcs.adc2lupsign");
     210            0 :    _config(m_deltaAngle, "adcs.deltaAngle");
     211            0 :    _config(m_adc1delta, "adcs.adc1delta");
     212            0 :    _config(m_adc2delta, "adcs.adc2delta");
     213            0 :    _config(m_minZD, "adcs.minZD");
     214            0 :    _config(m_adc1DevName, "adcs.adc1DevName");
     215            0 :    _config(m_adc2DevName, "adcs.adc2DevName");
     216              : 
     217            0 :    _config(m_tcsDevName, "tcs.devName");
     218              : 
     219            0 :    _config(m_updateInterval, "tracking.updateInterval");
     220              : 
     221            0 :    return 0;
     222              : }
     223              : 
     224            0 : void adcTracker::loadConfig()
     225              : {
     226            0 :    loadConfigImpl(config);
     227            0 : }
     228              : 
     229            0 : int adcTracker::appStartup()
     230              : {
     231              : 
     232            0 :    std::string luppath = m_calibDir + "/" + m_lookupFile;
     233              : 
     234            0 :    std::cerr << "Reading " << luppath << "\n";
     235              : 
     236            0 :    if(mx::ioutils::readColumns<mx::ioutils::readColCommaDelim>(luppath, m_lupZD, m_lupADC1, m_lupADC2) != mx::error_t::noerror)
     237              :    {
     238            0 :       log<software_critical>({__FILE__,__LINE__, "error reading lookup table from " + luppath});
     239            0 :       return -1;
     240              :    }
     241              : 
     242            0 :    if(m_lupZD.size() != m_lupADC1.size() || m_lupZD.size()!= m_lupADC2.size())
     243              :    {
     244            0 :       log<software_critical>({__FILE__,__LINE__, "inconsistent sizes in " + luppath});
     245            0 :       return -1;
     246              :    }
     247              : 
     248            0 :    log<text_log>("Read " + std::to_string(m_lupZD.size()) + " points from " + m_lookupFile);
     249              : 
     250            0 :    m_terpADC1.setup(m_lupZD, m_lupADC1);
     251            0 :    m_terpADC2.setup(m_lupZD, m_lupADC2);
     252              : 
     253            0 :    createStandardIndiToggleSw( m_indiP_tracking, "tracking");
     254            0 :    registerIndiPropertyNew( m_indiP_tracking, INDI_NEWCALLBACK(m_indiP_tracking));
     255              : 
     256            0 :    createStandardIndiNumber<float>( m_indiP_deltaAngle, "deltaAngle", -180.0, 180.0, 0, "%0.2f");
     257            0 :    m_indiP_deltaAngle["target"].set(m_deltaAngle);
     258            0 :    m_indiP_deltaAngle["current"].set(m_deltaAngle);
     259            0 :    registerIndiPropertyNew( m_indiP_deltaAngle, INDI_NEWCALLBACK(m_indiP_deltaAngle));
     260              : 
     261            0 :    createStandardIndiNumber<float>( m_indiP_deltaADC1, "deltaADC1", -180.0, 180.0, 0, "%0.2f");
     262            0 :    m_indiP_deltaADC1["target"].set(m_adc1delta);
     263            0 :    m_indiP_deltaADC1["current"].set(m_adc1delta);
     264            0 :    registerIndiPropertyNew( m_indiP_deltaADC1, INDI_NEWCALLBACK(m_indiP_deltaADC1));
     265              : 
     266            0 :    createStandardIndiNumber<float>( m_indiP_deltaADC2, "deltaADC2", -180.0, 180.0, 0, "%0.2f");
     267            0 :    m_indiP_deltaADC2["target"].set(m_adc2delta);
     268            0 :    m_indiP_deltaADC2["current"].set(m_adc2delta);
     269            0 :    registerIndiPropertyNew( m_indiP_deltaADC2, INDI_NEWCALLBACK(m_indiP_deltaADC2));
     270              : 
     271            0 :    createStandardIndiNumber<float>( m_indiP_minZD, "minZD", 0.0, 90.0, 0, "%0.2f");
     272            0 :    m_indiP_minZD["target"].set(m_minZD);
     273            0 :    m_indiP_minZD["current"].set(m_minZD);
     274            0 :    registerIndiPropertyNew( m_indiP_minZD, INDI_NEWCALLBACK(m_indiP_minZD));
     275              : 
     276            0 :    REG_INDI_SETPROP(m_indiP_teldata, m_tcsDevName, "teldata");
     277              : 
     278            0 :    m_indiP_adc1pos = pcf::IndiProperty(pcf::IndiProperty::Number);
     279            0 :    m_indiP_adc1pos.setDevice(m_adc1DevName);
     280            0 :    m_indiP_adc1pos.setName("position");
     281            0 :    m_indiP_adc1pos.add(pcf::IndiElement("target"));
     282              : 
     283            0 :    m_indiP_adc2pos = pcf::IndiProperty(pcf::IndiProperty::Number);
     284            0 :    m_indiP_adc2pos.setDevice(m_adc2DevName);
     285            0 :    m_indiP_adc2pos.setName("position");
     286            0 :    m_indiP_adc2pos.add(pcf::IndiElement("target"));
     287              : 
     288            0 :    state(stateCodes::READY);
     289              : 
     290            0 :    return 0;
     291            0 : }
     292              : 
     293            0 : int adcTracker::appLogic()
     294              : {
     295              : 
     296              :    static double lastupdate = 0;
     297              : 
     298            0 :    if(m_tracking && mx::sys::get_curr_time() - lastupdate > m_updateInterval)
     299              :    {
     300            0 :       float dadc1 = 0.0;
     301            0 :       float dadc2 = 0.0;
     302              : 
     303            0 :       if(m_zd > m_lupZD.back())
     304              :       {
     305            0 :          std::cerr << "end of lup\n";
     306            0 :          dadc1 = m_lupADC1.back();
     307            0 :          dadc2 = m_lupADC2.back();
     308              :       }
     309            0 :       else if(m_zd >= m_minZD)
     310              :       {
     311            0 :          dadc1 = fabs(m_terpADC1(m_zd));
     312            0 :          dadc2 = fabs(m_terpADC2(m_zd));
     313              :       }
     314              :       else
     315              :       {
     316            0 :          std::cerr << "zenith limit\n";
     317              :       }
     318              : 
     319            0 :       float adc1 = m_adc1zero + m_adc1lupsign*(dadc1 + m_adc1delta + m_deltaAngle);
     320            0 :       float adc2 = m_adc2zero + m_adc2lupsign*(dadc2 + m_adc2delta + m_deltaAngle);
     321              : 
     322            0 :       std::cerr << "Sending adcs to: " << adc1 << " " << adc2 << "\n";
     323              : 
     324              : 
     325            0 :       m_indiP_adc1pos["target"] = adc1;
     326            0 :       sendNewProperty (m_indiP_adc1pos);
     327              : 
     328            0 :       m_indiP_adc2pos["target"] = adc2;
     329            0 :       sendNewProperty (m_indiP_adc2pos);
     330              : 
     331            0 :       lastupdate = mx::sys::get_curr_time();
     332              :    }
     333            0 :    else if(!m_tracking) lastupdate = 0;
     334              : 
     335            0 :    return 0;
     336              : }
     337              : 
     338            0 : int adcTracker::appShutdown()
     339              : {
     340            0 :    return 0;
     341              : }
     342              : 
     343            3 : INDI_NEWCALLBACK_DEFN(adcTracker, m_indiP_tracking)(const pcf::IndiProperty &ipRecv)
     344              : {
     345            3 :    INDI_VALIDATE_CALLBACK_PROPS(m_indiP_tracking, ipRecv);
     346              : 
     347              :    if(!ipRecv.find("toggle")) return 0;
     348              : 
     349              :    if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
     350              :    {
     351              :       updateSwitchIfChanged(m_indiP_tracking, "toggle", pcf::IndiElement::On, INDI_IDLE);
     352              : 
     353              :       m_tracking = true;
     354              : 
     355              :       log<text_log>("started ADC rotation tracking");
     356              :    }
     357              :    else
     358              :    {
     359              :       updateSwitchIfChanged(m_indiP_tracking, "toggle", pcf::IndiElement::Off, INDI_IDLE);
     360              : 
     361              :       m_tracking = false;
     362              : 
     363              :       log<text_log>("stopped ADC rotation tracking");
     364              :    }
     365              : 
     366              :    return 0;
     367              : }
     368              : 
     369            3 : INDI_NEWCALLBACK_DEFN(adcTracker, m_indiP_deltaAngle)(const pcf::IndiProperty &ipRecv)
     370              : {
     371            3 :    INDI_VALIDATE_CALLBACK_PROPS(m_indiP_deltaAngle, ipRecv);
     372              : 
     373              :    float target;
     374              : 
     375              :    if( indiTargetUpdate( m_indiP_deltaAngle, target, ipRecv) < 0)
     376              :    {
     377              :       log<software_error>({__FILE__,__LINE__});
     378              :       return -1;
     379              :    }
     380              : 
     381              :    m_deltaAngle = target;
     382              : 
     383              :    std::lock_guard<std::mutex> guard(m_indiMutex);
     384              : 
     385              :    updateIfChanged(m_indiP_deltaAngle, "current", m_deltaAngle);
     386              : 
     387              :    log<text_log>("set deltaAngle to " + std::to_string(m_deltaAngle));
     388              : 
     389              :    return 0;
     390              : }
     391              : 
     392            3 : INDI_NEWCALLBACK_DEFN(adcTracker, m_indiP_deltaADC1)(const pcf::IndiProperty &ipRecv)
     393              : {
     394            3 :    INDI_VALIDATE_CALLBACK_PROPS(m_indiP_deltaADC1, ipRecv);
     395              : 
     396              :    float target;
     397              : 
     398              :    if( indiTargetUpdate( m_indiP_deltaADC1, target, ipRecv) < 0)
     399              :    {
     400              :       log<software_error>({__FILE__,__LINE__});
     401              :       return -1;
     402              :    }
     403              : 
     404              :    m_adc1delta = target;
     405              : 
     406              :    std::lock_guard<std::mutex> guard(m_indiMutex);
     407              : 
     408              :    updateIfChanged(m_indiP_deltaADC1, "current", m_adc1delta);
     409              : 
     410              :    log<text_log>("set deltaADC1 to " + std::to_string(m_adc1delta));
     411              : 
     412              :    return 0;
     413              : }
     414              : 
     415            3 : INDI_NEWCALLBACK_DEFN(adcTracker, m_indiP_deltaADC2)(const pcf::IndiProperty &ipRecv)
     416              : {
     417            3 :    INDI_VALIDATE_CALLBACK_PROPS(m_indiP_deltaADC2, ipRecv);
     418              : 
     419              :    float target;
     420              : 
     421              :    if( indiTargetUpdate( m_indiP_deltaADC2, target, ipRecv) < 0)
     422              :    {
     423              :       log<software_error>({__FILE__,__LINE__});
     424              :       return -1;
     425              :    }
     426              : 
     427              :    m_adc2delta = target;
     428              : 
     429              :    std::lock_guard<std::mutex> guard(m_indiMutex);
     430              : 
     431              :    updateIfChanged(m_indiP_deltaADC2, "current", m_adc2delta);
     432              : 
     433              :    log<text_log>("set deltaADC2 to " + std::to_string(m_adc2delta));
     434              : 
     435              :    return 0;
     436              : }
     437              : 
     438            3 : INDI_NEWCALLBACK_DEFN(adcTracker, m_indiP_minZD)(const pcf::IndiProperty &ipRecv)
     439              : {
     440            3 :    INDI_VALIDATE_CALLBACK_PROPS(m_indiP_minZD, ipRecv);
     441              : 
     442              :    float target;
     443              : 
     444              :    if( indiTargetUpdate( m_indiP_minZD, target, ipRecv) < 0)
     445              :    {
     446              :       log<software_error>({__FILE__,__LINE__});
     447              :       return -1;
     448              :    }
     449              : 
     450              :    m_minZD = target;
     451              : 
     452              :    std::lock_guard<std::mutex> guard(m_indiMutex);
     453              : 
     454              :    updateIfChanged(m_indiP_minZD, "current", m_minZD);
     455              : 
     456              :    log<text_log>("set minZD to " + std::to_string(m_minZD));
     457              : 
     458              :    return 0;
     459              : }
     460              : 
     461            3 : INDI_SETCALLBACK_DEFN(adcTracker, m_indiP_teldata)(const pcf::IndiProperty &ipRecv)
     462              : {
     463            3 :    INDI_VALIDATE_CALLBACK_PROPS(m_indiP_teldata, ipRecv);
     464              : 
     465              : 
     466              :    if(!ipRecv.find("zd")) return 0;
     467              : 
     468              :    m_zd = ipRecv["zd"].get<float>();
     469              : 
     470              :    return 0;
     471              : }
     472              : 
     473              : } //namespace app
     474              : } //namespace MagAOX
     475              : 
     476              : #endif //adcTracker_hpp
     477              : 
        

Generated by: LCOV version 2.0-1