LCOV - code coverage report
Current view: top level - apps/tcsInterface - tcsInterface.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 5.7 % 1305 74
Test Date: 2026-01-03 21:03:39 Functions: 20.8 % 72 15

            Line data    Source code
       1              : /** \file tcsInterface.hpp
       2              :  * \brief The MagAO-X TCS Interface header file
       3              :  *
       4              :  * \ingroup tcsInterface_files
       5              :  */
       6              : 
       7              : #ifndef tcsInterface_hpp
       8              : #define tcsInterface_hpp
       9              : 
      10              : #include <cmath>
      11              : #include <iostream>
      12              : 
      13              : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
      14              : #include "../../magaox_git_version.h"
      15              : 
      16              : #include "../../libMagAOX/app/dev/telemeter.hpp"
      17              : 
      18              : // #define LOG_TCS_STATUS
      19              : 
      20              : /** \defgroup tcsInterface
      21              :  * \brief The MagAO-X application to do interface with the Clay TCS
      22              :  *
      23              :  * <a href="../handbook/operating/software/apps/tcsInterface.html">Application Documentation</a>
      24              :  *
      25              :  * \ingroup apps
      26              :  *
      27              :  */
      28              : 
      29              : /** \defgroup tcsInterface_files
      30              :  * \ingroup tcsInterface
      31              :  */
      32              : 
      33              : namespace MagAOX
      34              : {
      35              : namespace app
      36              : {
      37              : 
      38              : /// The MagAO-X Clay Telescope TCS Interface
      39              : /**
      40              :  * \ingroup tcsInterface
      41              :  */
      42              : class tcsInterface : public MagAOXApp<true>, public dev::ioDevice, public dev::telemeter<tcsInterface>
      43              : {
      44              : 
      45              :     // Give the test harness access.
      46              :     friend class tcsInterface_test;
      47              : 
      48              :     friend class dev::telemeter<tcsInterface>;
      49              : 
      50              :   protected:
      51              :     /** \name lab mode
      52              :      * @{
      53              :      */
      54              : 
      55              :     bool m_labMode{ true };
      56              : 
      57              :     pcf::IndiProperty m_indiP_labMode;
      58            0 :     INDI_NEWCALLBACK_DECL( tcsInterface, m_indiP_labMode );
      59              : 
      60              :     ///@}
      61              : 
      62              :     /** \name TCS Networking
      63              :      *@{
      64              :      */
      65              : 
      66              :     std::string m_deviceAddr{ "localhost" }; ///< The IP address or resolvable name of the TCS.
      67              :     int m_devicePort{ 5811 }; ///< The IP port for TCS communications. Should be the command port.  Default is 5811
      68              :     int m_seeingInterval{ 2 };
      69              : 
      70              :     /// Mutex for locking TCS communications.
      71              :     std::mutex m_tcsMutex;
      72              : 
      73              :     tty::netSerial m_sock;
      74              : 
      75              :     ///@}
      76              : 
      77              :     // Telescope time:
      78              :     double m_telST{ 0 };
      79              : 
      80              :     pcf::IndiProperty m_indiP_teltime;
      81              : 
      82              :     // Telescope position:
      83              :     double m_telEpoch{ 0 };
      84              :     double m_telRA{ 0 };
      85              :     double m_telDec{ 0 };
      86              :     double m_telEl{ 0 };
      87              :     double m_telHA{ 0 };
      88              :     double m_telAM{ 0 };
      89              :     double m_telRotOff{ 0 };
      90              : 
      91              :     pcf::IndiProperty m_indiP_telpos;
      92              : 
      93              :     /// \name Telescope Data
      94              :     /**@{
      95              :      */
      96              :     int    m_telROI{ 0 };          ///< The rotator of interest
      97              :     int    m_telTracking{ 0 };     ///< tracking state
      98              :     int    m_telGuiding{ 0 };      ///< guider moving state
      99              :     int    m_telSlewing{ 0 };      ///< slewing state
     100              :     int    m_telGuiderMoving{ 0 }; ///< guider moving state
     101              :     double m_telAz{ 0 };           ///< azimuth
     102              :     double m_telZd{ 0 };           ///< zenith distance
     103              :     double m_telPA{ 0 };           ///< parallactic angle
     104              :     double m_telDomeAz{ 0 };       ///< dome azimuth
     105              :     int    m_telDomeStat{ 0 };     ///< dome status
     106              : 
     107              :     pcf::IndiProperty m_indiP_teldata;
     108              :     ///@}
     109              : 
     110              :     /// \name Telescope Catalog Information
     111              :     /**@{
     112              :      */
     113              :     double      m_catRA{ 0 };  ///< Catalog right ascension [degrees]
     114              :     double      m_catDec{ 0 }; ///< Catalog declination [degrees]
     115              :     double      m_catEp{ 0 };  ///< Catalog epoch
     116              :     double      m_catRo{ 0 };  ///< Catalog rotator offset
     117              :     std::string m_catRm;       ///< Catalog rotator mode
     118              :     std::string m_catObj;      ///< Catalog object name
     119              : 
     120              :     pcf::IndiProperty m_indiP_catalog; ///< INDI Property for the catalog text information
     121              :     pcf::IndiProperty m_indiP_catdata; ///< INDI Property for the catalog data
     122              :     ///@}
     123              : 
     124              :     // Telescope Vane-End positions
     125              :     double m_telSecZ{ 0 };
     126              :     double m_telEncZ{ 0 };
     127              :     double m_telSecX{ 0 };
     128              :     double m_telEncX{ 0 };
     129              :     double m_telSecY{ 0 };
     130              :     double m_telEncY{ 0 };
     131              :     double m_telSecH{ 0 };
     132              :     double m_telEncH{ 0 };
     133              :     double m_telSecV{ 0 };
     134              :     double m_telEncV{ 0 };
     135              : 
     136              :     pcf::IndiProperty m_indiP_vaneend; ///< INDI Property for the vane end positions
     137              : 
     138              :     // Environment
     139              :     double m_wxtemp{ 0 };     ///< Outside temperature, Celsius
     140              :     double m_wxpres{ 0 };     ///< Outside pressue, millibars
     141              :     double m_wxhumid{ 0 };    ///< Outside humidity, percent
     142              :     double m_wxwind{ 0 };     ///< outside wind intensity, mph
     143              :     double m_wxwdir{ 0 };     ///< outside wind direction, degrees
     144              :     double m_ttruss{ 0 };     ///< Telescope truss temperature, Celsius
     145              :     double m_tcell{ 0 };      ///< Primary mirror cell temperature, Celsius
     146              :     double m_tseccell{ 0 };   ///< Secondary mirror cell temperature, Celsius
     147              :     double m_tambient{ 0 };   ///< Dome air temperature, Celsius
     148              :     double m_wxdewpoint{ 0 }; ///< Dew point from weather station
     149              : 
     150              :     pcf::IndiProperty m_indiP_env; ///< INDI Property for environment
     151              : 
     152              :     // Seeing
     153              :     double m_dimm_el{ 0 };        ///< DIMM elevation at time of seeing measurement
     154              :     double m_dimm_fwhm_corr{ 0 }; ///< DIMM elevation corrected FWHM
     155              :     int    m_dimm_time{ 0 };      ///< Seconds since midnight of DIMM measurement.
     156              : 
     157              :     double m_mag1_fwhm_corr{ 0 }; ///< MAG1 elevation corrected FWHM
     158              :     int    m_mag1_time{ 0 };      ///< Seconds since midnight of MAG1 measurement.
     159              : 
     160              :     double m_mag2_fwhm_corr{ 0 }; ///< MAG2 elevation corrected FWHM
     161              :     int    m_mag2_time{ 0 };      ///< Seconds since midnight of MAG2 measurement.
     162              : 
     163              :     pcf::IndiProperty m_indiP_seeing; ///< INDI Property for seeing
     164              : 
     165              :   public:
     166              :     /// Default c'tor.
     167              :     tcsInterface();
     168              : 
     169              :     /// D'tor, declared and defined for noexcept.
     170           52 :     ~tcsInterface() noexcept
     171           52 :     {
     172           52 :     }
     173              : 
     174              :     virtual void setupConfig();
     175              : 
     176              :     /// Implementation of loadConfig logic, separated for testing.
     177              :     /** This is called by loadConfig().
     178              :      */
     179              :     int loadConfigImpl(
     180              :         mx::app::appConfigurator &_config /**< [in] an application configuration from which to load values*/ );
     181              : 
     182              :     virtual void loadConfig();
     183              : 
     184              :     /// Startup function
     185              :     /**
     186              :      *
     187              :      */
     188              :     virtual int appStartup();
     189              : 
     190              :     /// Implementation of the FSM for tcsInterface.
     191              :     /**
     192              :      * \returns 0 on no critical error
     193              :      * \returns -1 on an error requiring shutdown
     194              :      */
     195              :     virtual int appLogic();
     196              : 
     197              :     /// Shutdown the app.
     198              :     /**
     199              :      *
     200              :      */
     201              :     virtual int appShutdown();
     202              : 
     203              :     int getMagTelStatus( std::string &response, const std::string &statreq );
     204              : 
     205              :     int sendMagTelCommand( const std::string &command, int timeout );
     206              : 
     207              :     int parse_xms( double &x, double &m, double &s, const std::string &xmsstr );
     208              : 
     209              :     std::vector<std::string> parse_teldata( std::string &tdat );
     210              : 
     211              :     // The "dump" commands:
     212              : 
     213              :     int getTelTime();
     214              :     int getTelPos();
     215              :     int getTelData();
     216              :     int getCatData();
     217              :     int getVaneData();
     218              :     int getEnvData();
     219              : 
     220              :     /// Record bad seeing measurements on errors in getSeeing().
     221              :     int badSeeing();
     222              : 
     223              :     /// Query, parse, and record seeing measurements
     224              :     int getSeeing();
     225              : 
     226              :     int updateINDI();
     227              : 
     228              :     /** \name Telemeter Interface
     229              :      * @{
     230              :      */
     231              :     int checkRecordTimes();
     232              : 
     233              :     int recordTelem( const telem_telpos * );
     234              : 
     235              :     int recordTelem( const telem_teldata * );
     236              : 
     237              :     int recordTelem( const telem_telvane * );
     238              : 
     239              :     int recordTelem( const telem_telenv * );
     240              : 
     241              :     int recordTelem( const telem_telcat * );
     242              : 
     243              :     int recordTelem( const telem_telsee * );
     244              : 
     245              :     int recordTelPos( bool force = false );
     246              : 
     247              :     int recordTelData( bool force = false );
     248              : 
     249              :     int recordTelVane( bool force = false );
     250              : 
     251              :     int recordTelEnv( bool force = false );
     252              : 
     253              :     int recordTelCat( bool force = false );
     254              : 
     255              :     int recordTelSee( bool force = false );
     256              : 
     257              :     ///@}
     258              : 
     259              :     int               m_loopState{ 0 };
     260              :     pcf::IndiProperty m_indiP_loopState; ///< Property used to report the loop state
     261              : 
     262            0 :     INDI_SETCALLBACK_DECL( tcsInterface, m_indiP_loopState );
     263              : 
     264              :     /** \name Pyramid Nudging and Acquisition
     265              :      * Handling of nudges on pyramid tip.
     266              :      * @{
     267              :      */
     268              : 
     269              :     // The Pyramid to AEG control matrix
     270              :     float m_pyrNudge_C_00{ 1 };
     271              :     float m_pyrNudge_C_01{ 0 };
     272              :     float m_pyrNudge_C_10{ 0 };
     273              :     float m_pyrNudge_C_11{ 1 };
     274              :     float m_pyrNudge_F_sign{ 1 };
     275              : 
     276              :     float m_pyrNudge_ang{ 45.0 };
     277              :     float m_pyrNudge_ang0{ 0.0 };
     278              :     float m_pyrNudge_parity{ -1 };
     279              : 
     280              :     int sendPyrNudge( float x, float y, float z );
     281              : 
     282              :     pcf::IndiProperty m_indiP_pyrNudge; ///< Property used to request a pyramid nudge
     283            0 :     INDI_NEWCALLBACK_DECL( tcsInterface, m_indiP_pyrNudge );
     284              : 
     285              :     int   m_acqZdSign{ -1 };
     286              :     float m_acqAz0{ 18.5 };
     287              :     float m_acqAzOff{ 0 };
     288              :     float m_acqEl0{ 10 };
     289              :     float m_acqElOff{ 0 };
     290              :     float m_acqFocus{ 1400 };
     291              : 
     292              :     int acquireFromGuider();
     293              : 
     294              :     pcf::IndiProperty m_indiP_acqFromGuider; ///< Property used to request a pyramid nudge
     295            0 :     INDI_NEWCALLBACK_DECL( tcsInterface, m_indiP_acqFromGuider );
     296              : 
     297              :     ///@}
     298              : 
     299              :     /** \name Woofer Offloading
     300              :      * Handling of offloads from the average woofer shape to the telescope
     301              :      * @{
     302              :      */
     303              : 
     304              :     bool m_offloadThreadInit{ true }; ///< Initialization flag for the offload thread.
     305              : 
     306              :     pid_t m_offloadThreadID{ 0 }; ///< Offload thread pid.
     307              : 
     308              :     pcf::IndiProperty m_offloadThreadProp; ///< Offload thread INDI property.
     309              : 
     310              :     std::thread m_offloadThread; ///< The offloading thread.
     311              : 
     312              :     /// Offload thread starter function
     313              :     static void offloadThreadStart( tcsInterface *t /**< [in] pointer to this */ );
     314              : 
     315              :     /// Offload thread function
     316              :     /** Runs until m_shutdown is true.
     317              :      */
     318              :     void offloadThreadExec();
     319              : 
     320              :     int doTToffload( float TT_0, float TT_1 );
     321              : 
     322              :     int sendTToffload( float TT_0, float TT_1 );
     323              : 
     324              :     int doFoffload( float F_0 );
     325              : 
     326              :     int sendFoffload( float F_0 );
     327              : 
     328              :     pcf::IndiProperty
     329              :         m_indiP_offloadCoeffs; ///< Property used to report the latest woofer modal coefficients for offloading
     330              : 
     331            0 :     INDI_SETCALLBACK_DECL( tcsInterface, m_indiP_offloadCoeffs );
     332              : 
     333              :     std::vector<std::vector<float>> m_offloadRequests;
     334              :     size_t                          m_firstRequest{ 0 };
     335              :     size_t                          m_lastRequest{ std::numeric_limits<size_t>::max() };
     336              :     size_t                          m_nRequests{ 0 };
     337              :     size_t                          m_last_nRequests{ 0 };
     338              : 
     339              :     // The TT control matrix -- LAb
     340              :     float m_lab_offlTT_C_00{ 0.17 };
     341              :     float m_lab_offlTT_C_01{ 1.03 };
     342              :     float m_lab_offlTT_C_10{ -1.03 };
     343              :     float m_lab_offlTT_C_11{ 0.48 };
     344              : 
     345              :     // The TT control matrix -- Telescope
     346              :     float m_offlTT_C_00{ -0.5 };
     347              :     float m_offlTT_C_01{ 0 };
     348              :     float m_offlTT_C_10{ 0 };
     349              :     float m_offlTT_C_11{ -0.25 };
     350              : 
     351              :     bool  m_offlTT_enabled{ false };
     352              :     bool  m_offlTT_dump{ false };
     353              :     float m_offlTT_avgInt{ 1.0 };
     354              :     float m_offlTT_gain{ 0.1 };
     355              :     float m_offlTT_thresh{ 0.1 };
     356              : 
     357              :     pcf::IndiProperty m_indiP_offlTTenable;
     358            0 :     INDI_NEWCALLBACK_DECL( tcsInterface, m_indiP_offlTTenable );
     359              : 
     360              :     pcf::IndiProperty m_indiP_offlTTdump;
     361            0 :     INDI_NEWCALLBACK_DECL( tcsInterface, m_indiP_offlTTdump );
     362              : 
     363              :     pcf::IndiProperty m_indiP_offlTTavgInt;
     364            0 :     INDI_NEWCALLBACK_DECL( tcsInterface, m_indiP_offlTTavgInt );
     365              : 
     366              :     pcf::IndiProperty m_indiP_offlTTgain;
     367            0 :     INDI_NEWCALLBACK_DECL( tcsInterface, m_indiP_offlTTgain );
     368              : 
     369              :     pcf::IndiProperty m_indiP_offlTTthresh;
     370            0 :     INDI_NEWCALLBACK_DECL( tcsInterface, m_indiP_offlTTthresh );
     371              : 
     372              :     // The Focus control constant
     373              :     float m_offlCFocus_00{ 1 };
     374              : 
     375              :     bool  m_offlF_enabled{ false };
     376              :     bool  m_offlF_dump{ false };
     377              :     float m_offlF_avgInt{ 1.0 };
     378              :     float m_offlF_gain{ 0.1 };
     379              :     float m_offlF_thresh{ 0.1 };
     380              : 
     381              :     pcf::IndiProperty m_indiP_offlFenable;
     382            0 :     INDI_NEWCALLBACK_DECL( tcsInterface, m_indiP_offlFenable );
     383              : 
     384              :     pcf::IndiProperty m_indiP_offlFdump;
     385            0 :     INDI_NEWCALLBACK_DECL( tcsInterface, m_indiP_offlFdump );
     386              : 
     387              :     pcf::IndiProperty m_indiP_offlFavgInt;
     388            0 :     INDI_NEWCALLBACK_DECL( tcsInterface, m_indiP_offlFavgInt );
     389              : 
     390              :     pcf::IndiProperty m_indiP_offlFgain;
     391            0 :     INDI_NEWCALLBACK_DECL( tcsInterface, m_indiP_offlFgain );
     392              : 
     393              :     pcf::IndiProperty m_indiP_offlFthresh;
     394            0 :     INDI_NEWCALLBACK_DECL( tcsInterface, m_indiP_offlFthresh );
     395              : 
     396              :     float m_offlCComa_00{ 1 };
     397              :     float m_offlCComa_01{ 0 };
     398              :     float m_offlCComa_10{ 1 };
     399              :     float m_offlCComa_11{ 0 };
     400              : 
     401              :     ///@}
     402              : };
     403              : 
     404          260 : inline tcsInterface::tcsInterface() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
     405              : {
     406           52 :     return;
     407            0 : }
     408              : 
     409            0 : inline void tcsInterface::setupConfig()
     410              : {
     411            0 :     config.add( "labMode",
     412              :                 "",
     413              :                 "labMode",
     414              :                 argType::Required,
     415              :                 "",
     416              :                 "labMode",
     417              :                 false,
     418              :                 "bool",
     419              :                 "Flag to enable lab mode.  Default is true." );
     420              : 
     421            0 :     config.add( "pyrNudger.C_00",
     422              :                 "",
     423              :                 "pyrNudger.C_00",
     424              :                 argType::Required,
     425              :                 "pyrNudger",
     426              :                 "C_00",
     427              :                 false,
     428              :                 "float",
     429              :                 "Pyramid to AEG control matrix [0,0] of a 2x2 matrix" );
     430            0 :     config.add( "pyrNudger.C_01",
     431              :                 "",
     432              :                 "pyrNudger.C_01",
     433              :                 argType::Required,
     434              :                 "pyrNudger",
     435              :                 "C_01",
     436              :                 false,
     437              :                 "float",
     438              :                 "Pyramid to AEG control matrix [0,1] of a 2x2 matrix " );
     439            0 :     config.add( "pyrNudger.C_10",
     440              :                 "",
     441              :                 "pyrNudger.C_10",
     442              :                 argType::Required,
     443              :                 "pyrNudger",
     444              :                 "C_10",
     445              :                 false,
     446              :                 "float",
     447              :                 "Pyramid to AEG control matrix [1,0] of a 2x2 matrix " );
     448            0 :     config.add( "pyrNudger.C_11",
     449              :                 "",
     450              :                 "pyrNudger.C_11",
     451              :                 argType::Required,
     452              :                 "pyrNudger",
     453              :                 "C_11",
     454              :                 false,
     455              :                 "float",
     456              :                 "Pyramid to AEG control matrix [1,1] of a 2x2 matrix " );
     457              : 
     458            0 :     config.add( "pyrNudger.ang", "", "pyrNudger.ang", argType::Required, "pyrNudger", "ang", false, "float", "" );
     459            0 :     config.add( "pyrNudger.ang0", "", "pyrNudger.ang0", argType::Required, "pyrNudger0", "ang0", false, "float", "" );
     460            0 :     config.add(
     461              :         "pyrNudger.parity", "", "pyrNudger.parity", argType::Required, "pyrNudger", "parity", false, "float", "" );
     462              : 
     463            0 :     config.add( "pyrNudger.F_sign",
     464              :                 "",
     465              :                 "pyrNudger.F_sign",
     466              :                 argType::Required,
     467              :                 "pyrNudger",
     468              :                 "F_sign",
     469              :                 false,
     470              :                 "int",
     471              :                 "Pyramid to AEG control matrix [1,1] of a 2x2 matrix " );
     472              : 
     473            0 :     config.add( "acqFromGuider.zdSign",
     474              :                 "",
     475              :                 "acqFromGuider.zdSign",
     476              :                 argType::Required,
     477              :                 "acqFromGuider",
     478              :                 "zdSign",
     479              :                 false,
     480              :                 "int",
     481              :                 "Sign of the Zd to rotation angle, +1 or -1, -1 default" );
     482            0 :     config.add( "acqFromGuider.az0",
     483              :                 "",
     484              :                 "acqFromGuider.az0",
     485              :                 argType::Required,
     486              :                 "acqFromGuider",
     487              :                 "az0",
     488              :                 false,
     489              :                 "float",
     490              :                 "az component of acquisition vector a 0 zd." );
     491            0 :     config.add( "acqFromGuider.azoff",
     492              :                 "",
     493              :                 "acqFromGuider.azoff",
     494              :                 argType::Required,
     495              :                 "acqFromGuider",
     496              :                 "azoff",
     497              :                 false,
     498              :                 "float",
     499              :                 "static offset to az component of acquisition vector" );
     500            0 :     config.add( "acqFromGuider.el0",
     501              :                 "",
     502              :                 "acqFromGuider.el0",
     503              :                 argType::Required,
     504              :                 "acqFromGuider",
     505              :                 "el0",
     506              :                 false,
     507              :                 "float",
     508              :                 "el component of acquisition vector a 0 zd." );
     509            0 :     config.add( "acqFromGuider.eloff",
     510              :                 "",
     511              :                 "acqFromGuider.eloff",
     512              :                 argType::Required,
     513              :                 "acqFromGuider",
     514              :                 "eloff",
     515              :                 false,
     516              :                 "float",
     517              :                 "static offset to el component of acquisition vector" );
     518            0 :     config.add( "acqFromGuider.focus",
     519              :                 "",
     520              :                 "acqFromGuider.focus",
     521              :                 argType::Required,
     522              :                 "acqFromGuider",
     523              :                 "focus",
     524              :                 false,
     525              :                 "float",
     526              :                 "static offset for focus acquisition" );
     527              : 
     528            0 :     config.add( "offload.TT_avgInt",
     529              :                 "",
     530              :                 "offload.TT_avgInt",
     531              :                 argType::Required,
     532              :                 "offload",
     533              :                 "TT_avgInt",
     534              :                 false,
     535              :                 "float",
     536              :                 "Woofer to Telescope T/T offload averaging interval [sec] " );
     537            0 :     config.add( "offload.TT_gain",
     538              :                 "",
     539              :                 "offload.TT_gain",
     540              :                 argType::Required,
     541              :                 "offload",
     542              :                 "TT_gain",
     543              :                 false,
     544              :                 "float",
     545              :                 "Woofer to Telescope T/T offload gain" );
     546            0 :     config.add( "offload.TT_thresh",
     547              :                 "",
     548              :                 "offload.TT_thresh",
     549              :                 argType::Required,
     550              :                 "offload",
     551              :                 "TT_thresh",
     552              :                 false,
     553              :                 "float",
     554              :                 "Woofer to Telescope T/T offload threshold" );
     555              : 
     556            0 :     config.add( "offload.lab_TT_C_00",
     557              :                 "",
     558              :                 "offload.lab_TT_C_00",
     559              :                 argType::Required,
     560              :                 "offload",
     561              :                 "lab_TT_C_00",
     562              :                 false,
     563              :                 "float",
     564              :                 "Woofer to TTM T/T offload control matrix [0,0] of a 2x2 matrix" );
     565            0 :     config.add( "offload.lab_TT_C_01",
     566              :                 "",
     567              :                 "offload.lab_TT_C_01",
     568              :                 argType::Required,
     569              :                 "offload",
     570              :                 "lab_TT_C_01",
     571              :                 false,
     572              :                 "float",
     573              :                 "Woofer to TTM T/T offload control matrix [0,1] of a 2x2 matrix " );
     574            0 :     config.add( "offload.lab_TT_C_10",
     575              :                 "",
     576              :                 "offload.lab_TT_C_10",
     577              :                 argType::Required,
     578              :                 "offload",
     579              :                 "lab_TT_C_10",
     580              :                 false,
     581              :                 "float",
     582              :                 "Woofer to TTM T/T offload control matrix [1,0] of a 2x2 matrix " );
     583            0 :     config.add( "offload.lab_TT_C_11",
     584              :                 "",
     585              :                 "offload.lab_TT_C_11",
     586              :                 argType::Required,
     587              :                 "offload",
     588              :                 "lab_TT_C_11",
     589              :                 false,
     590              :                 "float",
     591              :                 "Woofer to TTM T/T offload control matrix [1,1] of a 2x2 matrix " );
     592              : 
     593            0 :     config.add( "offload.TT_C_00",
     594              :                 "",
     595              :                 "offload.TT_C_00",
     596              :                 argType::Required,
     597              :                 "offload",
     598              :                 "TT_C_00",
     599              :                 false,
     600              :                 "float",
     601              :                 "Woofer to Telescope T/T offload control matrix [0,0] of a 2x2 matrix" );
     602            0 :     config.add( "offload.TT_C_01",
     603              :                 "",
     604              :                 "offload.TT_C_01",
     605              :                 argType::Required,
     606              :                 "offload",
     607              :                 "TT_C_01",
     608              :                 false,
     609              :                 "float",
     610              :                 "Woofer to Telescope T/T offload control matrix [0,1] of a 2x2 matrix " );
     611            0 :     config.add( "offload.TT_C_10",
     612              :                 "",
     613              :                 "offload.TT_C_10",
     614              :                 argType::Required,
     615              :                 "offload",
     616              :                 "TT_C_10",
     617              :                 false,
     618              :                 "float",
     619              :                 "Woofer to Telescope T/T offload control matrix [1,0] of a 2x2 matrix " );
     620            0 :     config.add( "offload.TT_C_11",
     621              :                 "",
     622              :                 "offload.TT_C_11",
     623              :                 argType::Required,
     624              :                 "offload",
     625              :                 "TT_C_11",
     626              :                 false,
     627              :                 "float",
     628              :                 "Woofer to Telescope T/T offload control matrix [1,1] of a 2x2 matrix " );
     629              : 
     630            0 :     config.add( "offload.F_avgInt",
     631              :                 "",
     632              :                 "offload.F_avgInt",
     633              :                 argType::Required,
     634              :                 "offload",
     635              :                 "F_avgInt",
     636              :                 false,
     637              :                 "float",
     638              :                 "Woofer to Telescope Focus offload averaging interval [sec] " );
     639            0 :     config.add( "offload.F_gain",
     640              :                 "",
     641              :                 "offload.F_gain",
     642              :                 argType::Required,
     643              :                 "offload",
     644              :                 "F_gain",
     645              :                 false,
     646              :                 "float",
     647              :                 "Woofer to Telescope Focus offload gain" );
     648            0 :     config.add( "offload.F_thresh",
     649              :                 "",
     650              :                 "offload.F_thresh",
     651              :                 argType::Required,
     652              :                 "offload",
     653              :                 "F_thresh",
     654              :                 false,
     655              :                 "float",
     656              :                 "Woofer to Telescope Focus offload threshold" );
     657              : 
     658            0 :     config.add( "offload.CFocus00",
     659              :                 "",
     660              :                 "offload.CFocus00",
     661              :                 argType::Required,
     662              :                 "offload",
     663              :                 "CFocus00",
     664              :                 false,
     665              :                 "float",
     666              :                 "Woofer to Telescope Focus offload control scale factor." );
     667              : 
     668            0 :     config.add( "offload.CComa00",
     669              :                 "",
     670              :                 "offload.CComa00",
     671              :                 argType::Required,
     672              :                 "offload",
     673              :                 "CComa00",
     674              :                 false,
     675              :                 "float",
     676              :                 "Woofer to Telescope Coma offload control matrix [0,0] of a 2x2 matrix" );
     677            0 :     config.add( "offload.CComa01",
     678              :                 "",
     679              :                 "offload.CComa01",
     680              :                 argType::Required,
     681              :                 "offload",
     682              :                 "CComa01",
     683              :                 false,
     684              :                 "float",
     685              :                 "Woofer to Telescope Coma offload control matrix [0,1] of a 2x2 matrix " );
     686            0 :     config.add( "offload.CComa10",
     687              :                 "",
     688              :                 "offload.CComa10",
     689              :                 argType::Required,
     690              :                 "offload",
     691              :                 "CComa10",
     692              :                 false,
     693              :                 "float",
     694              :                 "Woofer to Telescope Coma offload control matrix [1,0] of a 2x2 matrix " );
     695            0 :     config.add( "offload.CComa11",
     696              :                 "",
     697              :                 "offload.CComa11",
     698              :                 argType::Required,
     699              :                 "offload",
     700              :                 "CComa11",
     701              :                 false,
     702              :                 "float",
     703              :                 "Woofer to Telescope Coma offload control matrix [1,1] of a 2x2 matrix " );
     704              : 
     705            0 :     config.add( "device.address",
     706              :                 "",
     707              :                 "device.address",
     708              :                 argType::Required,
     709              :                 "device",
     710              :                 "address",
     711              :                 false,
     712              :                 "string",
     713              :                 "The IP address or resolvable name of the TCS." );
     714            0 :     config.add( "device.port",
     715              :                 "",
     716              :                 "device.port",
     717              :                 argType::Required,
     718              :                 "device",
     719              :                 "port",
     720              :                 false,
     721              :                 "int",
     722              :                 "The IP port for TCS communications. Should be the command port.  Default is 5811." );
     723              : 
     724            0 :     dev::ioDevice::setupConfig( config );
     725            0 :     dev::telemeter<tcsInterface>::setupConfig( config );
     726            0 : }
     727              : 
     728            0 : inline int tcsInterface::loadConfigImpl( mx::app::appConfigurator &_config )
     729              : {
     730              : 
     731            0 :     _config( m_labMode, "labMode" );
     732              : 
     733            0 :     _config( m_pyrNudge_C_00, "pyrNudger.C_00" );
     734            0 :     _config( m_pyrNudge_C_01, "pyrNudger.C_01" );
     735            0 :     _config( m_pyrNudge_C_10, "pyrNudger.C_10" );
     736            0 :     _config( m_pyrNudge_C_11, "pyrNudger.C_11" );
     737              : 
     738            0 :     _config( m_pyrNudge_ang, "pyrNudger.ang" );
     739            0 :     _config( m_pyrNudge_ang0, "pyrNudger.ang0" );
     740            0 :     _config( m_pyrNudge_parity, "pyrNudger.parity" );
     741              : 
     742            0 :     _config( m_acqZdSign, "acqFromGuider.zdSign" );
     743            0 :     _config( m_acqAz0, "acqFromGuider.az0" );
     744            0 :     _config( m_acqAzOff, "acqFromGuider.azoff" );
     745            0 :     _config( m_acqEl0, "acqFromGuider.el0" );
     746            0 :     _config( m_acqElOff, "acqFromGuider.eloff" );
     747            0 :     _config( m_acqFocus, "acqFromGuider.focus" );
     748              : 
     749            0 :     _config( m_offlTT_avgInt, "offload.TT_avgInt" );
     750            0 :     _config( m_offlTT_gain, "offload.TT_gain" );
     751            0 :     _config( m_offlTT_thresh, "offload.TT_thresh" );
     752              : 
     753            0 :     _config( m_lab_offlTT_C_00, "offload.lab_TT_C_00" );
     754            0 :     _config( m_lab_offlTT_C_01, "offload.lab_TT_C_01" );
     755            0 :     _config( m_lab_offlTT_C_10, "offload.lab_TT_C_10" );
     756            0 :     _config( m_lab_offlTT_C_11, "offload.lab_TT_C_11" );
     757              : 
     758            0 :     _config( m_offlTT_C_00, "offload.TT_C_00" );
     759            0 :     _config( m_offlTT_C_01, "offload.TT_C_01" );
     760            0 :     _config( m_offlTT_C_10, "offload.TT_C_10" );
     761            0 :     _config( m_offlTT_C_11, "offload.TT_C_11" );
     762              : 
     763            0 :     _config( m_offlF_avgInt, "offload.F_avgInt" );
     764            0 :     _config( m_offlF_gain, "offload.F_gain" );
     765            0 :     _config( m_offlF_thresh, "offload.F_thresh" );
     766              : 
     767            0 :     _config( m_offlCFocus_00, "offload.CFocus00" );
     768              : 
     769            0 :     _config( m_offlCComa_00, "offload.CComa00" );
     770            0 :     _config( m_offlCComa_01, "offload.CComa01" );
     771            0 :     _config( m_offlCComa_10, "offload.CComa10" );
     772            0 :     _config( m_offlCComa_11, "offload.CComa11" );
     773              : 
     774            0 :     _config( m_deviceAddr, "device.address" );
     775            0 :     _config( m_devicePort, "device.port" );
     776              : 
     777            0 :     dev::ioDevice::loadConfig( _config );
     778              : 
     779            0 :     dev::telemeter<tcsInterface>::loadConfig( _config );
     780              : 
     781            0 :     return 0;
     782              : }
     783              : 
     784            0 : inline void tcsInterface::loadConfig()
     785              : {
     786            0 :     loadConfigImpl( config );
     787            0 : }
     788              : 
     789            0 : inline int tcsInterface::appStartup()
     790              : {
     791            0 :     CREATE_REG_INDI_NEW_TOGGLESWITCH( m_indiP_labMode, "labMode" );
     792            0 :     if( m_labMode )
     793              :     {
     794            0 :         m_indiP_labMode["toggle"].setSwitchState( pcf::IndiElement::On );
     795            0 :         log<text_log>( "lab mode ON", logPrio::LOG_NOTICE );
     796              :     }
     797              :     else
     798              :     {
     799            0 :         m_indiP_labMode["toggle"].setSwitchState( pcf::IndiElement::Off );
     800            0 :         log<text_log>( "lab mode OFF", logPrio::LOG_NOTICE );
     801              :     }
     802              : 
     803            0 :     updateSwitchIfChanged( m_indiP_offlTTenable, "toggle", pcf::IndiElement::On, INDI_OK );
     804              : 
     805            0 :     createROIndiNumber( m_indiP_teltime, "teltime", "Telscope Time", "TCS" );
     806            0 :     indi::addNumberElement<double>(
     807            0 :         m_indiP_teltime, "sidereal_time", 0, std::numeric_limits<double>::max(), 0, "%0.6f" );
     808            0 :     m_indiP_teltime["sidereal_time"] = m_telST;
     809            0 :     registerIndiPropertyReadOnly( m_indiP_teltime );
     810              : 
     811            0 :     createROIndiNumber( m_indiP_telpos, "telpos", "Telscope Position", "TCS" );
     812            0 :     indi::addNumberElement<double>( m_indiP_telpos, "epoch", 0, std::numeric_limits<double>::max(), 0, "%0.6f" );
     813            0 :     m_indiP_telpos["epoch"] = m_telEpoch;
     814            0 :     indi::addNumberElement<double>( m_indiP_telpos, "ra", 0, 360, 0, "%0.6f" );
     815            0 :     m_indiP_telpos["ra"] = m_telRA;
     816            0 :     indi::addNumberElement<double>( m_indiP_telpos, "dec", -90, 90, 0, "%0.6f" );
     817            0 :     m_indiP_telpos["dec"] = m_telDec;
     818            0 :     indi::addNumberElement<double>( m_indiP_telpos, "el", 0, 90, 0, "%0.6f" );
     819            0 :     m_indiP_telpos["el"] = m_telEl;
     820            0 :     indi::addNumberElement<double>( m_indiP_telpos, "ha", -180, 160, 0, "%0.6f" );
     821            0 :     m_indiP_telpos["ha"] = m_telHA;
     822            0 :     indi::addNumberElement<double>( m_indiP_telpos, "am", 0, 4, 0, "%0.2f" );
     823            0 :     m_indiP_telpos["am"] = m_telAM;
     824            0 :     indi::addNumberElement<double>( m_indiP_telpos, "rotoff", 0, 360, 0, "%0.6f" );
     825            0 :     m_indiP_telpos["rotoff"] = m_telRotOff;
     826              : 
     827            0 :     registerIndiPropertyReadOnly( m_indiP_telpos );
     828              : 
     829            0 :     createROIndiNumber( m_indiP_teldata, "teldata", "Telscope Data", "TCS" );
     830            0 :     indi::addNumberElement<int>( m_indiP_teldata, "roi", 0, 10, 1, "%d" );
     831            0 :     m_indiP_teldata["roi"] = m_telROI;
     832            0 :     indi::addNumberElement<int>( m_indiP_teldata, "tracking", 0, 1, 1, "%d" );
     833            0 :     m_indiP_teldata["tracking"] = m_telTracking;
     834            0 :     indi::addNumberElement<int>( m_indiP_teldata, "guiding", 0, 1, 1, "%d" );
     835            0 :     m_indiP_teldata["guiding"] = m_telGuiding;
     836            0 :     indi::addNumberElement<int>( m_indiP_teldata, "slewing", 0, 1, 1, "%d" );
     837            0 :     m_indiP_teldata["slewing"] = m_telSlewing;
     838            0 :     indi::addNumberElement<int>( m_indiP_teldata, "guider_moving", 0, 1, 1, "%d" );
     839            0 :     m_indiP_teldata["guider_moving"] = m_telGuiderMoving;
     840            0 :     indi::addNumberElement<double>( m_indiP_teldata, "az", 0, 360, 0, "%0.6f" );
     841            0 :     m_indiP_teldata["az"] = m_telAz;
     842            0 :     indi::addNumberElement<double>( m_indiP_teldata, "zd", 0, 90, 0, "%0.6f" );
     843            0 :     m_indiP_teldata["zd"] = m_telZd;
     844            0 :     indi::addNumberElement<double>( m_indiP_teldata, "pa", 0, 360, 0, "%0.6f" );
     845            0 :     m_indiP_teldata["pa"] = m_telPA;
     846            0 :     indi::addNumberElement<double>( m_indiP_teldata, "dome_az", 0, 360, 0, "%0.6f" );
     847            0 :     m_indiP_teldata["dome_az"] = m_telDomeAz;
     848            0 :     indi::addNumberElement<int>( m_indiP_teldata, "dome_stat", 0, 1, 1, "%d" );
     849            0 :     m_indiP_teldata["dome_stat"] = m_telDomeStat;
     850              : 
     851            0 :     registerIndiPropertyReadOnly( m_indiP_teldata );
     852              : 
     853            0 :     createROIndiText( m_indiP_catalog, "catalog", "object", "Catalog Entry", "TCS", "Object Name" );
     854            0 :     m_indiP_catalog.add( pcf::IndiElement( "rotmode" ) );
     855            0 :     m_indiP_catalog["rotmode"].setLabel( "Rotator Mode" );
     856              : 
     857            0 :     registerIndiPropertyReadOnly( m_indiP_catalog );
     858              : 
     859            0 :     createROIndiNumber( m_indiP_catdata, "catdata", "Catalog Entry Data", "TCS" );
     860            0 :     indi::addNumberElement<double>( m_indiP_catdata, "ra", 0, 360, 0, "%0.6f" );
     861            0 :     m_indiP_catdata["ra"] = m_catRA;
     862            0 :     indi::addNumberElement<double>( m_indiP_catdata, "dec", -90, 90, 0, "%0.6f" );
     863            0 :     m_indiP_catdata["dec"] = m_catDec;
     864            0 :     indi::addNumberElement<double>( m_indiP_catdata, "epoch", 0, std::numeric_limits<double>::max(), 0, "%0.6f" );
     865            0 :     m_indiP_catdata["epoch"] = m_catEp;
     866            0 :     indi::addNumberElement<double>( m_indiP_catdata, "rotoff", 0, 360, 0, "%0.6f" );
     867            0 :     m_indiP_catdata["rotoff"] = m_catRo;
     868              : 
     869            0 :     registerIndiPropertyReadOnly( m_indiP_catdata );
     870              : 
     871            0 :     createROIndiNumber( m_indiP_vaneend, "vaneend", "Vane End Data", "TCS" );
     872            0 :     indi::addNumberElement<double>( m_indiP_vaneend,
     873              :                                     "secz",
     874            0 :                                     std::numeric_limits<double>::lowest(),
     875            0 :                                     std::numeric_limits<double>::max(),
     876              :                                     0,
     877              :                                     "%0.6f" );
     878            0 :     m_indiP_vaneend["secz"] = m_telSecZ;
     879            0 :     indi::addNumberElement<double>( m_indiP_vaneend,
     880              :                                     "encz",
     881            0 :                                     std::numeric_limits<double>::lowest(),
     882            0 :                                     std::numeric_limits<double>::max(),
     883              :                                     0,
     884              :                                     "%0.6f" );
     885            0 :     m_indiP_vaneend["encz"] = m_telEncZ;
     886            0 :     indi::addNumberElement<double>( m_indiP_vaneend,
     887              :                                     "secx",
     888            0 :                                     std::numeric_limits<double>::lowest(),
     889            0 :                                     std::numeric_limits<double>::max(),
     890              :                                     0,
     891              :                                     "%0.6f" );
     892            0 :     m_indiP_vaneend["secx"] = m_telSecX;
     893            0 :     indi::addNumberElement<double>( m_indiP_vaneend,
     894              :                                     "encx",
     895            0 :                                     std::numeric_limits<double>::lowest(),
     896            0 :                                     std::numeric_limits<double>::max(),
     897              :                                     0,
     898              :                                     "%0.6f" );
     899            0 :     m_indiP_vaneend["encx"] = m_telEncX;
     900            0 :     indi::addNumberElement<double>( m_indiP_vaneend,
     901              :                                     "secy",
     902            0 :                                     std::numeric_limits<double>::lowest(),
     903            0 :                                     std::numeric_limits<double>::max(),
     904              :                                     0,
     905              :                                     "%0.6f" );
     906            0 :     m_indiP_vaneend["secy"] = m_telSecY;
     907            0 :     indi::addNumberElement<double>( m_indiP_vaneend,
     908              :                                     "ency",
     909            0 :                                     std::numeric_limits<double>::lowest(),
     910            0 :                                     std::numeric_limits<double>::max(),
     911              :                                     0,
     912              :                                     "%0.6f" );
     913            0 :     m_indiP_vaneend["ency"] = m_telEncY;
     914            0 :     indi::addNumberElement<double>( m_indiP_vaneend,
     915              :                                     "sech",
     916            0 :                                     std::numeric_limits<double>::lowest(),
     917            0 :                                     std::numeric_limits<double>::max(),
     918              :                                     0,
     919              :                                     "%0.6f" );
     920            0 :     m_indiP_vaneend["sech"] = m_telSecH;
     921            0 :     indi::addNumberElement<double>( m_indiP_vaneend,
     922              :                                     "ench",
     923            0 :                                     std::numeric_limits<double>::lowest(),
     924            0 :                                     std::numeric_limits<double>::max(),
     925              :                                     0,
     926              :                                     "%0.6f" );
     927            0 :     m_indiP_vaneend["ench"] = m_telEncH;
     928            0 :     indi::addNumberElement<double>( m_indiP_vaneend,
     929              :                                     "secv",
     930            0 :                                     std::numeric_limits<double>::lowest(),
     931            0 :                                     std::numeric_limits<double>::max(),
     932              :                                     0,
     933              :                                     "%0.6f" );
     934            0 :     m_indiP_vaneend["secv"] = m_telSecV;
     935            0 :     indi::addNumberElement<double>( m_indiP_vaneend,
     936              :                                     "encv",
     937            0 :                                     std::numeric_limits<double>::lowest(),
     938            0 :                                     std::numeric_limits<double>::max(),
     939              :                                     0,
     940              :                                     "%0.6f" );
     941            0 :     m_indiP_vaneend["encv"] = m_telEncV;
     942              : 
     943            0 :     registerIndiPropertyReadOnly( m_indiP_vaneend );
     944              : 
     945            0 :     createROIndiNumber( m_indiP_env, "environment", "Environment Data", "TCS" );
     946            0 :     indi::addNumberElement<double>( m_indiP_env,
     947              :                                     "temp-out",
     948            0 :                                     std::numeric_limits<double>::lowest(),
     949            0 :                                     std::numeric_limits<double>::max(),
     950              :                                     0,
     951              :                                     "%0.2f" );
     952            0 :     m_indiP_env["temp-out"] = m_wxtemp;
     953            0 :     indi::addNumberElement<double>( m_indiP_env,
     954              :                                     "pressure",
     955            0 :                                     std::numeric_limits<double>::lowest(),
     956            0 :                                     std::numeric_limits<double>::max(),
     957              :                                     0,
     958              :                                     "%0.2f" );
     959            0 :     m_indiP_env["pressure"] = m_wxpres;
     960            0 :     indi::addNumberElement<double>( m_indiP_env,
     961              :                                     "humidity",
     962            0 :                                     std::numeric_limits<double>::lowest(),
     963            0 :                                     std::numeric_limits<double>::max(),
     964              :                                     0,
     965              :                                     "%0.2f" );
     966            0 :     m_indiP_env["humidity"] = m_wxhumid;
     967            0 :     indi::addNumberElement<double>(
     968            0 :         m_indiP_env, "wind", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f" );
     969            0 :     m_indiP_env["wind"] = m_wxwind;
     970            0 :     indi::addNumberElement<double>(
     971            0 :         m_indiP_env, "winddir", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f" );
     972            0 :     m_indiP_env["winddir"] = m_wxwdir;
     973            0 :     indi::addNumberElement<double>( m_indiP_env,
     974              :                                     "temp-truss",
     975            0 :                                     std::numeric_limits<double>::lowest(),
     976            0 :                                     std::numeric_limits<double>::max(),
     977              :                                     0,
     978              :                                     "%0.2f" );
     979            0 :     m_indiP_env["temp-truss"] = m_ttruss;
     980            0 :     indi::addNumberElement<double>( m_indiP_env,
     981              :                                     "temp-cell",
     982            0 :                                     std::numeric_limits<double>::lowest(),
     983            0 :                                     std::numeric_limits<double>::max(),
     984              :                                     0,
     985              :                                     "%0.2f" );
     986            0 :     m_indiP_env["temp-cell"] = m_tcell;
     987            0 :     indi::addNumberElement<double>( m_indiP_env,
     988              :                                     "temp-seccell",
     989            0 :                                     std::numeric_limits<double>::lowest(),
     990            0 :                                     std::numeric_limits<double>::max(),
     991              :                                     0,
     992              :                                     "%0.2f" );
     993            0 :     m_indiP_env["temp-seccell"] = m_tseccell;
     994            0 :     indi::addNumberElement<double>( m_indiP_env,
     995              :                                     "temp-amb",
     996            0 :                                     std::numeric_limits<double>::lowest(),
     997            0 :                                     std::numeric_limits<double>::max(),
     998              :                                     0,
     999              :                                     "%0.2f" );
    1000            0 :     m_indiP_env["temp-amb"] = m_tambient;
    1001            0 :     indi::addNumberElement<double>( m_indiP_env,
    1002              :                                     "dewpoint",
    1003            0 :                                     std::numeric_limits<double>::lowest(),
    1004            0 :                                     std::numeric_limits<double>::max(),
    1005              :                                     0,
    1006              :                                     "%0.2f" );
    1007            0 :     m_indiP_env["dewpoint"] = m_wxdewpoint;
    1008              : 
    1009            0 :     registerIndiPropertyReadOnly( m_indiP_env );
    1010              : 
    1011            0 :     createROIndiNumber( m_indiP_seeing, "seeing", "Seeing Data", "TCS" );
    1012            0 :     indi::addNumberElement<unsigned>( m_indiP_seeing,
    1013              :                                       "dimm_time",
    1014            0 :                                       std::numeric_limits<unsigned>::lowest(),
    1015            0 :                                       std::numeric_limits<unsigned>::max(),
    1016            0 :                                       0,
    1017              :                                       "%d" );
    1018            0 :     m_indiP_seeing["dimm_time"] = m_dimm_time;
    1019            0 :     indi::addNumberElement<double>( m_indiP_seeing,
    1020              :                                     "dimm_fwhm_corr",
    1021            0 :                                     std::numeric_limits<double>::lowest(),
    1022            0 :                                     std::numeric_limits<double>::max(),
    1023              :                                     0,
    1024              :                                     "%0.2f" );
    1025            0 :     m_indiP_seeing["dimm_fwhm_corr"] = m_dimm_fwhm_corr;
    1026            0 :     indi::addNumberElement<unsigned>( m_indiP_seeing,
    1027              :                                       "mag1_time",
    1028            0 :                                       std::numeric_limits<unsigned>::lowest(),
    1029            0 :                                       std::numeric_limits<unsigned>::max(),
    1030            0 :                                       0,
    1031              :                                       "%d" );
    1032            0 :     m_indiP_seeing["mag1_time"] = m_mag1_time;
    1033            0 :     indi::addNumberElement<double>( m_indiP_seeing,
    1034              :                                     "mag1_fwhm_corr",
    1035            0 :                                     std::numeric_limits<double>::lowest(),
    1036            0 :                                     std::numeric_limits<double>::max(),
    1037              :                                     0,
    1038              :                                     "%0.2f" );
    1039            0 :     m_indiP_seeing["mag1_fwhm_corr"] = m_mag1_fwhm_corr;
    1040            0 :     indi::addNumberElement<unsigned>( m_indiP_seeing,
    1041              :                                       "mag2_time",
    1042            0 :                                       std::numeric_limits<unsigned>::lowest(),
    1043            0 :                                       std::numeric_limits<unsigned>::max(),
    1044            0 :                                       0,
    1045              :                                       "%d" );
    1046            0 :     m_indiP_seeing["mag2_time"] = m_mag2_time;
    1047            0 :     indi::addNumberElement<double>( m_indiP_seeing,
    1048              :                                     "mag2_fwhm_corr",
    1049            0 :                                     std::numeric_limits<double>::lowest(),
    1050            0 :                                     std::numeric_limits<double>::max(),
    1051              :                                     0,
    1052              :                                     "%0.2f" );
    1053            0 :     m_indiP_seeing["mag2_fwhm_corr"] = m_mag2_fwhm_corr;
    1054              : 
    1055            0 :     registerIndiPropertyReadOnly( m_indiP_seeing );
    1056              : 
    1057            0 :     signal( SIGPIPE, SIG_IGN );
    1058              : 
    1059            0 :     if( dev::ioDevice::appStartup() < 0 )
    1060              :     {
    1061            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
    1062              :     }
    1063              : 
    1064            0 :     if( dev::telemeter<tcsInterface>::appStartup() < 0 )
    1065              :     {
    1066            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
    1067              :     }
    1068              : 
    1069            0 :     REG_INDI_NEWPROP( m_indiP_pyrNudge, "pyrNudge", pcf::IndiProperty::Number );
    1070            0 :     m_indiP_pyrNudge.add( pcf::IndiElement( "y" ) );
    1071            0 :     m_indiP_pyrNudge.add( pcf::IndiElement( "x" ) );
    1072            0 :     m_indiP_pyrNudge.add( pcf::IndiElement( "z" ) );
    1073              : 
    1074            0 :     createStandardIndiRequestSw( m_indiP_acqFromGuider, "acqFromGuider" );
    1075            0 :     if( registerIndiPropertyNew( m_indiP_acqFromGuider, st_newCallBack_m_indiP_acqFromGuider ) < 0 )
    1076              :     {
    1077            0 :         log<software_error>( { __FILE__, __LINE__ } );
    1078            0 :         return -1;
    1079              :     }
    1080              : 
    1081            0 :     createStandardIndiRequestSw( m_indiP_offlTTdump, "offlTT_dump" );
    1082            0 :     if( registerIndiPropertyNew( m_indiP_offlTTdump, st_newCallBack_m_indiP_offlTTdump ) < 0 )
    1083              :     {
    1084            0 :         log<software_error>( { __FILE__, __LINE__ } );
    1085            0 :         return -1;
    1086              :     }
    1087              : 
    1088            0 :     createStandardIndiToggleSw( m_indiP_offlTTenable, "offlTT_enable" );
    1089            0 :     if( registerIndiPropertyNew( m_indiP_offlTTenable, st_newCallBack_m_indiP_offlTTenable ) < 0 )
    1090              :     {
    1091            0 :         log<software_error>( { __FILE__, __LINE__ } );
    1092            0 :         return -1;
    1093              :     }
    1094              : 
    1095            0 :     createStandardIndiNumber( m_indiP_offlTTavgInt, "offlTT_avgInt", 0, 3600, 1, "%d" );
    1096            0 :     m_indiP_offlTTavgInt["current"].set( m_offlTT_avgInt );
    1097            0 :     m_indiP_offlTTavgInt["target"].set( m_offlTT_avgInt );
    1098            0 :     if( registerIndiPropertyNew( m_indiP_offlTTavgInt, st_newCallBack_m_indiP_offlTTavgInt ) < 0 )
    1099              :     {
    1100            0 :         log<software_error>( { __FILE__, __LINE__ } );
    1101            0 :         return -1;
    1102              :     }
    1103              : 
    1104            0 :     createStandardIndiNumber( m_indiP_offlTTgain, "offlTT_gain", 0.0, 1.0, 0.0, "%0.2f" );
    1105            0 :     m_indiP_offlTTgain["current"].set( m_offlTT_gain );
    1106            0 :     m_indiP_offlTTgain["target"].set( m_offlTT_gain );
    1107            0 :     if( registerIndiPropertyNew( m_indiP_offlTTgain, st_newCallBack_m_indiP_offlTTgain ) < 0 )
    1108              :     {
    1109            0 :         log<software_error>( { __FILE__, __LINE__ } );
    1110            0 :         return -1;
    1111              :     }
    1112              : 
    1113            0 :     createStandardIndiNumber( m_indiP_offlTTthresh, "offlTT_thresh", 0.0, 1.0, 0.0, "%0.2f" );
    1114            0 :     m_indiP_offlTTthresh["current"].set( m_offlTT_thresh );
    1115            0 :     m_indiP_offlTTthresh["target"].set( m_offlTT_thresh );
    1116            0 :     if( registerIndiPropertyNew( m_indiP_offlTTthresh, st_newCallBack_m_indiP_offlTTthresh ) < 0 )
    1117              :     {
    1118            0 :         log<software_error>( { __FILE__, __LINE__ } );
    1119            0 :         return -1;
    1120              :     }
    1121              : 
    1122            0 :     createStandardIndiRequestSw( m_indiP_offlFdump, "offlF_dump" );
    1123            0 :     if( registerIndiPropertyNew( m_indiP_offlFdump, st_newCallBack_m_indiP_offlFdump ) < 0 )
    1124              :     {
    1125            0 :         log<software_error>( { __FILE__, __LINE__ } );
    1126            0 :         return -1;
    1127              :     }
    1128              : 
    1129            0 :     createStandardIndiToggleSw( m_indiP_offlFenable, "offlF_enable" );
    1130            0 :     if( registerIndiPropertyNew( m_indiP_offlFenable, st_newCallBack_m_indiP_offlFenable ) < 0 )
    1131              :     {
    1132            0 :         log<software_error>( { __FILE__, __LINE__ } );
    1133            0 :         return -1;
    1134              :     }
    1135              : 
    1136            0 :     createStandardIndiNumber( m_indiP_offlFavgInt, "offlF_avgInt", 0, 3600, 1, "%d" );
    1137            0 :     m_indiP_offlFavgInt["current"].set( m_offlF_avgInt );
    1138            0 :     m_indiP_offlFavgInt["target"].set( m_offlF_avgInt );
    1139            0 :     if( registerIndiPropertyNew( m_indiP_offlFavgInt, st_newCallBack_m_indiP_offlFavgInt ) < 0 )
    1140              :     {
    1141            0 :         log<software_error>( { __FILE__, __LINE__ } );
    1142            0 :         return -1;
    1143              :     }
    1144              : 
    1145            0 :     createStandardIndiNumber( m_indiP_offlFgain, "offlF_gain", 0.0, 1.0, 0.0, "%0.2f" );
    1146            0 :     m_indiP_offlFgain["current"].set( m_offlF_gain );
    1147            0 :     m_indiP_offlFgain["target"].set( m_offlF_gain );
    1148            0 :     if( registerIndiPropertyNew( m_indiP_offlFgain, st_newCallBack_m_indiP_offlFgain ) < 0 )
    1149              :     {
    1150            0 :         log<software_error>( { __FILE__, __LINE__ } );
    1151            0 :         return -1;
    1152              :     }
    1153              : 
    1154            0 :     createStandardIndiNumber( m_indiP_offlFthresh, "offlF_thresh", 0.0, 1.0, 0.0, "%0.2f" );
    1155            0 :     m_indiP_offlFthresh["current"].set( m_offlF_thresh );
    1156            0 :     m_indiP_offlFthresh["target"].set( m_offlF_thresh );
    1157            0 :     if( registerIndiPropertyNew( m_indiP_offlFthresh, st_newCallBack_m_indiP_offlFthresh ) < 0 )
    1158              :     {
    1159            0 :         log<software_error>( { __FILE__, __LINE__ } );
    1160            0 :         return -1;
    1161              :     }
    1162              : 
    1163              :     // Get the loop state for managing offloading
    1164            0 :     REG_INDI_SETPROP( m_indiP_loopState, "holoop", "loop_state" );
    1165              : 
    1166            0 :     m_offloadRequests.resize( 5 );
    1167            0 :     for( size_t n = 0; n < m_offloadRequests.size(); ++n )
    1168            0 :         m_offloadRequests[n].resize( 10, 0 );
    1169              : 
    1170            0 :     if( threadStart( m_offloadThread,
    1171            0 :                      m_offloadThreadInit,
    1172            0 :                      m_offloadThreadID,
    1173            0 :                      m_offloadThreadProp,
    1174              :                      0,
    1175              :                      "",
    1176              :                      "offload",
    1177              :                      this,
    1178            0 :                      offloadThreadStart ) < 0 )
    1179              :     {
    1180            0 :         log<software_error>( { __FILE__, __LINE__ } );
    1181            0 :         return -1;
    1182              :     }
    1183              : 
    1184              :     // Register to receive the coeff updates from Kyle
    1185            0 :     REG_INDI_SETPROP( m_indiP_offloadCoeffs, "w2tcsOffloader", "zCoeffs" );
    1186              : 
    1187            0 :     state( stateCodes::NOTCONNECTED );
    1188              : 
    1189            0 :     return 0;
    1190              : }
    1191              : 
    1192            0 : inline int tcsInterface::appLogic()
    1193              : {
    1194            0 :     if( state() == stateCodes::ERROR )
    1195              :     {
    1196            0 :         int rv = m_sock.serialInit( m_deviceAddr.c_str(), m_devicePort );
    1197              : 
    1198            0 :         if( rv != 0 )
    1199              :         {
    1200            0 :             state( stateCodes::NOTCONNECTED );
    1201            0 :             log<text_log>( "In state ERROR, connection lost, will retry.", logPrio::LOG_ERROR );
    1202            0 :             return 0;
    1203              :         }
    1204              : 
    1205            0 :         log<text_log>( "In state ERROR, but connected.  Trying to continue.", logPrio::LOG_WARNING );
    1206              : 
    1207            0 :         state( stateCodes::CONNECTED );
    1208              :     }
    1209              : 
    1210            0 :     if( state() == stateCodes::NOTCONNECTED )
    1211              :     {
    1212              :         static int lastrv    = 0; // Used to handle a change in error within the same state.  Make general?
    1213              :         static int lasterrno = 0;
    1214              : 
    1215            0 :         int rv = m_sock.serialInit( m_deviceAddr.c_str(), m_devicePort );
    1216              : 
    1217            0 :         if( rv == 0 )
    1218              :         {
    1219            0 :             state( stateCodes::CONNECTED );
    1220              : 
    1221            0 :             if( !stateLogged() )
    1222              :             {
    1223            0 :                 std::stringstream logs;
    1224            0 :                 logs << "Connected to " << m_deviceAddr << ":" << m_devicePort;
    1225            0 :                 log<text_log>( logs.str() );
    1226            0 :             }
    1227            0 :             lastrv    = rv;
    1228            0 :             lasterrno = errno;
    1229              :         }
    1230              :         else
    1231              :         {
    1232            0 :             if( !stateLogged() )
    1233              :             {
    1234            0 :                 log<text_log>( { "Failed to connect to " + m_deviceAddr + ":" + std::to_string( m_devicePort ) },
    1235              :                                logPrio::LOG_ERROR );
    1236              :             }
    1237            0 :             if( rv != lastrv )
    1238              :             {
    1239            0 :                 log<software_error>( { __FILE__, __LINE__, 0, rv, tty::ttyErrorString( rv ) } );
    1240            0 :                 lastrv = rv;
    1241              :             }
    1242            0 :             if( errno != lasterrno )
    1243              :             {
    1244            0 :                 log<software_error>( { __FILE__, __LINE__, errno } );
    1245            0 :                 lasterrno = errno;
    1246              :             }
    1247            0 :             return 0;
    1248              :         }
    1249              :     }
    1250              : 
    1251            0 :     if( state() == stateCodes::CONNECTED )
    1252              :     {
    1253            0 :         std::string response;
    1254              : 
    1255              :         // If any of these are unsuccesful we go around without recording data.
    1256            0 :         if( getTelTime() < 0 )
    1257              :         {
    1258            0 :             return 0; // app state will be set based on what the error was
    1259              :         }
    1260              : 
    1261            0 :         if( getTelPos() < 0 )
    1262              :         {
    1263            0 :             return 0; // app state will be set based on what the error was
    1264              :         }
    1265              : 
    1266            0 :         if( getTelData() < 0 )
    1267              :         {
    1268            0 :             return 0;
    1269              :         }
    1270              : 
    1271            0 :         if( getCatData() < 0 )
    1272              :         {
    1273            0 :             return 0;
    1274              :         }
    1275              : 
    1276            0 :         if( getVaneData() < 0 )
    1277              :         {
    1278            0 :             return 0;
    1279              :         }
    1280              : 
    1281            0 :         if( getEnvData() < 0 )
    1282              :         {
    1283            0 :             return 0;
    1284              :         }
    1285              : 
    1286            0 :         if( getSeeing() < 0 )
    1287              :         {
    1288            0 :             return 0;
    1289              :         }
    1290              : 
    1291            0 :         telemeter<tcsInterface>::appLogic();
    1292              : 
    1293            0 :         if( updateINDI() < 0 )
    1294              :         {
    1295            0 :             log<text_log>( "Error from updateINDI", logPrio::LOG_ERROR );
    1296            0 :             return 0;
    1297              :         }
    1298            0 :     }
    1299              : 
    1300            0 :     return 0;
    1301              : }
    1302              : 
    1303            0 : inline int tcsInterface::appShutdown()
    1304              : {
    1305              :     // Wait for offload thread to exit on m_shutdown.
    1306            0 :     if( m_offloadThread.joinable() )
    1307              :     {
    1308              :         try
    1309              :         {
    1310            0 :             m_offloadThread.join(); // this will throw if it was already joined
    1311              :         }
    1312            0 :         catch( ... )
    1313              :         {
    1314            0 :         }
    1315              :     }
    1316              : 
    1317            0 :     return 0;
    1318              : }
    1319              : 
    1320            0 : inline int tcsInterface::getMagTelStatus( std::string &response, const std::string &statreq )
    1321              : {
    1322              : 
    1323              :     int         stat;
    1324              :     char        answer[512];
    1325            0 :     std::string statreq_nl;
    1326              : 
    1327              : #ifdef LOG_TCS_STATUS
    1328              :     log<text_log>( "Sending status request: " + statreq );
    1329              : #endif
    1330              : 
    1331            0 :     std::lock_guard<std::mutex> guard( m_tcsMutex );
    1332              : 
    1333            0 :     statreq_nl = statreq;
    1334            0 :     statreq_nl += '\n';
    1335            0 :     stat = m_sock.serialOut( statreq_nl.c_str(), statreq_nl.length() );
    1336              : 
    1337            0 :     if( stat != NETSERIAL_E_NOERROR )
    1338              :     {
    1339            0 :         log<text_log>( "Error sending status request: " + statreq, logPrio::LOG_ERROR );
    1340            0 :         response = "";
    1341            0 :         return 0;
    1342              :     }
    1343              : 
    1344            0 :     stat = m_sock.serialInString( answer, 512, m_readTimeout, '\n' );
    1345              : 
    1346            0 :     if( stat <= 0 )
    1347              :     {
    1348            0 :         log<text_log>( "No response received to status request: " + statreq, logPrio::LOG_ERROR );
    1349            0 :         response = "";
    1350            0 :         return 0;
    1351              :     }
    1352              : 
    1353            0 :     char *nl = strchr( answer, '\n' );
    1354            0 :     if( nl )
    1355            0 :         answer[nl - answer] = '\0';
    1356              : 
    1357              : #ifdef LOG_TCS_STATUS
    1358              :     log<text_log>( std::string( "Received response: " ) + answer );
    1359              : #endif
    1360              : 
    1361            0 :     response = answer;
    1362              : 
    1363            0 :     return 0;
    1364            0 : } // int tcsInterface::getMagTelStatus
    1365              : 
    1366            0 : inline int tcsInterface::sendMagTelCommand( const std::string &command, int timeout )
    1367              : {
    1368              :     int         stat;
    1369              :     char        answer[512];
    1370            0 :     std::string command_nl;
    1371              : 
    1372              : #ifdef LOG_TCS_STATUS
    1373              :     log<text_log>( "Sending command: " + command );
    1374              : #endif
    1375              : 
    1376            0 :     std::lock_guard<std::mutex> guard( m_tcsMutex );
    1377              : 
    1378            0 :     command_nl = command;
    1379            0 :     command_nl += '\n';
    1380            0 :     stat = m_sock.serialOut( command_nl.c_str(), command_nl.length() );
    1381              : 
    1382            0 :     if( stat != NETSERIAL_E_NOERROR )
    1383              :     {
    1384            0 :         log<text_log>( "Error sending command: " + command, logPrio::LOG_ERROR );
    1385            0 :         return -1000;
    1386              :     }
    1387              : 
    1388            0 :     stat = m_sock.serialInString( answer, sizeof( answer ), timeout, '\n' );
    1389              : 
    1390            0 :     if( stat <= 0 )
    1391              :     {
    1392            0 :         log<text_log>( "No response received to command: " + command, logPrio::LOG_ERROR );
    1393            0 :         return -1000;
    1394              :     }
    1395              : 
    1396            0 :     char *nl = strchr( answer, '\n' );
    1397            0 :     if( nl )
    1398            0 :         answer[nl - answer] = '\0';
    1399              : 
    1400              : #ifdef LOG_TCS_STATUS
    1401              :     log<text_log>( std::string( "Received response: " ) + answer );
    1402              : #endif
    1403              : 
    1404            0 :     return atoi( answer );
    1405              : 
    1406            0 : } // int tcsInterface::sendMagTelCommand
    1407              : 
    1408            0 : inline std::vector<std::string> tcsInterface::parse_teldata( std::string &tdat )
    1409              : {
    1410            0 :     std::vector<std::string> vres;
    1411              : 
    1412            0 :     std::string tok;
    1413              : 
    1414              :     int pos1, pos2;
    1415              : 
    1416              :     // skip all leading spaces
    1417            0 :     pos1 = tdat.find_first_not_of( " ", 0 );
    1418              : 
    1419            0 :     if( pos1 == -1 )
    1420            0 :         pos1 = 0;
    1421              : 
    1422            0 :     pos2 = tdat.find_first_of( " ", pos1 );
    1423              : 
    1424            0 :     while( pos2 > 0 )
    1425              :     {
    1426            0 :         tok = tdat.substr( pos1, pos2 - pos1 );
    1427              : 
    1428            0 :         vres.push_back( tok );
    1429              : 
    1430              :         // now move past end of current spaces - might be more than one.
    1431            0 :         pos1 = pos2;
    1432              : 
    1433            0 :         pos2 = tdat.find_first_not_of( " ", pos1 );
    1434              : 
    1435              :         // and then find the end of this value.
    1436            0 :         pos1 = pos2;
    1437              : 
    1438            0 :         pos2 = tdat.find_first_of( " ", pos1 );
    1439              :     }
    1440              : 
    1441              :     // If there is another value, we pick it up here.
    1442            0 :     if( pos1 >= 0 )
    1443              :     {
    1444            0 :         pos2 = tdat.length();
    1445              : 
    1446            0 :         tok = tdat.substr( pos1, pos2 - pos1 );
    1447              : 
    1448            0 :         pos2 = tok.find_first_of( " \n\r", 0 );
    1449              : 
    1450            0 :         if( pos2 >= 0 )
    1451            0 :             tok.erase( pos2, tok.length() - pos2 );
    1452              : 
    1453            0 :         vres.push_back( tok );
    1454              :     }
    1455              : 
    1456            0 :     return vres;
    1457            0 : }
    1458              : 
    1459           16 : inline int tcsInterface::parse_xms( double &x, double &m, double &s, const std::string &xmsstr )
    1460              : {
    1461              :     size_t st, en;
    1462              : 
    1463           16 :     int sgn = 1;
    1464              : 
    1465           16 :     st = 0;
    1466           16 :     en = xmsstr.find( ':', st );
    1467              : 
    1468              :     // Check not found
    1469           16 :     if( en == std::string::npos )
    1470              :     {
    1471            2 :         log<software_error>( { __FILE__, __LINE__, "error parsing x:m:s (missing ':')" } );
    1472            2 :         return -1;
    1473              :     }
    1474              : 
    1475              :     // Check 0 length or invalid
    1476           14 :     if( en - st < 2 || en < st )
    1477              :     {
    1478            2 :         log<software_error>( { __FILE__, __LINE__, "error parsing x:m:s (0 length or invalid)" } );
    1479            2 :         return -1;
    1480              :     }
    1481              : 
    1482           12 :     std::string xstr;
    1483              :     try
    1484              :     {
    1485           12 :         xstr = xmsstr.substr( st, en - st );
    1486              :     }
    1487            0 :     catch( const std::exception &e )
    1488              :     {
    1489            0 :         log<software_error>( { __FILE__, __LINE__, e.what() } );
    1490            0 :         return -1;
    1491            0 :     }
    1492              : 
    1493              :     try
    1494              :     {
    1495           12 :         x = std::stod( xstr );
    1496              :     }
    1497            1 :     catch( const std::exception &e )
    1498              :     {
    1499            1 :         log<software_error>( { __FILE__, __LINE__, e.what() } );
    1500            1 :         return -1;
    1501            1 :     }
    1502              : 
    1503              :     // Check for negative
    1504           11 :     if( std::signbit( x ) )
    1505            4 :         sgn = -1;
    1506           11 :     if( xmsstr[0] == '-' )
    1507            4 :         sgn = -1;
    1508              : 
    1509           11 :     st = en + 1;
    1510              : 
    1511           11 :     en = xmsstr.find( ':', st );
    1512              : 
    1513              :     // Check not found
    1514           11 :     if( en == std::string::npos )
    1515              :     {
    1516            1 :         log<software_error>( { __FILE__, __LINE__, "error parsing x:m:s (missing ':')" } );
    1517            1 :         return -1;
    1518              :     }
    1519              : 
    1520              :     // Check 0 length or invalid
    1521           10 :     if( en - st < 2 || en < st )
    1522              :     {
    1523            3 :         log<software_error>( { __FILE__, __LINE__, "error parsing x:m:s (0 length or invalid)" } );
    1524            3 :         return -1;
    1525              :     }
    1526              : 
    1527            7 :     std::string mstr;
    1528              :     try
    1529              :     {
    1530            7 :         mstr = xmsstr.substr( st, en - st );
    1531              :     }
    1532            0 :     catch( const std::exception &e )
    1533              :     {
    1534            0 :         log<software_error>( { __FILE__, __LINE__, e.what() } );
    1535            0 :         return -1;
    1536            0 :     }
    1537              : 
    1538              :     try
    1539              :     {
    1540            7 :         m = sgn * std::stod( mstr );
    1541              :     }
    1542            0 :     catch( const std::exception &e )
    1543              :     {
    1544            0 :         log<software_error>( { __FILE__, __LINE__, e.what() } );
    1545            0 :         return -1;
    1546            0 :     }
    1547              : 
    1548            7 :     st = en + 1;
    1549              : 
    1550            7 :     if( st >= xmsstr.length() - 1 )
    1551              :     {
    1552            1 :         log<software_error>( { __FILE__, __LINE__, "error parsing x:m:s" } );
    1553            1 :         return -1;
    1554              :     }
    1555              : 
    1556            6 :     std::string sstr;
    1557              :     try
    1558              :     {
    1559            6 :         sstr = xmsstr.substr( st, xmsstr.length() - st );
    1560              :     }
    1561            0 :     catch( const std::exception &e )
    1562              :     {
    1563            0 :         log<software_error>( { __FILE__, __LINE__, e.what() } );
    1564            0 :         return -1;
    1565            0 :     }
    1566              : 
    1567              :     try
    1568              :     {
    1569            6 :         s = sgn * std::stod( sstr );
    1570              :     }
    1571            2 :     catch( const std::exception &e )
    1572              :     {
    1573            2 :         log<software_error>( { __FILE__, __LINE__, e.what() } );
    1574            2 :         return -1;
    1575            2 :     }
    1576              : 
    1577            4 :     return 0;
    1578           12 : }
    1579              : 
    1580            0 : inline int tcsInterface::getTelTime()
    1581              : {
    1582              :     double h, m, s;
    1583              : 
    1584            0 :     std::vector<std::string> pdat;
    1585            0 :     std::string              posstr;
    1586              : 
    1587            0 :     if( getMagTelStatus( posstr, "datetime" ) < 0 )
    1588              :     {
    1589            0 :         state( stateCodes::NOTCONNECTED );
    1590            0 :         log<text_log>( "Error getting telescope position (telpos)", logPrio::LOG_ERROR );
    1591            0 :         return -1;
    1592              :     }
    1593              : 
    1594            0 :     pdat = parse_teldata( posstr );
    1595              : 
    1596            0 :     if( pdat[0] == "-1" )
    1597              :     {
    1598            0 :         state( stateCodes::ERROR );
    1599            0 :         log<text_log>( "Error getting telescope time (datetime): TCS returned -1", logPrio::LOG_WARNING );
    1600            0 :         return -1;
    1601              :     }
    1602              : 
    1603            0 :     if( pdat.size() != 3 )
    1604              :     {
    1605            0 :         state( stateCodes::ERROR );
    1606            0 :         log<text_log>( "Error getting telescope position (datetime): TCS response wrong size, returned " +
    1607            0 :                            std::to_string( pdat.size() ) + " values",
    1608              :                        logPrio::LOG_WARNING );
    1609            0 :         return -1;
    1610              :     }
    1611              : 
    1612            0 :     if( parse_xms( h, m, s, pdat[2] ) != 0 )
    1613              :     {
    1614            0 :         log<text_log>( "Error parsing telescope ST", logPrio::LOG_WARNING );
    1615            0 :         return -1;
    1616              :     }
    1617              : 
    1618            0 :     m_telST = ( h + m / 60. + s / 3600. );
    1619              : 
    1620            0 :     return 0;
    1621            0 : } // int tcsInterface::getTelTime()
    1622              : 
    1623            0 : inline int tcsInterface::getTelPos()
    1624              : {
    1625              :     double h, m, s;
    1626              : 
    1627            0 :     std::vector<std::string> pdat;
    1628            0 :     std::string              posstr;
    1629              : 
    1630            0 :     if( getMagTelStatus( posstr, "telpos" ) < 0 )
    1631              :     {
    1632            0 :         state( stateCodes::NOTCONNECTED );
    1633            0 :         log<text_log>( "Error getting telescope position (telpos)", logPrio::LOG_ERROR );
    1634            0 :         return -1;
    1635              :     }
    1636              : 
    1637            0 :     pdat = parse_teldata( posstr );
    1638              : 
    1639            0 :     if( pdat[0] == "-1" )
    1640              :     {
    1641            0 :         state( stateCodes::ERROR );
    1642            0 :         log<text_log>( "Error getting telescope position (telpos): TCS returned -1", logPrio::LOG_WARNING );
    1643            0 :         return -1;
    1644              :     }
    1645              : 
    1646            0 :     if( pdat.size() != 6 )
    1647              :     {
    1648            0 :         state( stateCodes::ERROR );
    1649            0 :         log<text_log>( "Error getting telescope position (telpos): TCS response wrong size, returned " +
    1650            0 :                            std::to_string( pdat.size() ) + " values",
    1651              :                        logPrio::LOG_WARNING );
    1652            0 :         return -1;
    1653              :     }
    1654              : 
    1655            0 :     if( parse_xms( h, m, s, pdat[0] ) != 0 )
    1656              :     {
    1657            0 :         log<text_log>( "Error parsing telescope RA", logPrio::LOG_WARNING );
    1658            0 :         return -1;
    1659              :     }
    1660              : 
    1661            0 :     m_telRA = ( h + m / 60. + s / 3600. ) * 15.;
    1662              : 
    1663            0 :     if( parse_xms( h, m, s, pdat[1] ) != 0 )
    1664              :     {
    1665            0 :         log<text_log>( "Error parsing telescope Dec", logPrio::LOG_WARNING );
    1666            0 :         return -1;
    1667              :     }
    1668              : 
    1669            0 :     m_telDec = h + m / 60. + s / 3600.;
    1670              : 
    1671              :     // m_telEl = strtod(pdat[1].c_str(),0);// * 3600.;
    1672              : 
    1673            0 :     m_telEpoch = strtod( pdat[2].c_str(), 0 );
    1674              : 
    1675            0 :     if( parse_xms( h, m, s, pdat[3] ) != 0 )
    1676              :     {
    1677            0 :         log<text_log>( "Error parsing telescope HA", logPrio::LOG_WARNING );
    1678            0 :         return -1;
    1679              :     }
    1680              : 
    1681              :     /************ BUG: this won't handle -0!
    1682              :      */
    1683            0 :     m_telHA = h + m / 60. + s / 3600.;
    1684              : 
    1685            0 :     m_telAM = strtod( pdat[4].c_str(), 0 );
    1686              : 
    1687            0 :     m_telRotOff = strtod( pdat[5].c_str(), 0 );
    1688              : 
    1689            0 :     if( recordTelPos() < 0 )
    1690              :     {
    1691            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
    1692              :     }
    1693              : 
    1694            0 :     return 0;
    1695            0 : } // int tcsInterface::getTelPos()
    1696              : 
    1697            0 : inline int tcsInterface::getTelData()
    1698              : {
    1699            0 :     std::string              xstr;
    1700            0 :     std::vector<std::string> tdat;
    1701              : 
    1702            0 :     if( getMagTelStatus( xstr, "teldata" ) < 0 )
    1703              :     {
    1704            0 :         state( stateCodes::NOTCONNECTED );
    1705            0 :         log<text_log>( "Error getting telescope data (teldata)", logPrio::LOG_ERROR );
    1706            0 :         return -1;
    1707              :     }
    1708              : 
    1709            0 :     tdat = parse_teldata( xstr );
    1710              : 
    1711            0 :     if( tdat[0] == "-1" )
    1712              :     {
    1713            0 :         state( stateCodes::ERROR );
    1714            0 :         log<text_log>( "Error getting telescope data (teldata): TCS returned -1", logPrio::LOG_WARNING );
    1715            0 :         return -1;
    1716              :     }
    1717              : 
    1718            0 :     if( tdat.size() != 10 )
    1719              :     {
    1720            0 :         state( stateCodes::ERROR );
    1721            0 :         log<text_log>( "[TCS] Error getting telescope data (teldata): TCS response wrong size, returned " +
    1722            0 :                            std::to_string( tdat.size() ) + " values",
    1723              :                        logPrio::LOG_WARNING );
    1724            0 :         return -1;
    1725              :     }
    1726              : 
    1727            0 :     m_telROI = atoi( tdat[0].c_str() );
    1728              : 
    1729              :     // Parse the telguide string
    1730            0 :     char bit[2]   = { 0, 0 };
    1731            0 :     bit[1]        = 0;
    1732            0 :     bit[0]        = tdat[1].c_str()[0];
    1733            0 :     m_telTracking = atoi( bit );
    1734            0 :     bit[0]        = tdat[1].c_str()[1];
    1735            0 :     m_telGuiding  = atoi( bit );
    1736              : 
    1737              :     // parse the gdrmountmv string
    1738            0 :     bit[0]            = tdat[2].c_str()[0];
    1739            0 :     m_telSlewing      = atoi( bit );
    1740            0 :     bit[0]            = tdat[2].c_str()[1];
    1741            0 :     m_telGuiderMoving = atoi( bit );
    1742              : 
    1743              :     // number 3 is mountmv
    1744              : 
    1745            0 :     m_telAz = strtod( tdat[4].c_str(), 0 );
    1746              : 
    1747            0 :     m_telEl = strtod( tdat[5].c_str(), 0 );
    1748              : 
    1749            0 :     m_telZd = strtod( tdat[6].c_str(), 0 ); // * 3600.;
    1750              : 
    1751            0 :     m_telPA = strtod( tdat[7].c_str(), 0 );
    1752              : 
    1753            0 :     m_telDomeAz = strtod( tdat[8].c_str(), 0 );
    1754              : 
    1755            0 :     m_telDomeStat = atoi( tdat[9].c_str() );
    1756              : 
    1757            0 :     if( recordTelData() < 0 )
    1758              :     {
    1759            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
    1760              :     }
    1761              : 
    1762            0 :     return 0;
    1763            0 : } // int tcsInterface::getTelData()
    1764              : 
    1765            0 : inline int tcsInterface::getCatData()
    1766              : {
    1767              :     double h, m, s;
    1768              : 
    1769            0 :     std::vector<std::string> cdat;
    1770            0 :     std::string              cstr;
    1771              : 
    1772            0 :     if( getMagTelStatus( cstr, "catdata" ) < 0 )
    1773              :     {
    1774            0 :         state( stateCodes::NOTCONNECTED );
    1775            0 :         log<text_log>( "Error getting catalog data (catdata)", logPrio::LOG_ERROR );
    1776            0 :         return -1;
    1777              :     }
    1778              : 
    1779            0 :     cdat = parse_teldata( cstr );
    1780              : 
    1781            0 :     if( cdat[0] == "-1" )
    1782              :     {
    1783            0 :         state( stateCodes::ERROR );
    1784            0 :         log<text_log>( "Error getting catalog data (catdata): TCS returned -1", logPrio::LOG_WARNING );
    1785            0 :         return -1;
    1786              :     }
    1787              : 
    1788            0 :     if( cdat.size() != 6 )
    1789              :     {
    1790            0 :         bool pointing = false;
    1791              : 
    1792            0 :         if( cdat.size() == 7 )
    1793              :         {
    1794            0 :             if( cdat[6] == "Pointing" )
    1795              :             {
    1796            0 :                 pointing = true;
    1797              :             }
    1798              :         }
    1799              : 
    1800            0 :         if( !pointing )
    1801              :         {
    1802              :             // This can occur if no target selected by operator
    1803            0 :             log<text_log>( "Catalog data (catdata): TCS response wrong size, returned " +
    1804            0 :                                std::to_string( cdat.size() ) + " values",
    1805              :                            logPrio::LOG_WARNING );
    1806              : 
    1807            0 :             for( size_t n = 0; n < cdat.size(); ++n )
    1808              :             {
    1809            0 :                 std::cerr << n << " " << cdat[n] << "\n";
    1810              :             }
    1811            0 :             m_catRA = 0;
    1812              : 
    1813            0 :             m_catDec = 0;
    1814              : 
    1815            0 :             m_catEp = 0;
    1816              : 
    1817            0 :             m_catRo = 0;
    1818              : 
    1819            0 :             m_catRm = "";
    1820              : 
    1821            0 :             m_catObj = "none";
    1822              : 
    1823            0 :             return 1;
    1824              :         }
    1825              :     }
    1826              : 
    1827            0 :     if( parse_xms( h, m, s, cdat[0] ) != 0 )
    1828              :     {
    1829            0 :         log<text_log>( "Error parsing catalog RA", logPrio::LOG_WARNING );
    1830            0 :         return -1;
    1831              :     }
    1832              : 
    1833            0 :     m_catRA = ( h + m / 60. + s / 3600. ) * 15.;
    1834              : 
    1835            0 :     if( parse_xms( h, m, s, cdat[1] ) != 0 )
    1836              :     {
    1837            0 :         log<text_log>( "Error parsing catalog Dec", logPrio::LOG_WARNING );
    1838            0 :         return -1;
    1839              :     }
    1840              : 
    1841            0 :     m_catDec = h + m / 60. + s / 3600.;
    1842              : 
    1843            0 :     m_catEp = strtod( cdat[2].c_str(), 0 );
    1844              : 
    1845            0 :     m_catRo = strtod( cdat[3].c_str(), 0 );
    1846              : 
    1847            0 :     m_catRm = cdat[4];
    1848              : 
    1849            0 :     m_catObj = cdat[5];
    1850              : 
    1851            0 :     return 0;
    1852            0 : } // int tcsInterface::getCatData()
    1853              : 
    1854            0 : inline int tcsInterface::getVaneData()
    1855              : {
    1856            0 :     std::string              xstr;
    1857            0 :     std::vector<std::string> vedat;
    1858              : 
    1859            0 :     if( getMagTelStatus( xstr, "vedata" ) < 0 )
    1860              :     {
    1861            0 :         state( stateCodes::NOTCONNECTED );
    1862            0 :         log<text_log>( "Error getting telescope secondary positions (vedata)", logPrio::LOG_ERROR );
    1863            0 :         return -1;
    1864              :     }
    1865              : 
    1866            0 :     vedat = parse_teldata( xstr );
    1867              : 
    1868            0 :     if( vedat[0] == "-1" )
    1869              :     {
    1870            0 :         state( stateCodes::ERROR );
    1871            0 :         log<text_log>( "Error getting telescope secondary positions (vedata): TCS returned -1", logPrio::LOG_WARNING );
    1872            0 :         return -1;
    1873              :     }
    1874              : 
    1875            0 :     if( vedat.size() != 10 )
    1876              :     {
    1877            0 :         state( stateCodes::ERROR );
    1878            0 :         log<text_log>( "Error getting telescope secondary positions (vedata): TCS response wrong size, returned " +
    1879            0 :                            std::to_string( vedat.size() ) + " values",
    1880              :                        logPrio::LOG_WARNING );
    1881            0 :         return -1;
    1882              :     }
    1883              : 
    1884            0 :     m_telSecZ = strtod( vedat[0].c_str(), 0 );
    1885            0 :     m_telEncZ = strtod( vedat[1].c_str(), 0 );
    1886            0 :     m_telSecX = strtod( vedat[2].c_str(), 0 );
    1887            0 :     m_telEncX = strtod( vedat[3].c_str(), 0 );
    1888            0 :     m_telSecY = strtod( vedat[4].c_str(), 0 );
    1889            0 :     m_telEncY = strtod( vedat[5].c_str(), 0 );
    1890            0 :     m_telSecH = strtod( vedat[6].c_str(), 0 );
    1891            0 :     m_telEncH = strtod( vedat[7].c_str(), 0 );
    1892            0 :     m_telSecV = strtod( vedat[8].c_str(), 0 );
    1893            0 :     m_telEncV = strtod( vedat[9].c_str(), 0 );
    1894              : 
    1895            0 :     if( recordTelVane() < 0 )
    1896              :     {
    1897            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
    1898              :     }
    1899              : 
    1900            0 :     return 0;
    1901            0 : } // int tcsInterface::getVaneData()
    1902              : 
    1903            0 : inline int tcsInterface::getEnvData()
    1904              : {
    1905            0 :     std::string              estr;
    1906            0 :     std::vector<std::string> edat;
    1907              : 
    1908            0 :     if( getMagTelStatus( estr, "telenv" ) < 0 )
    1909              :     {
    1910            0 :         state( stateCodes::NOTCONNECTED );
    1911            0 :         log<text_log>( "Error getting telescope environment data (telenv)", logPrio::LOG_ERROR );
    1912            0 :         return -1;
    1913              :     }
    1914              : 
    1915            0 :     edat = parse_teldata( estr );
    1916              : 
    1917            0 :     if( edat[0] == "-1" )
    1918              :     {
    1919            0 :         state( stateCodes::NOTCONNECTED );
    1920            0 :         log<text_log>( "Error getting telescope environment data (telenv): TCS returned -1", logPrio::LOG_WARNING );
    1921            0 :         return -1;
    1922              :     }
    1923              : 
    1924            0 :     if( edat.size() != 10 )
    1925              :     {
    1926            0 :         state( stateCodes::NOTCONNECTED );
    1927            0 :         log<text_log>( "Error getting telescope environment data (telenv): TCS response wrong size, returned " +
    1928            0 :                            std::to_string( edat.size() ) + "values",
    1929              :                        logPrio::LOG_WARNING );
    1930            0 :         return -1;
    1931              :     }
    1932              : 
    1933            0 :     m_wxtemp     = strtod( edat[0].c_str(), 0 );
    1934            0 :     m_wxpres     = strtod( edat[1].c_str(), 0 );
    1935            0 :     m_wxhumid    = strtod( edat[2].c_str(), 0 );
    1936            0 :     m_wxwind     = strtod( edat[3].c_str(), 0 );
    1937            0 :     m_wxwdir     = strtod( edat[4].c_str(), 0 );
    1938            0 :     m_ttruss     = strtod( edat[5].c_str(), 0 );
    1939            0 :     m_tcell      = strtod( edat[6].c_str(), 0 );
    1940            0 :     m_tseccell   = strtod( edat[7].c_str(), 0 );
    1941            0 :     m_tambient   = strtod( edat[8].c_str(), 0 );
    1942            0 :     m_wxdewpoint = strtod( edat[9].c_str(), 0 );
    1943              : 
    1944            0 :     if( recordTelEnv() < 0 )
    1945              :     {
    1946            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
    1947              :     }
    1948              : 
    1949            0 :     return 0;
    1950            0 : } // int tcsInterface::getEnvData()
    1951              : 
    1952            0 : int tcsInterface::badSeeing()
    1953              : {
    1954            0 :     m_dimm_time      = 0;
    1955            0 :     m_dimm_fwhm_corr = -1;
    1956            0 :     m_mag1_time      = 0;
    1957            0 :     m_mag1_fwhm_corr = -1;
    1958            0 :     m_mag2_time      = 0;
    1959            0 :     m_mag2_fwhm_corr = -1;
    1960              : 
    1961            0 :     if( recordTelSee() < 0 )
    1962              :     {
    1963            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
    1964              :     }
    1965              : 
    1966            0 :     return 0;
    1967              : }
    1968              : 
    1969            0 : int tcsInterface::getSeeing()
    1970              : {
    1971              :     static int last_query = 0;
    1972              : 
    1973            0 :     if( time( 0 ) - last_query > m_seeingInterval )
    1974              :     {
    1975              :         time_t sec_midnight;
    1976              :         time_t dt;
    1977              : 
    1978            0 :         int rv = system( "query_seeing > /dev/null" );
    1979            0 :         if( rv < 0 )
    1980              :         {
    1981            0 :             if( badSeeing() < 0 )
    1982              :             {
    1983            0 :                 log<software_error>( { __FILE__, __LINE__ } );
    1984              :             }
    1985              : 
    1986            0 :             if( !m_labMode )
    1987              :             {
    1988            0 :                 log<software_error>( { __FILE__, __LINE__, "Error from seeing query" } );
    1989            0 :                 return -1;
    1990              :             }
    1991              : 
    1992            0 :             return 0;
    1993              :         }
    1994              : 
    1995            0 :         last_query   = time( 0 );
    1996            0 :         sec_midnight = last_query % 86400;
    1997              : 
    1998            0 :         std::ifstream fin;
    1999            0 :         std::string   label, datestr, timestr, scopestr, fwhmcorrstr;
    2000              :         double        h, m, s;
    2001              :         // note: query seeing picks a temp file with the username included
    2002              :         // to reduce file clobbering conflicts
    2003            0 :         fin.open( "/tmp/xsup_query_seeing.txt" );
    2004              : 
    2005            0 :         if( fin.fail() )
    2006              :         {
    2007            0 :             if( badSeeing() < 0 )
    2008              :             {
    2009            0 :                 log<software_error>( { __FILE__, __LINE__ } );
    2010              :             }
    2011              : 
    2012            0 :             if( !m_labMode )
    2013              :             {
    2014            0 :                 log<software_error>( { __FILE__, __LINE__, "Error reading dimm seeing" } );
    2015            0 :                 return -1;
    2016              :             }
    2017              :         }
    2018              : 
    2019            0 :         fin >> label;
    2020            0 :         fin >> label;
    2021            0 :         fin >> label;
    2022            0 :         fin >> label;
    2023              : 
    2024            0 :         fin >> datestr;
    2025            0 :         fin >> timestr;
    2026            0 :         fin >> scopestr;
    2027            0 :         fin >> m_dimm_fwhm_corr;
    2028              : 
    2029              :         // Handle no values from query
    2030            0 :         if( !fin )
    2031              :         {
    2032            0 :             if( badSeeing() < 0 )
    2033              :             {
    2034            0 :                 log<software_error>( { __FILE__, __LINE__ } );
    2035              :             }
    2036              : 
    2037            0 :             if( !m_labMode )
    2038              :             {
    2039            0 :                 log<software_error>( { __FILE__, __LINE__, "query_seeing returned not enough values" } );
    2040            0 :                 return -1;
    2041              :             }
    2042              :             else
    2043              :             {
    2044            0 :                 return 0;
    2045              :             }
    2046              :         }
    2047              : 
    2048            0 :         if( scopestr != "dimm" )
    2049              :         {
    2050            0 :             log<software_error>(
    2051            0 :                 { __FILE__, __LINE__, "Unexpected order of telescope names.  Instead of dimm got: " + scopestr } );
    2052              :         }
    2053              : 
    2054            0 :         parse_xms( h, m, s, timestr );
    2055              : 
    2056            0 :         m_dimm_time = (int)( h * 3600 + m * 60 + s + 0.5 );
    2057              : 
    2058            0 :         dt = sec_midnight - m_dimm_time;
    2059            0 :         if( dt < 0 )
    2060            0 :             dt = 86400 - dt;
    2061              : 
    2062            0 :         if( dt > 300 || m_dimm_fwhm_corr <= 0 )
    2063              :         {
    2064            0 :             m_dimm_fwhm_corr = -1;
    2065              :         }
    2066              : 
    2067              :         // Next scope: mag1/baade
    2068            0 :         fin >> datestr;
    2069            0 :         fin >> timestr;
    2070            0 :         fin >> scopestr;
    2071            0 :         fin >> m_mag1_fwhm_corr;
    2072              : 
    2073              :         // Handle no values from query
    2074            0 :         if( !fin )
    2075              :         {
    2076            0 :             if( badSeeing() < 0 )
    2077              :             {
    2078            0 :                 log<software_error>( { __FILE__, __LINE__ } );
    2079              :             }
    2080              : 
    2081            0 :             if( !m_labMode )
    2082              :             {
    2083            0 :                 log<software_error>( { __FILE__, __LINE__, "query_seeing returned not enough values" } );
    2084            0 :                 return -1;
    2085              :             }
    2086              :             else
    2087              :             {
    2088            0 :                 return 0;
    2089              :             }
    2090              :         }
    2091              : 
    2092            0 :         if( scopestr != "baade" )
    2093              :         {
    2094            0 :             log<software_error>(
    2095            0 :                 { __FILE__, __LINE__, "Unexpected order of telescope names.  Instead of baade got: " + scopestr } );
    2096              :         }
    2097              : 
    2098            0 :         parse_xms( h, m, s, timestr );
    2099              : 
    2100            0 :         m_mag1_time = (int)( h * 3600 + m * 60 + s + 0.5 );
    2101              : 
    2102            0 :         dt = sec_midnight - m_mag1_time;
    2103            0 :         if( dt < 0 )
    2104            0 :             dt = 86400 - dt;
    2105              : 
    2106            0 :         if( dt > 300 || m_mag1_fwhm_corr <= 0 )
    2107              :         {
    2108            0 :             m_mag1_fwhm_corr = -1;
    2109              :         }
    2110              : 
    2111              :         // Last scope: mag2/clay
    2112            0 :         fin >> datestr;
    2113            0 :         fin >> timestr;
    2114            0 :         fin >> scopestr;
    2115            0 :         fin >> m_mag2_fwhm_corr;
    2116              : 
    2117              :         // Handle no values from query
    2118            0 :         if( !fin )
    2119              :         {
    2120            0 :             if( badSeeing() < 0 )
    2121              :             {
    2122            0 :                 log<software_error>( { __FILE__, __LINE__ } );
    2123              :             }
    2124              : 
    2125            0 :             if( !m_labMode )
    2126              :             {
    2127            0 :                 log<software_error>( { __FILE__, __LINE__, "query_seeing returned not enough values" } );
    2128            0 :                 return -1;
    2129              :             }
    2130              :             else
    2131              :             {
    2132            0 :                 return 0;
    2133              :             }
    2134              :         }
    2135              : 
    2136            0 :         if( scopestr != "clay" )
    2137              :         {
    2138            0 :             log<software_error>(
    2139            0 :                 { __FILE__, __LINE__, "Unexpected order of telescope names.  Instead of clay got: " + scopestr } );
    2140              :         }
    2141              : 
    2142            0 :         fin.close();
    2143              : 
    2144            0 :         parse_xms( h, m, s, timestr );
    2145              : 
    2146            0 :         m_mag2_time = (int)( h * 3600 + m * 60 + s + 0.5 );
    2147              : 
    2148            0 :         dt = sec_midnight - m_mag2_time;
    2149            0 :         if( dt < 0 )
    2150            0 :             dt = 86400 - dt;
    2151              : 
    2152            0 :         if( dt > 300 || m_mag2_fwhm_corr <= 0 )
    2153              :         {
    2154            0 :             m_mag2_fwhm_corr = -1;
    2155              :         }
    2156              : 
    2157            0 :         if( recordTelSee() < 0 )
    2158              :         {
    2159            0 :             return log<software_error, -1>( { __FILE__, __LINE__ } );
    2160              :         }
    2161            0 :     }
    2162              : 
    2163            0 :     return 0;
    2164              : }
    2165              : 
    2166            0 : inline int tcsInterface::updateINDI()
    2167              : {
    2168              :     try
    2169              :     {
    2170            0 :         updateIfChanged( m_indiP_teltime, "sidereal_time", m_telST, INDI_OK );
    2171              :         //      m_indiP_teltime["sidereal_time"] = m_telST;
    2172              :     }
    2173            0 :     catch( ... )
    2174              :     {
    2175            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2176            0 :         return -1;
    2177            0 :     }
    2178              : 
    2179              :     /* try
    2180              :      {
    2181              :         m_indiP_teltime.setState(INDI_OK);
    2182              :      }
    2183              :      catch(...)
    2184              :      {
    2185              :         log<software_error>({__FILE__,__LINE__,"INDI library exception"});
    2186              :         return -1;
    2187              :      }
    2188              : 
    2189              :      try
    2190              :      {
    2191              :         m_indiDriver->sendSetProperty(m_indiP_teltime);
    2192              :      }
    2193              :      catch(...)
    2194              :      {
    2195              :         log<software_error>({__FILE__,__LINE__,"INDI library exception"});
    2196              :         return -1;
    2197              :      }*/
    2198              : 
    2199              :     try
    2200              :     {
    2201            0 :         indi::updateIfChanged(
    2202            0 :             m_indiP_telpos,
    2203            0 :             std::vector<std::string>( { "epoch", "ra", "dec", "el", "ha", "am", "rotoff" } ),
    2204            0 :             std::vector<double>( { m_telEpoch, m_telRA, m_telDec, m_telEl, m_telHA, m_telAM, m_telRotOff } ),
    2205              :             m_indiDriver,
    2206              :             INDI_OK );
    2207              :         /*m_indiP_telpos["epoch"] = m_telEpoch;
    2208              :         m_indiP_telpos["ra"] = m_telRA;
    2209              :         m_indiP_telpos["dec"] = m_telDec;
    2210              :         m_indiP_telpos["el"] = m_telEl;
    2211              :         m_indiP_telpos["ha"] = m_telHA;
    2212              :         m_indiP_telpos["am"] = m_telAM;
    2213              :         m_indiP_telpos["rotoff"] = m_telRotOff;*/
    2214              :     }
    2215            0 :     catch( ... )
    2216              :     {
    2217            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2218            0 :         return -1;
    2219            0 :     }
    2220              : 
    2221              :     /*try
    2222              :     {
    2223              :        m_indiP_telpos.setState(INDI_OK);
    2224              :     }
    2225              :     catch(...)
    2226              :     {
    2227              :        log<software_error>({__FILE__,__LINE__,"INDI library exception"});
    2228              :        return -1;
    2229              :     }
    2230              : 
    2231              :     try
    2232              :     {
    2233              :        m_indiDriver->sendSetProperty (m_indiP_telpos);
    2234              :     }
    2235              :     catch(...)
    2236              :     {
    2237              :        log<software_error>({__FILE__,__LINE__,"INDI library exception"});
    2238              :        return -1;
    2239              :     }*/
    2240              : 
    2241              :     try
    2242              :     {
    2243            0 :         m_indiP_teldata["roi"]           = m_telROI;
    2244            0 :         m_indiP_teldata["tracking"]      = m_telTracking;
    2245            0 :         m_indiP_teldata["guiding"]       = m_telGuiding;
    2246            0 :         m_indiP_teldata["slewing"]       = m_telSlewing;
    2247            0 :         m_indiP_teldata["guider_moving"] = m_telGuiderMoving;
    2248            0 :         m_indiP_teldata["az"]            = m_telAz;
    2249            0 :         m_indiP_teldata["zd"]            = m_telZd;
    2250            0 :         m_indiP_teldata["pa"]            = m_telPA;
    2251            0 :         m_indiP_teldata["dome_az"]       = m_telDomeAz;
    2252            0 :         m_indiP_teldata["dome_stat"]     = m_telDomeStat;
    2253              :     }
    2254            0 :     catch( ... )
    2255              :     {
    2256            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2257            0 :         return -1;
    2258            0 :     }
    2259              : 
    2260              :     try
    2261              :     {
    2262            0 :         m_indiP_teldata.setState( INDI_OK );
    2263              :     }
    2264            0 :     catch( ... )
    2265              :     {
    2266            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2267            0 :         return -1;
    2268            0 :     }
    2269              : 
    2270              :     try
    2271              :     {
    2272            0 :         m_indiDriver->sendSetProperty( m_indiP_teldata );
    2273              :     }
    2274            0 :     catch( ... )
    2275              :     {
    2276            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2277            0 :         return -1;
    2278            0 :     }
    2279              : 
    2280              :     try
    2281              :     {
    2282            0 :         indi::updateIfChanged( m_indiP_catalog,
    2283            0 :                                std::vector<std::string>( { "object", "rotmode" } ),
    2284            0 :                                std::vector<std::string>( { m_catObj, m_catRm } ),
    2285              :                                m_indiDriver,
    2286              :                                INDI_OK );
    2287              :         // m_indiP_catalog["object"] = m_catObj;
    2288              :         // m_indiP_catalog["rotmode"] = m_catRo;
    2289              :     }
    2290            0 :     catch( ... )
    2291              :     {
    2292            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2293            0 :         return -1;
    2294            0 :     }
    2295              : 
    2296              :     /*   try
    2297              :        {
    2298              :           m_indiP_catalog.setState(INDI_OK);
    2299              :        }
    2300              :        catch(...)
    2301              :        {
    2302              :           log<software_error>({__FILE__,__LINE__,"INDI library exception"});
    2303              :           return -1;
    2304              :        }
    2305              : 
    2306              :        try
    2307              :        {
    2308              :           m_indiDriver->sendSetProperty (m_indiP_catalog);
    2309              :        }
    2310              :        catch(...)
    2311              :        {
    2312              :           log<software_error>({__FILE__,__LINE__,"INDI library exception"});
    2313              :           return -1;
    2314              :        }*/
    2315              : 
    2316              :     try
    2317              :     {
    2318            0 :         indi::updateIfChanged( m_indiP_catdata,
    2319            0 :                                std::vector<std::string>( { "ra", "dec", "epoch", "rotoff" } ),
    2320            0 :                                std::vector<double>( { m_catRA, m_catDec, m_catEp, m_catRo } ),
    2321              :                                m_indiDriver,
    2322              :                                INDI_OK );
    2323              :         /*m_indiP_catdata["ra"] = m_catRA;
    2324              :         m_indiP_catdata["dec"] = m_catDec;
    2325              :         m_indiP_catdata["epoch"] = m_catEp;
    2326              :         m_indiP_catdata["rotoff"] = m_catRo;*/
    2327              :     }
    2328            0 :     catch( ... )
    2329              :     {
    2330            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2331            0 :         return -1;
    2332            0 :     }
    2333              : 
    2334              :     /*   try
    2335              :        {
    2336              :           m_indiP_catdata.setState(INDI_OK);
    2337              :        }
    2338              :        catch(...)
    2339              :        {
    2340              :           log<software_error>({__FILE__,__LINE__,"INDI library exception"});
    2341              :           return -1;
    2342              :        }
    2343              : 
    2344              :        try
    2345              :        {
    2346              :           m_indiDriver->sendSetProperty (m_indiP_catdata);
    2347              :        }
    2348              :        catch(...)
    2349              :        {
    2350              :           log<software_error>({__FILE__,__LINE__,"INDI library exception"});
    2351              :           return -1;
    2352              :        }*/
    2353              : 
    2354              :     //---- Vane End ----//
    2355              :     try
    2356              :     {
    2357            0 :         m_indiP_vaneend["secz"] = m_telSecZ;
    2358            0 :         m_indiP_vaneend["encz"] = m_telEncZ;
    2359            0 :         m_indiP_vaneend["secx"] = m_telSecX;
    2360            0 :         m_indiP_vaneend["encx"] = m_telEncX;
    2361            0 :         m_indiP_vaneend["secy"] = m_telSecY;
    2362            0 :         m_indiP_vaneend["ency"] = m_telEncY;
    2363            0 :         m_indiP_vaneend["sech"] = m_telSecH;
    2364            0 :         m_indiP_vaneend["ench"] = m_telEncH;
    2365            0 :         m_indiP_vaneend["secv"] = m_telSecV;
    2366            0 :         m_indiP_vaneend["encv"] = m_telEncV;
    2367              :     }
    2368            0 :     catch( ... )
    2369              :     {
    2370            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2371            0 :         return -1;
    2372            0 :     }
    2373              : 
    2374              :     try
    2375              :     {
    2376            0 :         m_indiP_vaneend.setState( INDI_OK );
    2377              :     }
    2378            0 :     catch( ... )
    2379              :     {
    2380            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2381            0 :         return -1;
    2382            0 :     }
    2383              : 
    2384              :     try
    2385              :     {
    2386            0 :         m_indiDriver->sendSetProperty( m_indiP_vaneend );
    2387              :     }
    2388            0 :     catch( ... )
    2389              :     {
    2390            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2391            0 :         return -1;
    2392            0 :     }
    2393              : 
    2394              :     //---- Environment ----//
    2395              :     try
    2396              :     {
    2397            0 :         m_indiP_env["temp-out"]     = m_wxtemp;
    2398            0 :         m_indiP_env["pressure"]     = m_wxpres;
    2399            0 :         m_indiP_env["humidity"]     = m_wxhumid;
    2400            0 :         m_indiP_env["wind"]         = m_wxwind;
    2401            0 :         m_indiP_env["winddir"]      = m_wxwdir;
    2402            0 :         m_indiP_env["temp-truss"]   = m_ttruss;
    2403            0 :         m_indiP_env["temp-cell"]    = m_tcell;
    2404            0 :         m_indiP_env["temp-seccell"] = m_tseccell;
    2405            0 :         m_indiP_env["temp-amb"]     = m_tambient;
    2406            0 :         m_indiP_env["dewpoint"]     = m_wxdewpoint;
    2407              :     }
    2408            0 :     catch( ... )
    2409              :     {
    2410            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2411            0 :         return -1;
    2412            0 :     }
    2413              : 
    2414              :     try
    2415              :     {
    2416            0 :         m_indiP_env.setState( INDI_OK );
    2417              :     }
    2418            0 :     catch( ... )
    2419              :     {
    2420            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2421            0 :         return -1;
    2422            0 :     }
    2423              : 
    2424              :     try
    2425              :     {
    2426            0 :         m_indiDriver->sendSetProperty( m_indiP_env );
    2427              :     }
    2428            0 :     catch( ... )
    2429              :     {
    2430            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2431            0 :         return -1;
    2432            0 :     }
    2433              : 
    2434              :     //---- Seeing ----//
    2435              :     try
    2436              :     {
    2437            0 :         m_indiP_seeing["dimm_time"]      = m_dimm_time;
    2438            0 :         m_indiP_seeing["dimm_fwhm_corr"] = m_dimm_fwhm_corr;
    2439              : 
    2440            0 :         m_indiP_seeing["mag1_time"]      = m_mag1_time;
    2441            0 :         m_indiP_seeing["mag1_fwhm_corr"] = m_mag1_fwhm_corr;
    2442              : 
    2443            0 :         m_indiP_seeing["mag2_time"]      = m_mag2_time;
    2444            0 :         m_indiP_seeing["mag2_fwhm_corr"] = m_mag2_fwhm_corr;
    2445              :     }
    2446            0 :     catch( ... )
    2447              :     {
    2448            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2449            0 :         return -1;
    2450            0 :     }
    2451              : 
    2452              :     try
    2453              :     {
    2454            0 :         m_indiP_seeing.setState( INDI_OK );
    2455              :     }
    2456            0 :     catch( ... )
    2457              :     {
    2458            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2459            0 :         return -1;
    2460            0 :     }
    2461              : 
    2462              :     try
    2463              :     {
    2464            0 :         m_indiDriver->sendSetProperty( m_indiP_seeing );
    2465              :     }
    2466            0 :     catch( ... )
    2467              :     {
    2468            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2469            0 :         return -1;
    2470            0 :     }
    2471              : 
    2472              :     //--- Offloading ---//
    2473              : 
    2474              :     try
    2475              :     {
    2476            0 :         if( m_offlTT_dump )
    2477              :         {
    2478            0 :             updateSwitchIfChanged( m_indiP_offlTTdump, "request", pcf::IndiElement::On, INDI_OK );
    2479              :         }
    2480              :         else
    2481              :         {
    2482            0 :             updateSwitchIfChanged( m_indiP_offlTTdump, "request", pcf::IndiElement::Off, INDI_IDLE );
    2483              :         }
    2484              : 
    2485            0 :         if( m_offlTT_enabled )
    2486              :         {
    2487            0 :             updateSwitchIfChanged( m_indiP_offlTTenable, "toggle", pcf::IndiElement::On, INDI_OK );
    2488              :         }
    2489              :         else
    2490              :         {
    2491            0 :             updateSwitchIfChanged( m_indiP_offlTTenable, "toggle", pcf::IndiElement::Off, INDI_IDLE );
    2492              :         }
    2493              : 
    2494            0 :         updateIfChanged( m_indiP_offlTTavgInt, "current", m_offlTT_avgInt );
    2495            0 :         updateIfChanged( m_indiP_offlTTgain, "current", m_offlTT_gain );
    2496            0 :         updateIfChanged( m_indiP_offlTTthresh, "current", m_offlTT_thresh );
    2497              :     }
    2498            0 :     catch( ... )
    2499              :     {
    2500            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2501            0 :         return -1;
    2502            0 :     }
    2503              : 
    2504              :     try
    2505              :     {
    2506            0 :         if( m_offlF_dump )
    2507              :         {
    2508            0 :             updateSwitchIfChanged( m_indiP_offlFdump, "request", pcf::IndiElement::On, INDI_OK );
    2509              :         }
    2510              :         else
    2511              :         {
    2512            0 :             updateSwitchIfChanged( m_indiP_offlFdump, "request", pcf::IndiElement::Off, INDI_IDLE );
    2513              :         }
    2514              : 
    2515            0 :         if( m_offlF_enabled )
    2516              :         {
    2517            0 :             updateSwitchIfChanged( m_indiP_offlFenable, "toggle", pcf::IndiElement::On, INDI_OK );
    2518              :         }
    2519              :         else
    2520              :         {
    2521            0 :             updateSwitchIfChanged( m_indiP_offlFenable, "toggle", pcf::IndiElement::Off, INDI_IDLE );
    2522              :         }
    2523              : 
    2524            0 :         updateIfChanged( m_indiP_offlFavgInt, "current", m_offlF_avgInt );
    2525            0 :         updateIfChanged( m_indiP_offlFgain, "current", m_offlF_gain );
    2526            0 :         updateIfChanged( m_indiP_offlFthresh, "current", m_offlF_thresh );
    2527              :     }
    2528            0 :     catch( ... )
    2529              :     {
    2530            0 :         log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
    2531            0 :         return -1;
    2532            0 :     }
    2533              : 
    2534            0 :     return 0;
    2535            0 : }
    2536              : 
    2537            0 : inline int tcsInterface::checkRecordTimes()
    2538              : {
    2539            0 :     return telemeter<tcsInterface>::checkRecordTimes(
    2540            0 :         telem_telpos(), telem_teldata(), telem_telvane(), telem_telenv(), telem_telcat(), telem_telsee() );
    2541              : }
    2542              : 
    2543            0 : inline int tcsInterface::recordTelem( const telem_telpos * )
    2544              : {
    2545            0 :     recordTelPos( true );
    2546            0 :     return 0;
    2547              : }
    2548              : 
    2549            0 : inline int tcsInterface::recordTelem( const telem_teldata * )
    2550              : {
    2551            0 :     recordTelData( true );
    2552            0 :     return 0;
    2553              : }
    2554              : 
    2555            0 : inline int tcsInterface::recordTelem( const telem_telvane * )
    2556              : {
    2557            0 :     recordTelVane( true );
    2558            0 :     return 0;
    2559              : }
    2560              : 
    2561            0 : inline int tcsInterface::recordTelem( const telem_telenv * )
    2562              : {
    2563            0 :     recordTelEnv( true );
    2564            0 :     return 0;
    2565              : }
    2566              : 
    2567            0 : inline int tcsInterface::recordTelem( const telem_telcat * )
    2568              : {
    2569            0 :     recordTelCat( true );
    2570            0 :     return 0;
    2571              : }
    2572              : 
    2573            0 : inline int tcsInterface::recordTelem( const telem_telsee * )
    2574              : {
    2575            0 :     recordTelSee( true );
    2576            0 :     return 0;
    2577              : }
    2578              : 
    2579            0 : inline int tcsInterface::recordTelPos( bool force )
    2580              : {
    2581              :     static double lastEpoch  = 0;
    2582              :     static double lastRA     = 0;
    2583              :     static double lastDec    = 0;
    2584              :     static double lastEl     = 0;
    2585              :     static double lastHA     = 0;
    2586              :     static double lastAM     = 0;
    2587              :     static double lastRotOff = 0;
    2588              : 
    2589            0 :     if( force || lastEpoch != m_telEpoch || lastRA != m_telRA || lastDec != m_telDec || lastEl != m_telEl ||
    2590            0 :         lastHA != m_telHA || lastAM != m_telAM || lastRotOff != m_telRotOff )
    2591              :     {
    2592            0 :         telem<telem_telpos>( { m_telEpoch, m_telRA, m_telDec, m_telEl, m_telHA, m_telAM, m_telRotOff } );
    2593              : 
    2594            0 :         lastEpoch  = m_telEpoch;
    2595            0 :         lastRA     = m_telRA;
    2596            0 :         lastDec    = m_telDec;
    2597            0 :         lastEl     = m_telEl;
    2598            0 :         lastHA     = m_telHA;
    2599            0 :         lastAM     = m_telAM;
    2600            0 :         lastRotOff = m_telRotOff;
    2601              :     }
    2602              : 
    2603            0 :     return 0;
    2604              : }
    2605              : 
    2606            0 : inline int tcsInterface::recordTelData( bool force )
    2607              : {
    2608              :     static int    lastROI          = -999;
    2609              :     static int    lastTracking     = -999;
    2610              :     static int    lastGuiding      = -999;
    2611              :     static int    lastSlewing      = -999;
    2612              :     static int    lastGuiderMoving = -999;
    2613              :     static double lastAz           = 0;
    2614              :     static double lastZd           = 0;
    2615              :     static double lastPA           = 0;
    2616              :     static double lastDomeAz       = 0;
    2617              :     static int    lastDomeStat     = -999;
    2618              : 
    2619            0 :     if( force || lastROI != m_telROI || lastTracking != m_telTracking || lastGuiding != m_telGuiding ||
    2620            0 :         lastSlewing != m_telSlewing || lastGuiderMoving != m_telGuiderMoving || lastAz != m_telAz ||
    2621            0 :         lastZd != m_telZd || lastPA != m_telPA || lastDomeAz != m_telDomeAz || lastDomeStat != m_telDomeStat )
    2622              :     {
    2623            0 :         telem<telem_teldata>( { m_telROI,
    2624            0 :                                 m_telTracking,
    2625            0 :                                 m_telGuiding,
    2626            0 :                                 m_telSlewing,
    2627            0 :                                 m_telGuiderMoving,
    2628            0 :                                 m_telAz,
    2629            0 :                                 m_telZd,
    2630            0 :                                 m_telPA,
    2631            0 :                                 m_telDomeAz,
    2632            0 :                                 m_telDomeStat } );
    2633              : 
    2634            0 :         lastROI          = m_telROI;
    2635            0 :         lastTracking     = m_telTracking;
    2636            0 :         lastGuiding      = m_telGuiding;
    2637            0 :         lastSlewing      = m_telSlewing;
    2638            0 :         lastGuiderMoving = m_telGuiderMoving;
    2639            0 :         lastAz           = m_telAz;
    2640            0 :         lastZd           = m_telZd;
    2641            0 :         lastPA           = m_telPA;
    2642            0 :         lastDomeAz       = m_telDomeAz;
    2643            0 :         lastDomeStat     = m_telDomeStat;
    2644              :     }
    2645              : 
    2646            0 :     return 0;
    2647              : }
    2648              : 
    2649            0 : inline int tcsInterface::recordTelVane( bool force )
    2650              : {
    2651              :     static double lastSecZ = -999;
    2652              :     static double lastEncZ = -999;
    2653              :     static double lastSecX = -999;
    2654              :     static double lastEncX = -999;
    2655              :     static double lastSecY = -999;
    2656              :     static double lastEncY = -999;
    2657              :     static double lastSecH = -999;
    2658              :     static double lastEncH = -999;
    2659              :     static double lastSecV = -999;
    2660              :     static double lastEncV = -999;
    2661              : 
    2662            0 :     if( force || lastSecZ != m_telSecZ || lastEncZ != m_telEncZ || lastSecX != m_telSecX || lastEncX != m_telEncX ||
    2663            0 :         lastSecY != m_telSecY || lastEncY != m_telEncY || lastSecH != m_telSecH || lastEncH != m_telEncH ||
    2664            0 :         lastSecV != m_telSecV || lastEncV != m_telEncV )
    2665              :     {
    2666            0 :         telem<telem_telvane>( { m_telSecZ,
    2667            0 :                                 m_telEncZ,
    2668            0 :                                 m_telSecX,
    2669            0 :                                 m_telEncX,
    2670            0 :                                 m_telSecY,
    2671            0 :                                 m_telEncY,
    2672            0 :                                 m_telSecH,
    2673            0 :                                 m_telEncH,
    2674            0 :                                 m_telSecV,
    2675            0 :                                 m_telEncV } );
    2676              : 
    2677            0 :         lastSecZ = m_telSecZ;
    2678            0 :         lastEncZ = m_telEncZ;
    2679            0 :         lastSecX = m_telSecX;
    2680            0 :         lastEncX = m_telEncX;
    2681            0 :         lastSecY = m_telSecY;
    2682            0 :         lastEncY = m_telEncY;
    2683            0 :         lastSecH = m_telSecH;
    2684            0 :         lastEncH = m_telEncH;
    2685            0 :         lastSecV = m_telSecV;
    2686            0 :         lastEncV = m_telEncV;
    2687              :     }
    2688              : 
    2689            0 :     return 0;
    2690              : }
    2691              : 
    2692            0 : inline int tcsInterface::recordTelEnv( bool force )
    2693              : {
    2694              :     static double lastWxtemp     = -999;
    2695              :     static double lastWxpres     = -999;
    2696              :     static double lastWxhumid    = -999;
    2697              :     static double lastWxwind     = -999;
    2698              :     static double lastWxwdir     = -999;
    2699              :     static double lastTtruss     = -999;
    2700              :     static double lastTcell      = -999;
    2701              :     static double lastTseccell   = -999;
    2702              :     static double lastTambient   = -999;
    2703              :     static double lastWxdewpoint = -999;
    2704              : 
    2705            0 :     if( force || lastWxtemp != m_wxtemp || lastWxpres != m_wxpres || lastWxhumid != m_wxhumid ||
    2706            0 :         lastWxwind != m_wxwind || lastWxwdir != m_wxwdir || lastTtruss != m_ttruss || lastTcell != m_tcell ||
    2707            0 :         lastTseccell != m_tseccell || lastTambient != m_tambient || lastWxdewpoint != m_wxdewpoint )
    2708              :     {
    2709            0 :         telem<telem_telenv>( { m_wxtemp,
    2710            0 :                                m_wxpres,
    2711            0 :                                m_wxhumid,
    2712            0 :                                m_wxwind,
    2713            0 :                                m_wxwdir,
    2714            0 :                                m_ttruss,
    2715            0 :                                m_tcell,
    2716            0 :                                m_tseccell,
    2717            0 :                                m_tambient,
    2718            0 :                                m_wxdewpoint } );
    2719              : 
    2720            0 :         lastWxtemp     = m_wxtemp;
    2721            0 :         lastWxpres     = m_wxpres;
    2722            0 :         lastWxhumid    = m_wxhumid;
    2723            0 :         lastWxwind     = m_wxwind;
    2724            0 :         lastWxwdir     = m_wxwdir;
    2725            0 :         lastTtruss     = m_ttruss;
    2726            0 :         lastTcell      = m_tcell;
    2727            0 :         lastTseccell   = m_tseccell;
    2728            0 :         lastTambient   = m_tambient;
    2729            0 :         lastWxdewpoint = m_wxdewpoint;
    2730              :     }
    2731              : 
    2732            0 :     return 0;
    2733              : }
    2734              : 
    2735            0 : inline int tcsInterface::recordTelCat( bool force )
    2736              : {
    2737            0 :     static std::string last_catObj;
    2738            0 :     static std::string last_catRm;
    2739              :     static double      last_catRA  = 0;
    2740              :     static double      last_catDec = 0;
    2741              :     static double      last_catEp  = 0;
    2742              :     static double      last_catRo  = 0;
    2743              : 
    2744            0 :     if( force || m_catObj != last_catObj || m_catRm != last_catRm || m_catRA != last_catRA || m_catDec != last_catDec ||
    2745            0 :         m_catEp != last_catEp || m_catRo != last_catRo )
    2746              :     {
    2747            0 :         telem<telem_telcat>( { m_catObj, m_catRm, m_catRA, m_catDec, m_catEp, m_catRo } );
    2748              : 
    2749            0 :         last_catObj = m_catObj;
    2750            0 :         last_catRm  = m_catRm;
    2751            0 :         last_catRA  = m_catRA;
    2752            0 :         last_catDec = m_catDec;
    2753            0 :         last_catEp  = m_catEp;
    2754            0 :         last_catRo  = m_catRo;
    2755              :     }
    2756              : 
    2757            0 :     return 0;
    2758              : }
    2759              : 
    2760            0 : inline int tcsInterface::recordTelSee( bool force )
    2761              : {
    2762              :     static int    last_dimm_time      = 0;
    2763              :     static double last_dimm_fwhm_corr = 0;
    2764              : 
    2765              :     static int    last_mag1_time      = 0;
    2766              :     static double last_mag1_fwhm_corr = 0;
    2767              : 
    2768              :     static int    last_mag2_time      = 0;
    2769              :     static double last_mag2_fwhm_corr = 0;
    2770              : 
    2771            0 :     if( force || m_dimm_time != last_dimm_time || m_dimm_fwhm_corr != last_dimm_fwhm_corr ||
    2772            0 :         m_mag1_time != last_mag1_time || m_mag1_fwhm_corr != last_mag1_fwhm_corr || m_mag2_time != last_mag2_time ||
    2773            0 :         m_mag2_fwhm_corr != last_mag2_fwhm_corr )
    2774              : 
    2775              :     {
    2776            0 :         telem<telem_telsee>(
    2777            0 :             { m_dimm_time, m_dimm_fwhm_corr, m_mag1_time, m_mag1_fwhm_corr, m_mag2_time, m_mag2_fwhm_corr } );
    2778              : 
    2779            0 :         last_dimm_time      = m_dimm_time;
    2780            0 :         last_dimm_fwhm_corr = m_dimm_fwhm_corr;
    2781              : 
    2782            0 :         last_mag1_time      = m_mag1_time;
    2783            0 :         last_mag1_fwhm_corr = m_mag1_fwhm_corr;
    2784              : 
    2785            0 :         last_mag2_time      = m_mag2_time;
    2786            0 :         last_mag2_fwhm_corr = m_mag2_fwhm_corr;
    2787              :     }
    2788              : 
    2789            0 :     return 0;
    2790              : }
    2791              : 
    2792            0 : int tcsInterface::sendPyrNudge( float x, float y, float z )
    2793              : {
    2794            0 :     if( x != 0 || y != 0 )
    2795              :     {
    2796            0 :         float dx = m_pyrNudge_C_00 * x + m_pyrNudge_C_01 * y;
    2797            0 :         float dy = m_pyrNudge_C_10 * x + m_pyrNudge_C_11 * y;
    2798              : 
    2799              :         // float cs = cos((m_pyrNudge_ang + m_pyrNudge_ang0 + m_telZd)*3.14159*180);
    2800              :         // float ss = sin((m_pyrNudge_ang + m_pyrNudge_ang0 + m_telZd)*3.14159*180);
    2801              : 
    2802              :         // float dx = x * cs - y * ss;
    2803              :         // float dy = m_pyrNudge_parity*(x * ss + y * cs);
    2804              : 
    2805              :         char ttstr[64];
    2806            0 :         snprintf( ttstr, sizeof( ttstr ), "aeg %f %f", dx, dy );
    2807              : 
    2808              :         ///\todo need logtypes for nudges and offloads
    2809            0 :         log<text_log>( std::string( "[PYRNUDGE] " ) + ttstr, logPrio::LOG_NOTICE );
    2810            0 :         if( sendMagTelCommand( ttstr, m_readTimeout ) < 0 )
    2811              :         {
    2812            0 :             log<software_error>( { __FILE__, __LINE__, std::string( "error sending command: " ) + ttstr } );
    2813            0 :             return -1;
    2814              :         }
    2815              :     }
    2816              : 
    2817            0 :     if( z != 0 )
    2818              :     {
    2819              :         char ttstr[64];
    2820            0 :         snprintf( ttstr, sizeof( ttstr ), "zimr %f", z * m_pyrNudge_F_sign );
    2821              : 
    2822            0 :         log<text_log>( std::string( "[PYRNUDGE] " ) + ttstr, logPrio::LOG_NOTICE );
    2823            0 :         if( sendMagTelCommand( ttstr, m_readTimeout ) < 0 )
    2824              :         {
    2825            0 :             log<software_error>( { __FILE__, __LINE__, std::string( "error sending command: " ) + ttstr } );
    2826            0 :             return -1;
    2827              :         }
    2828              :     }
    2829              : 
    2830            0 :     return 0;
    2831              : }
    2832              : 
    2833            0 : int tcsInterface::acquireFromGuider()
    2834              : {
    2835              :     // Convert current telescope rotator angle to radians
    2836            0 :     float q = ( m_acqZdSign * m_telZd ) * 3.14159 / 180.;
    2837              : 
    2838              :     // The rotation matrix
    2839            0 :     float az = m_acqAz0 * cos( q ) - m_acqEl0 * sin( q ) + m_acqAzOff;
    2840            0 :     float el = m_acqAz0 * sin( q ) + m_acqEl0 * cos( q ) + m_acqElOff;
    2841              : 
    2842              :     char ttstr[64];
    2843            0 :     snprintf( ttstr, sizeof( ttstr ), "aeg %f %f", az, el );
    2844              : 
    2845              :     ///\todo need logtypes for nudges and offloads
    2846            0 :     log<text_log>( std::string( "[ACQUIRE] " ) + ttstr, logPrio::LOG_NOTICE );
    2847            0 :     if( sendMagTelCommand( ttstr, m_readTimeout ) < 0 )
    2848              :     {
    2849            0 :         log<software_error>( { __FILE__, __LINE__, std::string( "error sending command: " ) + ttstr } );
    2850            0 :         return -1;
    2851              :     }
    2852              : 
    2853            0 :     float z = m_acqFocus;
    2854            0 :     snprintf( ttstr, sizeof( ttstr ), "zimr %f", z * m_pyrNudge_F_sign );
    2855              : 
    2856            0 :     log<text_log>( std::string( "[ACQUIRE] " ) + ttstr, logPrio::LOG_NOTICE );
    2857            0 :     if( sendMagTelCommand( ttstr, m_readTimeout ) < 0 )
    2858              :     {
    2859            0 :         log<software_error>( { __FILE__, __LINE__, std::string( "error sending command: " ) + ttstr } );
    2860            0 :         return -1;
    2861              :     }
    2862              : 
    2863            0 :     return 0;
    2864              : }
    2865              : 
    2866            0 : void tcsInterface::offloadThreadStart( tcsInterface *t )
    2867              : {
    2868            0 :     t->offloadThreadExec();
    2869            0 : }
    2870              : 
    2871            0 : void tcsInterface::offloadThreadExec()
    2872              : {
    2873              :     // Get the thread PID immediately so the caller can return.
    2874            0 :     m_offloadThreadID = syscall( SYS_gettid );
    2875              : 
    2876              :     static int last_loopState = -1;
    2877              : 
    2878            0 :     while( ( m_offloadThreadInit == true || state() != stateCodes::CONNECTED ) && shutdown() == 0 )
    2879              :     {
    2880            0 :         sleep( 1 );
    2881              :     }
    2882              : 
    2883              :     float avg_TT_0;
    2884              :     float avg_TT_1;
    2885            0 :     int   sincelast_TT = 0;
    2886              : 
    2887              :     float avg_F_0;
    2888            0 :     int   sincelast_F = 0;
    2889              : 
    2890            0 :     while( shutdown() == 0 )
    2891              :     {
    2892              :         // Check if loop open
    2893            0 :         if( m_loopState == 0 )
    2894              :         {
    2895              :             // If this is new, then reset the averaging buffer
    2896            0 :             if( m_loopState != last_loopState )
    2897              :             {
    2898            0 :                 m_firstRequest   = 0;
    2899            0 :                 m_lastRequest    = std::numeric_limits<size_t>::max();
    2900            0 :                 m_nRequests      = 0;
    2901            0 :                 m_last_nRequests = 0;
    2902              :             }
    2903            0 :             sleep( 1 );
    2904            0 :             last_loopState = m_loopState;
    2905            0 :             continue;
    2906              :         }
    2907              : 
    2908              :         // Check if loop paused
    2909            0 :         if( m_loopState == 1 )
    2910              :         {
    2911            0 :             sleep( 1 );
    2912            0 :             last_loopState = m_loopState;
    2913            0 :             continue;
    2914              :         }
    2915              : 
    2916              :         // Ok loop closed
    2917              : 
    2918            0 :         if( m_firstRequest == m_lastRequest )
    2919            0 :             continue; // this really should mutexed instead
    2920              : 
    2921              :         // If we got a new offload request, process it
    2922            0 :         if( m_last_nRequests != m_nRequests )
    2923              :         {
    2924              :             // std::cerr << m_firstRequest << " " << m_lastRequest << " " << m_nRequests << std::endl;
    2925              : 
    2926              :             ///\todo offloading: These sections ought to be separate functions for clarity
    2927              :             /* --- TT --- */
    2928            0 :             avg_TT_0 = 0;
    2929            0 :             avg_TT_1 = 0;
    2930              : 
    2931            0 :             int navg = 0;
    2932              : 
    2933            0 :             size_t i = m_lastRequest;
    2934              : 
    2935            0 :             for( size_t n = 0; n < m_offlTT_avgInt; ++n )
    2936              :             {
    2937            0 :                 avg_TT_0 += m_offloadRequests[0][i];
    2938            0 :                 avg_TT_1 += m_offloadRequests[1][i];
    2939            0 :                 ++navg;
    2940              : 
    2941            0 :                 if( i == m_firstRequest )
    2942            0 :                     break;
    2943              : 
    2944            0 :                 if( i == 0 )
    2945            0 :                     i = m_offloadRequests[0].size() - 1;
    2946              :                 else
    2947            0 :                     --i;
    2948              :             }
    2949              : 
    2950            0 :             avg_TT_0 /= navg;
    2951            0 :             avg_TT_1 /= navg;
    2952              : 
    2953            0 :             ++sincelast_TT;
    2954            0 :             if( sincelast_TT > m_offlTT_avgInt )
    2955              :             {
    2956            0 :                 doTToffload( avg_TT_0, avg_TT_1 );
    2957            0 :                 sincelast_TT = 0;
    2958              :             }
    2959              : 
    2960              :             /* --- Focus --- */
    2961            0 :             avg_F_0 = 0;
    2962              : 
    2963            0 :             navg = 0;
    2964              : 
    2965            0 :             i = m_lastRequest;
    2966              : 
    2967            0 :             for( size_t n = 0; n < m_offlF_avgInt; ++n )
    2968              :             {
    2969            0 :                 avg_F_0 += m_offloadRequests[2][i];
    2970            0 :                 ++navg;
    2971              : 
    2972            0 :                 if( i == m_firstRequest )
    2973            0 :                     break;
    2974              : 
    2975            0 :                 if( i == 0 )
    2976            0 :                     i = m_offloadRequests[0].size() - 1;
    2977              :                 else
    2978            0 :                     --i;
    2979              :             }
    2980              : 
    2981            0 :             avg_F_0 /= navg;
    2982              : 
    2983            0 :             ++sincelast_F;
    2984            0 :             if( sincelast_F > m_offlF_avgInt )
    2985              :             {
    2986            0 :                 doFoffload( avg_F_0 );
    2987            0 :                 sincelast_F = 0;
    2988              :             }
    2989              : 
    2990            0 :             m_last_nRequests = m_nRequests;
    2991              :         }
    2992            0 :         last_loopState = m_loopState;
    2993              : 
    2994            0 :         sleep( 1 );
    2995              :     }
    2996              : 
    2997            0 :     return;
    2998              : }
    2999              : 
    3000            0 : int tcsInterface::doTToffload( float tt_0, float tt_1 )
    3001              : {
    3002            0 :     if( m_offlTT_dump )
    3003              :     {
    3004            0 :         log<text_log>( "[OFFL] TT dumping: " + std::to_string( tt_0 ) + " " + std::to_string( tt_1 ) );
    3005            0 :         sendTToffload( tt_0, tt_1 );
    3006            0 :         m_offlTT_dump = false;
    3007              :     }
    3008              :     else
    3009              :     {
    3010            0 :         tt_0 *= m_offlTT_gain;
    3011            0 :         tt_1 *= m_offlTT_gain;
    3012              : 
    3013            0 :         if( fabs( tt_0 ) < m_offlTT_thresh )
    3014            0 :             tt_0 = 0;
    3015            0 :         if( fabs( tt_1 ) < m_offlTT_thresh )
    3016            0 :             tt_1 = 0;
    3017              : 
    3018            0 :         if( tt_0 == 0 && tt_1 == 0 )
    3019              :         {
    3020              :             // Do nothing
    3021              :         }
    3022            0 :         else if( m_offlTT_enabled )
    3023              :         {
    3024            0 :             log<text_log>( "[OFFL] TT offloading: " + std::to_string( tt_0 ) + " " + std::to_string( tt_1 ) );
    3025            0 :             sendTToffload( tt_0, tt_1 );
    3026              :         }
    3027            0 :         else if( !m_labMode )
    3028              :         {
    3029            0 :             log<text_log>( "TT offload above threshold but TT offloading disabled", logPrio::LOG_WARNING );
    3030              :         }
    3031              :     }
    3032              : 
    3033            0 :     return 0;
    3034              : }
    3035              : 
    3036            0 : int tcsInterface::sendTToffload( float tt_0, float tt_1 )
    3037              : {
    3038              : 
    3039            0 :     if( m_labMode ) // If labMode we send offloads to the modulator
    3040              :     {
    3041            0 :         pcf::IndiProperty ip( pcf::IndiProperty::Number );
    3042            0 :         ip.setDevice( "modwfs" );
    3043            0 :         ip.setName( "offset12" );
    3044            0 :         ip.add( pcf::IndiElement( "dC1" ) );
    3045            0 :         ip.add( pcf::IndiElement( "dC2" ) );
    3046              : 
    3047            0 :         sendNewProperty( ip );
    3048              : 
    3049            0 :         ip["dC1"] = tt_0;
    3050            0 :         ip["dC2"] = tt_1;
    3051              : 
    3052            0 :         sendNewProperty( ip );
    3053            0 :         return 0;
    3054            0 :     }
    3055              : 
    3056              :     char ttstr[64];
    3057              : 
    3058            0 :     snprintf( ttstr, sizeof( ttstr ), "aeg %f %f", tt_0, tt_1 );
    3059              : 
    3060            0 :     log<text_log>( std::string( "[OFFL] sending: " ) + ttstr );
    3061              : 
    3062            0 :     return sendMagTelCommand( ttstr, m_readTimeout );
    3063              : }
    3064              : 
    3065            0 : int tcsInterface::doFoffload( float F_0 )
    3066              : {
    3067            0 :     if( m_offlF_dump )
    3068              :     {
    3069            0 :         log<text_log>( "[OFFL] Focus dumping: " + std::to_string( F_0 ) );
    3070            0 :         sendFoffload( F_0 );
    3071            0 :         m_offlF_dump = false;
    3072              :     }
    3073              :     else
    3074              :     {
    3075            0 :         F_0 *= m_offlF_gain;
    3076              : 
    3077            0 :         if( fabs( F_0 ) < m_offlF_thresh )
    3078            0 :             F_0 = 0;
    3079              : 
    3080            0 :         if( F_0 == 0 )
    3081              :         {
    3082              :         }
    3083            0 :         else if( m_offlF_enabled )
    3084              :         {
    3085            0 :             log<text_log>( "[OFFL] Focus sending: " + std::to_string( F_0 ) );
    3086            0 :             sendFoffload( F_0 );
    3087              :         }
    3088            0 :         else if( !m_labMode )
    3089              :         {
    3090              : 
    3091            0 :             log<text_log>( "Focus offload above threshold but Focus offloading disabled", logPrio::LOG_WARNING );
    3092              :         }
    3093              :     }
    3094            0 :     return 0;
    3095              : }
    3096              : 
    3097            0 : int tcsInterface::sendFoffload( float F_0 )
    3098              : {
    3099              :     char fstr[64];
    3100              : 
    3101              :     // Use zimr to update the IMA values
    3102            0 :     snprintf( fstr, sizeof( fstr ), "zimr %f", F_0 );
    3103              : 
    3104            0 :     log<text_log>( std::string( "[OFFL] sending: " ) + fstr );
    3105              : 
    3106            0 :     return sendMagTelCommand( fstr, m_readTimeout );
    3107              : }
    3108              : 
    3109            0 : INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_labMode )( const pcf::IndiProperty &ipRecv )
    3110              : {
    3111            0 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_labMode, ipRecv );
    3112              : 
    3113              :     if( !ipRecv.find( "toggle" ) )
    3114              :     {
    3115              :         return log<software_error, -1>( { __FILE__, __LINE__, "no toggle element" } );
    3116              :     }
    3117              : 
    3118              :     bool labMode;
    3119              : 
    3120              :     if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
    3121              :     {
    3122              :         labMode = true;
    3123              :     }
    3124              :     else
    3125              :     {
    3126              :         labMode = false;
    3127              :     }
    3128              : 
    3129              :     if( m_labMode == labMode )
    3130              :     {
    3131              :         return 0;
    3132              :     }
    3133              : 
    3134              :     m_labMode = labMode;
    3135              : 
    3136              :     if( m_labMode )
    3137              :     {
    3138              :         log<text_log>( "lab mode ON", logPrio::LOG_NOTICE );
    3139              :         updateSwitchIfChanged( m_indiP_labMode, "toggle", pcf::IndiElement::On, INDI_OK );
    3140              :     }
    3141              :     else
    3142              :     {
    3143              :         log<text_log>( "lab mode OFF", logPrio::LOG_NOTICE );
    3144              :         updateSwitchIfChanged( m_indiP_labMode, "toggle", pcf::IndiElement::Off, INDI_OK );
    3145              :     }
    3146              : 
    3147              :     return 0;
    3148              : }
    3149              : 
    3150            3 : INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_pyrNudge )( const pcf::IndiProperty &ipRecv )
    3151              : {
    3152            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_pyrNudge, ipRecv );
    3153              : 
    3154              :     float x = 0;
    3155              :     float y = 0;
    3156              :     float z = 0;
    3157              : 
    3158              :     if( ipRecv.find( "y" ) )
    3159              :     {
    3160              :         x = ipRecv["y"].get<float>();
    3161              :     }
    3162              : 
    3163              :     if( ipRecv.find( "x" ) )
    3164              :     {
    3165              :         y = ipRecv["x"].get<float>();
    3166              :     }
    3167              : 
    3168              :     if( ipRecv.find( "z" ) )
    3169              :     {
    3170              :         z = ipRecv["z"].get<float>();
    3171              :     }
    3172              : 
    3173              :     return sendPyrNudge( x, y, z );
    3174              : }
    3175              : 
    3176            3 : INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_acqFromGuider )( const pcf::IndiProperty &ipRecv )
    3177              : {
    3178            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_acqFromGuider, ipRecv );
    3179              : 
    3180              :     if( !ipRecv.find( "request" ) )
    3181              :     {
    3182              :         return 0;
    3183              :     }
    3184              : 
    3185              :     if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
    3186              :     {
    3187              :         return acquireFromGuider();
    3188              :     }
    3189              : 
    3190              :     return 0;
    3191              : }
    3192              : 
    3193            0 : INDI_SETCALLBACK_DEFN( tcsInterface, m_indiP_loopState )( const pcf::IndiProperty &ipRecv )
    3194              : {
    3195            0 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_loopState, ipRecv );
    3196              : 
    3197              :     if( !ipRecv.find( "toggle" ) )
    3198              :         return 0;
    3199              : 
    3200              :     if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
    3201              :     {
    3202              :         m_loopState = 2;
    3203              :     }
    3204              :     else
    3205              :     {
    3206              :         m_loopState = 0;
    3207              :     }
    3208              : 
    3209              :     return 0;
    3210              : }
    3211              : 
    3212            0 : INDI_SETCALLBACK_DEFN( tcsInterface, m_indiP_offloadCoeffs )( const pcf::IndiProperty &ipRecv )
    3213              : {
    3214            0 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offloadCoeffs, ipRecv );
    3215              : 
    3216              :     if( m_loopState != 2 )
    3217              :         return 0;
    3218              : 
    3219              :     size_t nextReq = m_lastRequest + 1;
    3220              : 
    3221              :     if( nextReq >= m_offloadRequests[0].size() )
    3222              :         nextReq = 0;
    3223              : 
    3224              :     // Tip-Tilt
    3225              :     float tt0 = ipRecv["00"].get<float>();
    3226              :     float tt1 = ipRecv["01"].get<float>();
    3227              : 
    3228              :     if( m_labMode )
    3229              :     {
    3230              :         m_offloadRequests[0][nextReq] = m_lab_offlTT_C_00 * tt0 + m_lab_offlTT_C_01 * tt1;
    3231              :         m_offloadRequests[1][nextReq] = m_lab_offlTT_C_10 * tt0 + m_lab_offlTT_C_11 * tt1;
    3232              :     }
    3233              :     else
    3234              :     {
    3235              :         m_offloadRequests[0][nextReq] = m_offlTT_C_00 * tt0 + m_offlTT_C_01 * tt1;
    3236              :         m_offloadRequests[1][nextReq] = m_offlTT_C_10 * tt0 + m_offlTT_C_11 * tt1;
    3237              :     }
    3238              : 
    3239              :     // Focus
    3240              :     float f0 = ipRecv["02"].get<float>();
    3241              : 
    3242              :     m_offloadRequests[2][nextReq] = m_offlCFocus_00 * f0;
    3243              : 
    3244              :     // Coma
    3245              :     float c0 = ipRecv["03"].get<float>();
    3246              :     float c1 = ipRecv["04"].get<float>();
    3247              : 
    3248              :     m_offloadRequests[3][nextReq] = m_offlCComa_00 * c0 + m_offlCComa_01 * c1;
    3249              :     m_offloadRequests[4][nextReq] = m_offlCComa_10 * c0 + m_offlCComa_11 * c1;
    3250              : 
    3251              :     // Now update circ buffer
    3252              :     m_lastRequest = nextReq;
    3253              :     ++m_nRequests;
    3254              :     if( m_nRequests > m_offloadRequests[0].size() )
    3255              :         ++m_firstRequest;
    3256              :     if( m_firstRequest >= m_offloadRequests[0].size() )
    3257              :         m_firstRequest = 0;
    3258              :     return 0;
    3259              : }
    3260              : 
    3261            3 : INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlTTenable )( const pcf::IndiProperty &ipRecv )
    3262              : {
    3263            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlTTenable, ipRecv );
    3264              : 
    3265              :     if( ipRecv.getName() != m_indiP_offlTTenable.getName() )
    3266              :     {
    3267              :         log<software_error>( { __FILE__, __LINE__, "wrong INDI property received." } );
    3268              :         return -1;
    3269              :     }
    3270              : 
    3271              :     if( !ipRecv.find( "toggle" ) )
    3272              :         return 0;
    3273              : 
    3274              :     if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On && m_offlTT_enabled == false )
    3275              :     {
    3276              :         updateSwitchIfChanged( m_indiP_offlTTenable, "toggle", pcf::IndiElement::On, INDI_OK );
    3277              : 
    3278              :         m_offlTT_enabled = true;
    3279              :     }
    3280              :     else if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off && m_offlTT_enabled == true )
    3281              :     {
    3282              :         updateSwitchIfChanged( m_indiP_offlTTenable, "toggle", pcf::IndiElement::Off, INDI_IDLE );
    3283              : 
    3284              :         m_offlTT_enabled = false;
    3285              :     }
    3286              : 
    3287              :     return 0;
    3288              : }
    3289              : 
    3290            3 : INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlTTdump )( const pcf::IndiProperty &ipRecv )
    3291              : {
    3292            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlTTdump, ipRecv );
    3293              : 
    3294              :     if( !ipRecv.find( "request" ) )
    3295              :         return 0;
    3296              : 
    3297              :     if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
    3298              :     {
    3299              :         updateSwitchIfChanged( m_indiP_offlTTdump, "request", pcf::IndiElement::On, INDI_OK );
    3300              : 
    3301              :         m_offlTT_dump = true;
    3302              :     }
    3303              : 
    3304              :     return 0;
    3305              : }
    3306              : 
    3307            3 : INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlTTavgInt )( const pcf::IndiProperty &ipRecv )
    3308              : {
    3309            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlTTavgInt, ipRecv );
    3310              : 
    3311              :     int target;
    3312              : 
    3313              :     if( indiTargetUpdate( m_indiP_offlTTavgInt, target, ipRecv, true ) < 0 )
    3314              :     {
    3315              :         log<software_error>( { __FILE__, __LINE__ } );
    3316              :         return -1;
    3317              :     }
    3318              : 
    3319              :     m_offlTT_avgInt = target;
    3320              : 
    3321              :     return 0;
    3322              : }
    3323              : 
    3324            3 : INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlTTgain )( const pcf::IndiProperty &ipRecv )
    3325              : {
    3326            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlTTgain, ipRecv );
    3327              : 
    3328              :     float target;
    3329              : 
    3330              :     if( indiTargetUpdate( m_indiP_offlTTgain, target, ipRecv, true ) < 0 )
    3331              :     {
    3332              :         log<software_error>( { __FILE__, __LINE__ } );
    3333              :         return -1;
    3334              :     }
    3335              : 
    3336              :     m_offlTT_gain = target;
    3337              : 
    3338              :     return 0;
    3339              : }
    3340              : 
    3341            3 : INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlTTthresh )( const pcf::IndiProperty &ipRecv )
    3342              : {
    3343            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlTTthresh, ipRecv );
    3344              : 
    3345              :     float target;
    3346              : 
    3347              :     if( indiTargetUpdate( m_indiP_offlTTthresh, target, ipRecv, true ) < 0 )
    3348              :     {
    3349              :         log<software_error>( { __FILE__, __LINE__ } );
    3350              :         return -1;
    3351              :     }
    3352              : 
    3353              :     m_offlTT_thresh = target;
    3354              : 
    3355              :     return 0;
    3356              : }
    3357              : 
    3358            3 : INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlFenable )( const pcf::IndiProperty &ipRecv )
    3359              : {
    3360            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlFenable, ipRecv );
    3361              : 
    3362              :     if( !ipRecv.find( "toggle" ) )
    3363              :         return 0;
    3364              : 
    3365              :     if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On && m_offlF_enabled == false )
    3366              :     {
    3367              :         updateSwitchIfChanged( m_indiP_offlFenable, "toggle", pcf::IndiElement::On, INDI_OK );
    3368              : 
    3369              :         m_offlF_enabled = true;
    3370              :     }
    3371              :     else if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off && m_offlF_enabled == true )
    3372              :     {
    3373              :         updateSwitchIfChanged( m_indiP_offlFenable, "toggle", pcf::IndiElement::Off, INDI_IDLE );
    3374              : 
    3375              :         m_offlF_enabled = false;
    3376              :     }
    3377              : 
    3378              :     return 0;
    3379              : }
    3380              : 
    3381            3 : INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlFdump )( const pcf::IndiProperty &ipRecv )
    3382              : {
    3383            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlFdump, ipRecv );
    3384              : 
    3385              :     if( !ipRecv.find( "request" ) )
    3386              :         return 0;
    3387              : 
    3388              :     if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
    3389              :     {
    3390              :         updateSwitchIfChanged( m_indiP_offlFdump, "request", pcf::IndiElement::On, INDI_OK );
    3391              : 
    3392              :         m_offlF_dump = true;
    3393              :     }
    3394              : 
    3395              :     return 0;
    3396              : }
    3397              : 
    3398            3 : INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlFavgInt )( const pcf::IndiProperty &ipRecv )
    3399              : {
    3400            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlFavgInt, ipRecv );
    3401              : 
    3402              :     int target;
    3403              : 
    3404              :     if( indiTargetUpdate( m_indiP_offlFavgInt, target, ipRecv, true ) < 0 )
    3405              :     {
    3406              :         log<software_error>( { __FILE__, __LINE__ } );
    3407              :         return -1;
    3408              :     }
    3409              : 
    3410              :     m_offlF_avgInt = target;
    3411              : 
    3412              :     return 0;
    3413              : }
    3414              : 
    3415            3 : INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlFgain )( const pcf::IndiProperty &ipRecv )
    3416              : {
    3417            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlFgain, ipRecv );
    3418              : 
    3419              :     float target;
    3420              : 
    3421              :     if( indiTargetUpdate( m_indiP_offlFgain, target, ipRecv, true ) < 0 )
    3422              :     {
    3423              :         log<software_error>( { __FILE__, __LINE__ } );
    3424              :         return -1;
    3425              :     }
    3426              : 
    3427              :     m_offlF_gain = target;
    3428              : 
    3429              :     return 0;
    3430              : }
    3431              : 
    3432            3 : INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlFthresh )( const pcf::IndiProperty &ipRecv )
    3433              : {
    3434            3 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlFthresh, ipRecv );
    3435              : 
    3436              :     float target;
    3437              : 
    3438              :     std::cerr << "Got offl thresh\n";
    3439              : 
    3440              :     if( indiTargetUpdate( m_indiP_offlFthresh, target, ipRecv, true ) < 0 )
    3441              :     {
    3442              :         log<software_error>( { __FILE__, __LINE__ } );
    3443              :         return -1;
    3444              :     }
    3445              : 
    3446              :     m_offlF_thresh = target;
    3447              : 
    3448              :     return 0;
    3449              : }
    3450              : 
    3451              : } // namespace app
    3452              : } // namespace MagAOX
    3453              : 
    3454              : #endif // tcsInterface_hpp
        

Generated by: LCOV version 2.0-1