LCOV - code coverage report
Current view: top level - apps/userGainCtrl - userGainCtrl.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 15.9 % 624 99
Test Date: 2026-01-03 21:03:39 Functions: 18.3 % 60 11

            Line data    Source code
       1              : /** \file userGainCtrl.hpp
       2              :   * \brief The MagAO-X user gain control app
       3              :   *
       4              :   * \ingroup app_files
       5              :   */
       6              : 
       7              : #ifndef userGainCtrl_hpp
       8              : #define userGainCtrl_hpp
       9              : 
      10              : #include <limits>
      11              : 
      12              : #include <mx/improc/eigenCube.hpp>
      13              : #include <mx/improc/eigenImage.hpp>
      14              : #include <mx/ioutils/fits/fitsFile.hpp>
      15              : 
      16              : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
      17              : #include "../../magaox_git_version.h"
      18              : 
      19              : namespace MagAOX
      20              : {
      21              : namespace app
      22              : {
      23              : 
      24          117 : uint16_t modesAtBlock( uint16_t b )
      25              : {
      26          117 :     int16_t N = 2* (2*b+1 + 1);
      27              : 
      28          117 :     return (N+1)*(N+1) - 1;
      29              : }
      30              : 
      31              : /// Calculate the number of modes in 1 block
      32              : /** A block is 2 Fourier mode indices wide.  At index m, there are 2m linear degrees of freedom.
      33              :   * This gives [(2m+1)(2m+1)-1] total Fourier modes.  By considering the difference for 2m and 2(m-1) we
      34              :   * find the number of modes in one index is 16m + 8.  Note that m here starts from 1.
      35              :   *
      36              :   * Block number b starts from 0, and is related to m by m = 2b + 1
      37              :   */
      38            0 : uint16_t modesInBlock( uint16_t b /**< [in] the block number */)
      39              : {
      40            0 :     return 32*b + 24;
      41              : }
      42              : 
      43              : /// Calculate the number of blocks and the number of modes per block
      44              : /** A block is 2 Fourier mode m-indices wide, going around to the m < 0 side.  At index m, there are 2m linear degrees of freedom.
      45              :   * Block number b starts from 0, and is related to m by m = 2b + 1.  So for b+1 blocks, there are N = 2* (2*b+1 + 1) linear
      46              :   * degrees of freedom, giving (N+1)*(N+1) - 1 total Fourier modes, with 32*b + 24 modes per block b.
      47              :   *
      48              :   * Complicating this is the usual practice of putting pure Zernike modes into the beginning of the basis.  This accounts for
      49              :   * this if desired, always splitting Tip/Tilt and Focus into separate blocks.  Tip/Tilt can optionally be 2 separate blocks.
      50              :   */
      51           34 : int blockModes( std::vector<uint16_t> & blocks,   ///< [out] the block structure.  The size is the number of blocks, and each entry contains the nubmer of modes in that block
      52              :                 std::vector<std::string> & names, ///< [out] the name of each block
      53              :                 uint16_t Nmodes,                  ///< [in] the total number of modes
      54              :                 uint16_t Nzern,                   ///< [in] the number of Zernikes appended at the front
      55              :                 bool splitTT                      ///< [in] whether or not to split tip and tilt
      56              :               )
      57              : {
      58           34 :     double Nblocksd = (sqrt(1.0+Nmodes) - 1)/4.;
      59           34 :     int Nblocks = Nblocksd;
      60              : 
      61           34 :     if(Nblocks < Nblocksd)
      62              :     {
      63           18 :         ++Nblocks;
      64              :     }
      65              : 
      66           34 :     blocks.clear();
      67           34 :     names.clear();
      68              : 
      69           34 :     uint16_t tot = 0;
      70           34 :     if(Nzern > 0)
      71              :     {
      72           28 :         if(Nzern < 2) //not enough modes for this
      73              :         {
      74              :             //This is dumb, whomever is doing this, you should know.
      75            0 :             Nblocks += 1;
      76            0 :             blocks.push_back(1);
      77            0 :             names.push_back("Tip");
      78            0 :             tot = 1;
      79              :         }
      80           28 :         else if(splitTT)
      81              :         {
      82           16 :             Nblocks += 2;
      83           16 :             blocks.push_back(1);
      84           16 :             names.push_back("Tip");
      85           16 :             blocks.push_back(1);
      86           16 :             names.push_back("Tilt");
      87           16 :             tot = 2;
      88              :         }
      89              :         else
      90              :         {
      91           12 :             Nblocks += 1;
      92           12 :             blocks.push_back(2);
      93           12 :             names.push_back("Tip/Tilt");
      94           12 :             tot = 2;
      95              :         }
      96              : 
      97           28 :         if(Nzern > 2)
      98              :         {
      99              :             //Focus
     100           28 :             Nblocks += 1;
     101           28 :             blocks.push_back(1);
     102           28 :             names.push_back("Focus");
     103           28 :             ++tot;
     104              : 
     105           28 :             if(Nzern > 3)
     106              :             {
     107           28 :                 Nblocks += 1;
     108           28 :                 blocks.push_back(Nzern - 3);
     109           28 :                 names.push_back("Z " + std::to_string(4)+"-" + std::to_string(Nzern));
     110           28 :                 tot += blocks.back();
     111              :             }
     112              :         }
     113              :     }
     114              : 
     115           34 :     if(tot >= Nmodes) //Here we handle the case of Nzern >= Nmodes.
     116              :     {
     117            3 :         uint16_t sum = 0;
     118           14 :         for(size_t n=0; n < blocks.size(); ++n)
     119              :         {
     120           11 :             sum += blocks[n];
     121              :         }
     122              : 
     123            3 :         if(sum != Nmodes && sum != Nzern)
     124              :         {
     125            0 :             return -4;
     126              :         }
     127              : 
     128            3 :         return 0;
     129              :     }
     130              : 
     131           31 :     uint16_t currb = 0;
     132              : 
     133          123 :     while(currb < Nblocks)
     134              :     {
     135          117 :         uint16_t NAtThis = modesAtBlock(currb);
     136              : 
     137          117 :         if(NAtThis <= tot) //Still in the Zernikes at the beginning
     138              :         {
     139              :             //--Nblocks;
     140           14 :             ++currb;
     141           14 :             continue;
     142              :         }
     143              : 
     144          103 :         uint16_t Nthis = NAtThis - tot;
     145              : 
     146          103 :         if(tot + Nthis > Nmodes)
     147              :         {
     148           42 :             Nthis = Nmodes - tot;
     149              :         }
     150              : 
     151          103 :         if(Nthis == 0)
     152              :         {
     153           25 :             break;
     154              :         }
     155              : 
     156           78 :         blocks.push_back(Nthis);
     157           78 :         names.push_back("Block " + std::to_string(blocks.size()-1));
     158           78 :         tot += Nthis;
     159           78 :         ++currb;
     160              :     }
     161              : 
     162           31 :     uint16_t sum = 0;
     163          198 :     for(size_t n=0; n < blocks.size(); ++n)
     164              :     {
     165          167 :         sum += blocks[n];
     166              :     }
     167              : 
     168           31 :     if(sum != Nmodes)
     169              :     {
     170            0 :         return -3;
     171              :     }
     172              : 
     173           31 :     return 0;
     174              : 
     175              : }
     176              : 
     177              : struct gainShmimT
     178              : {
     179            0 :    static std::string configSection()
     180              :    {
     181            0 :       return "gainShmim";
     182              :    };
     183              : 
     184            0 :    static std::string indiPrefix()
     185              :    {
     186            0 :       return "gainShmim";
     187              :    };
     188              : };
     189              : 
     190              : struct multcoeffShmimT
     191              : {
     192            0 :    static std::string configSection()
     193              :    {
     194            0 :       return "multcoeffShmim";
     195              :    };
     196              : 
     197            0 :    static std::string indiPrefix()
     198              :    {
     199            0 :       return "multcoeffShmim";
     200              :    };
     201              : };
     202              : 
     203              : struct limitShmimT
     204              : {
     205            0 :    static std::string configSection()
     206              :    {
     207            0 :       return "limitShmim";
     208              :    };
     209              : 
     210            0 :    static std::string indiPrefix()
     211              :    {
     212            0 :       return "limitShmim";
     213              :    };
     214              : };
     215              : 
     216              : /** \defgroup userGainCtrl User Interface to Cacao Gains
     217              :   * \brief Tracks the cacao gain factor vector and updates upon request, using blocks.
     218              :   *
     219              :   * <a href="../handbook/operating/software/apps/userGainCtrl.html">Application Documentation</a>
     220              :   *
     221              :   * \ingroup apps
     222              :   *
     223              :   */
     224              : 
     225              : /** \defgroup userGainCtrl_files User Gain Control
     226              :   * \ingroup userGainCtrl
     227              :   */
     228              : 
     229              : /** MagAO-X application to provide a user interface to cacao gains
     230              :   *
     231              :   * \ingroup userGainCtrl
     232              :   *
     233              :   */
     234              : class userGainCtrl : public MagAOXApp<true>, public dev::shmimMonitor<userGainCtrl,gainShmimT>,
     235              :                      public dev::shmimMonitor<userGainCtrl,multcoeffShmimT>,  public dev::shmimMonitor<userGainCtrl,limitShmimT>,
     236              :                      public dev::telemeter<userGainCtrl>
     237              : {
     238              : 
     239              :     //Give the test harness access.
     240              :     friend class userGainCtrl_test;
     241              : 
     242              :     friend class dev::shmimMonitor<userGainCtrl,gainShmimT>;
     243              :     friend class dev::shmimMonitor<userGainCtrl,multcoeffShmimT>;
     244              :     friend class dev::shmimMonitor<userGainCtrl,limitShmimT>;
     245              : 
     246              :     typedef dev::telemeter<userGainCtrl> telemeterT;
     247              : 
     248              :     friend class dev::telemeter<userGainCtrl>;
     249              : 
     250              : public:
     251              : 
     252              :     //The base shmimMonitor type
     253              :     typedef dev::shmimMonitor<userGainCtrl,gainShmimT> shmimMonitorT;
     254              :     typedef dev::shmimMonitor<userGainCtrl,multcoeffShmimT> mcShmimMonitorT;
     255              :     typedef dev::shmimMonitor<userGainCtrl,limitShmimT> limitShmimMonitorT;
     256              : 
     257              :     ///Floating point type in which to do all calculations.
     258              :     typedef float realT;
     259              : 
     260              : protected:
     261              : 
     262              :     /** \name Configurable Parameters
     263              :       *@{
     264              :       */
     265              :     int m_loopNumber {-1};
     266              :     int m_nZern {0};
     267              :     bool m_splitTT {false};
     268              : 
     269              :     bool m_individualModes {false};
     270              : 
     271              :     ///@}
     272              : 
     273              :     mx::improc::eigenImage<realT> m_gainsCurrent; ///< The current gains.
     274              :     mx::improc::eigenImage<realT> m_gainsTarget; ///< The target gains.
     275              : 
     276              :     realT (*pixget)(void *, size_t) {nullptr}; ///< Pointer to a function to extract the image data as our desired type realT.
     277              : 
     278              :     mx::improc::eigenImage<realT> m_mcsCurrent; ///< The current gains.
     279              :     mx::improc::eigenImage<realT> m_mcsTarget; ///< The target gains.
     280              : 
     281              :     realT (*mc_pixget)(void *, size_t) {nullptr}; ///< Pointer to a function to extract the image data as our desired type realT.
     282              : 
     283              :     mx::improc::eigenImage<realT> m_limitsCurrent; ///< The current gains.
     284              :     mx::improc::eigenImage<realT> m_limitsTarget; ///< The target gains.
     285              : 
     286              :     realT (*limit_pixget)(void *, size_t) {nullptr}; ///< Pointer to a function to extract the image data as our desired type realT.
     287              : 
     288              :     std::vector<uint16_t> m_modeBlockStart;
     289              :     std::vector<uint16_t> m_modeBlockN;
     290              :     std::vector<std::string> m_modeBlockNames;
     291              : 
     292              :     int m_totalNModes {0}; ///< The total number of WFS modes in the calib.
     293              : 
     294              :     std::vector<float> m_modeBlockGains;
     295              :     std::vector<uint8_t> m_modeBlockGainsConstant;
     296              : 
     297              :     std::vector<float> m_modeBlockMCs;
     298              :     std::vector<uint8_t> m_modeBlockMCsConstant;
     299              : 
     300              :     std::vector<float> m_modeBlockLims;
     301              :     std::vector<uint8_t> m_modeBlockLimsConstant;
     302              : 
     303              :     std::mutex m_modeBlockMutex;
     304              : 
     305              :     mx::fits::fitsFile<float> m_ff;
     306              : 
     307              :     int m_singleModeNo {0};
     308              : 
     309              :     float m_powerLawIndex {2};
     310              :     float m_powerLawFloor {0.05};
     311              : 
     312              : public:
     313              :     /// Default c'tor.
     314              :     userGainCtrl();
     315              : 
     316              :     /// D'tor, declared and defined for noexcept.
     317           21 :     ~userGainCtrl() noexcept
     318           21 :     {}
     319              : 
     320              :     virtual void setupConfig();
     321              : 
     322              :     /// Implementation of loadConfig logic, separated for testing.
     323              :     /** This is called by loadConfig().
     324              :       */
     325              :     int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
     326              : 
     327              :     virtual void loadConfig();
     328              : 
     329              :     /// Startup function
     330              :     /**
     331              :       *
     332              :       */
     333              :     virtual int appStartup();
     334              : 
     335              :     /// Implementation of the FSM for userGainCtrl.
     336              :     /**
     337              :       * \returns 0 on no critical error
     338              :       * \returns -1 on an error requiring shutdown
     339              :       */
     340              :     virtual int appLogic();
     341              : 
     342              :     /// Shutdown the app.
     343              :     /**
     344              :       *
     345              :       */
     346              :     virtual int appShutdown();
     347              : 
     348              : protected:
     349              : 
     350              :     //int checkAOCalib(); ///< Test if the AO calib is accessible.
     351              : 
     352              :     //int getAOCalib();
     353              : 
     354              :     int getModeBlocks();
     355              : 
     356              :     int allocate( const gainShmimT & dummy /**< [in] tag to differentiate shmimMonitor parents.*/);
     357              : 
     358              :     int processImage( void * curr_src,          ///< [in] pointer to start of current frame.
     359              :                       const gainShmimT & dummy ///< [in] tag to differentiate shmimMonitor parents.
     360              :                     );
     361              : 
     362              :     int writeGains();
     363              : 
     364              :     int setBlockGain( int n,
     365              :                       float g
     366              :                     );
     367              : 
     368              :     int allocate( const multcoeffShmimT & dummy /**< [in] tag to differentiate shmimMonitor parents.*/);
     369              : 
     370              :     int processImage( void * curr_src,          ///< [in] pointer to start of current frame.
     371              :                       const multcoeffShmimT & dummy ///< [in] tag to differentiate shmimMonitor parents.
     372              :                     );
     373              : 
     374              :     int writeMCs();
     375              : 
     376              :     int setBlockMC( int n,
     377              :                      float mc
     378              :                   );
     379              : 
     380              :     int allocate( const limitShmimT & dummy /**< [in] tag to differentiate shmimMonitor parents.*/);
     381              : 
     382              :     int processImage( void * curr_src,          ///< [in] pointer to start of current frame.
     383              :                       const limitShmimT & dummy ///< [in] tag to differentiate shmimMonitor parents.
     384              :                     );
     385              : 
     386              :     int writeLimits();
     387              : 
     388              :     int setBlockLimit( int n,
     389              :                        float l
     390              :                      );
     391              : 
     392              :     int setSingleModeNo (int m);
     393              : 
     394              :     int setSingleGain( float g );
     395              : 
     396              :     int setSingleMC( float mc );
     397              : 
     398              :     void updateSingles();
     399              : 
     400              :     void powerLawIndex( float pli );
     401              : 
     402              :     void powerLawFloor( float plf );
     403              : 
     404              :     void powerLawSet();
     405              : 
     406              :     pcf::IndiProperty m_indiP_modes;
     407              : 
     408              :     pcf::IndiProperty m_indiP_zeroAll;
     409              : 
     410              :     std::vector<pcf::IndiProperty> m_indiP_blockGains;
     411              :     std::vector<pcf::IndiProperty> m_indiP_blockMCs;
     412              :     std::vector<pcf::IndiProperty> m_indiP_blockLimits;
     413              : 
     414              :     pcf::IndiProperty m_indiP_singleModeNo;
     415              :     pcf::IndiProperty m_indiP_singleGain;
     416              :     pcf::IndiProperty m_indiP_singleMC;
     417              : 
     418              :     pcf::IndiProperty m_indiP_powerLawIndex;
     419              :     pcf::IndiProperty m_indiP_powerLawFloor;
     420              :     pcf::IndiProperty m_indiP_powerLawSet;
     421              : 
     422              : public:
     423              : 
     424            0 :     INDI_NEWCALLBACK_DECL(userGainCtrl, m_indiP_zeroAll);
     425              : 
     426            0 :     INDI_NEWCALLBACK_DECL(userGainCtrl, m_indiP_singleModeNo);
     427              : 
     428            0 :     INDI_NEWCALLBACK_DECL(userGainCtrl, m_indiP_singleGain);
     429              : 
     430            0 :     INDI_NEWCALLBACK_DECL(userGainCtrl, m_indiP_singleMC);
     431              : 
     432            0 :     INDI_NEWCALLBACK_DECL(userGainCtrl, m_indiP_powerLawIndex);
     433            0 :     INDI_NEWCALLBACK_DECL(userGainCtrl, m_indiP_powerLawFloor);
     434            0 :     INDI_NEWCALLBACK_DECL(userGainCtrl, m_indiP_powerLawSet);
     435              : 
     436              :     /// The static callback function to be registered for block gains
     437              :     /** Dispatches to the relevant handler
     438              :       *
     439              :       * \returns 0 on success.
     440              :       * \returns -1 on error.
     441              :       */
     442              :     static int st_newCallBack_blockGains( void * app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
     443              :                                           const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
     444              :                                         );
     445              : 
     446              :     /// Callback to process a NEW block gain request
     447              :     /**
     448              :       * \returns 0 on success.
     449              :       * \returns -1 on error.
     450              :       */
     451              :     int newCallBack_blockGains( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
     452              : 
     453              :     /// The static callback function to be registered for block mult. coeff.s
     454              :     /** Dispatches to the relevant handler
     455              :       *
     456              :       * \returns 0 on success.
     457              :       * \returns -1 on error.
     458              :       */
     459              :     static int st_newCallBack_blockMCs( void * app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
     460              :                                         const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
     461              :                                       );
     462              : 
     463              :     /// Callback to process a NEW block mult. coeff.s
     464              :     /**
     465              :       * \returns 0 on success.
     466              :       * \returns -1 on error.
     467              :       */
     468              :     int newCallBack_blockMCs( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
     469              : 
     470              :     /// The static callback function to be registered for block limits
     471              :     /** Dispatches to the relevant handler
     472              :       *
     473              :       * \returns 0 on success.
     474              :       * \returns -1 on error.
     475              :       */
     476              :     static int st_newCallBack_blockLimits( void * app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
     477              :                                          const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
     478              :                                        );
     479              : 
     480              :     /// Callback to process a NEW block limits
     481              :     /**
     482              :       * \returns 0 on success.
     483              :       * \returns -1 on error.
     484              :       */
     485              :     int newCallBack_blockLimits( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
     486              : 
     487              :     /** \name Telemeter Interface
     488              :       *
     489              :       * @{
     490              :       */
     491              :     int checkRecordTimes();
     492              : 
     493              :     int recordTelem( const telem_blockgains * );
     494              : 
     495              :     int recordBlockGains( bool force = false );
     496              : 
     497              :     ///@}
     498              : };
     499              : 
     500              : inline
     501           63 : userGainCtrl::userGainCtrl() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
     502              : {
     503              : 
     504           21 :    shmimMonitorT::m_getExistingFirst = true;
     505           21 :    mcShmimMonitorT::m_getExistingFirst = true;
     506           21 :    limitShmimMonitorT::m_getExistingFirst = true;
     507              : 
     508           21 :    return;
     509            0 : }
     510              : 
     511              : inline
     512            0 : void userGainCtrl::setupConfig()
     513              : {
     514            0 :    shmimMonitorT::setupConfig(config);
     515            0 :    mcShmimMonitorT::setupConfig(config);
     516            0 :    limitShmimMonitorT::setupConfig(config);
     517              : 
     518            0 :    config.add("loop.number", "", "loop.number", argType::Required, "loop", "number", false, "int", "The loop number");
     519            0 :    config.add("blocks.splitTT", "", "blocks.splitTT", argType::Required, "blocks", "splitTT", false, "bool", "If true, the first block is split into two modes.");
     520            0 :    config.add("blocks.nZern", "", "blocks.nZern", argType::Required, "blocks", "nZern", false, "int", "Number of Zernikes at beginning.  T/T and F are split, the rest in their own block.");
     521              : 
     522            0 :    config.add("blocks.individualModes", "", "blocks.individualModes", argType::Required, "blocks", "individualModes", false, "bool", "make each block a single mode");
     523              : 
     524            0 :    telemeterT::setupConfig(config);
     525            0 : }
     526              : 
     527              : inline
     528            0 : int userGainCtrl::loadConfigImpl( mx::app::appConfigurator & _config )
     529              : {
     530            0 :    _config(m_loopNumber, "loop.number");
     531            0 :    _config(m_splitTT, "blocks.splitTT");
     532            0 :    _config(m_nZern, "blocks.nZern");
     533            0 :    _config(m_individualModes, "block.individualModes");
     534              : 
     535            0 :    shmimMonitorT::m_shmimName = "aol" + std::to_string(m_loopNumber) + "_mgainfact";
     536            0 :    shmimMonitorT::loadConfig(config);
     537              : 
     538            0 :    mcShmimMonitorT::m_shmimName = "aol" + std::to_string(m_loopNumber) + "_mmultfact";
     539            0 :    mcShmimMonitorT::loadConfig(config);
     540              : 
     541            0 :    limitShmimMonitorT::m_shmimName = "aol" + std::to_string(m_loopNumber) + "_mlimitfact";
     542            0 :    limitShmimMonitorT::loadConfig(config);
     543              : 
     544            0 :    if(telemeterT::loadConfig(_config) < 0)
     545              :    {
     546            0 :       log<text_log>("Error during telemeter config", logPrio::LOG_CRITICAL);
     547            0 :       m_shutdown = true;
     548              :    }
     549              : 
     550            0 :    return 0;
     551              : }
     552              : 
     553              : inline
     554            0 : void userGainCtrl::loadConfig()
     555              : {
     556            0 :    loadConfigImpl(config);
     557            0 : }
     558              : 
     559              : inline
     560            0 : int userGainCtrl::appStartup()
     561              : {
     562            0 :    createROIndiNumber( m_indiP_modes, "modes", "Loop Modes", "Loop Controls");
     563            0 :    indi::addNumberElement(m_indiP_modes, "total", 0, 1, 25000, "Total Modes");
     564            0 :    indi::addNumberElement(m_indiP_modes, "blocks", 0, 1, 99, "Mode Blocks");
     565            0 :    registerIndiPropertyReadOnly(m_indiP_modes);
     566              : 
     567              : 
     568            0 :    createStandardIndiRequestSw( m_indiP_zeroAll, "zero_all");
     569            0 :    if( registerIndiPropertyNew( m_indiP_zeroAll, INDI_NEWCALLBACK(m_indiP_zeroAll)) < 0)
     570              :    {
     571            0 :       log<software_error>({__FILE__,__LINE__});
     572            0 :       return -1;
     573              :    }
     574              : 
     575            0 :    createStandardIndiNumber<int>( m_indiP_singleModeNo, "singleModeNo", 0, 2400 ,0, "%0d", "");
     576            0 :    m_indiP_singleModeNo["current"].set(m_singleModeNo);
     577            0 :    m_indiP_singleModeNo["target"].set(m_singleModeNo);
     578            0 :    registerIndiPropertyNew(m_indiP_singleModeNo, INDI_NEWCALLBACK(m_indiP_singleModeNo));
     579              : 
     580            0 :    createStandardIndiNumber<int>( m_indiP_singleGain, "singleGain", 0, 1.5 ,0, "%0.2f", "");
     581            0 :    m_indiP_singleGain["current"].set(1);
     582            0 :    m_indiP_singleGain["target"].set(1);
     583            0 :    registerIndiPropertyNew(m_indiP_singleGain, INDI_NEWCALLBACK(m_indiP_singleGain));
     584              : 
     585            0 :    createStandardIndiNumber<int>( m_indiP_singleMC, "singleMC", 0, 1.0 ,0, "%0.2f", "");
     586            0 :    m_indiP_singleMC["current"].set(1);
     587            0 :    m_indiP_singleMC["target"].set(1);
     588            0 :    registerIndiPropertyNew(m_indiP_singleMC, INDI_NEWCALLBACK(m_indiP_singleMC));
     589              : 
     590            0 :    createStandardIndiNumber<int>( m_indiP_powerLawIndex, "pwrlaw_index", 0, 10.0 ,0, "%0.2f", "");
     591            0 :    m_indiP_powerLawIndex["current"].set(m_powerLawIndex);
     592            0 :    m_indiP_powerLawIndex["target"].set(m_powerLawIndex);
     593            0 :    registerIndiPropertyNew(m_indiP_powerLawIndex, INDI_NEWCALLBACK(m_indiP_powerLawIndex));
     594              : 
     595            0 :    createStandardIndiNumber<int>( m_indiP_powerLawFloor, "pwrlaw_floor", 0, 1.0 ,0, "%0.2f", "");
     596            0 :    m_indiP_powerLawFloor["current"].set(m_powerLawFloor);
     597            0 :    m_indiP_powerLawFloor["target"].set(m_powerLawFloor);
     598            0 :    registerIndiPropertyNew(m_indiP_powerLawFloor, INDI_NEWCALLBACK(m_indiP_powerLawFloor));
     599              : 
     600            0 :    createStandardIndiRequestSw( m_indiP_powerLawSet, "pwrlaw_set");
     601            0 :    if( registerIndiPropertyNew( m_indiP_powerLawSet, INDI_NEWCALLBACK(m_indiP_powerLawSet)) < 0)
     602              :    {
     603            0 :       log<software_error>({__FILE__,__LINE__});
     604            0 :       return -1;
     605              :    }
     606              : 
     607            0 :    if(shmimMonitorT::appStartup() < 0)
     608              :    {
     609            0 :       return log<software_error,-1>({__FILE__, __LINE__});
     610              :    }
     611              : 
     612            0 :    if(mcShmimMonitorT::appStartup() < 0)
     613              :    {
     614            0 :       return log<software_error,-1>({__FILE__, __LINE__});
     615              :    }
     616              : 
     617            0 :    if(limitShmimMonitorT::appStartup() < 0)
     618              :    {
     619            0 :       return log<software_error,-1>({__FILE__, __LINE__});
     620              :    }
     621              : 
     622            0 :    if(telemeterT::appStartup() < 0)
     623              :    {
     624            0 :       return log<software_error,-1>({__FILE__,__LINE__});
     625              :    }
     626              : 
     627            0 :    state(stateCodes::CONNECTED);
     628              : 
     629            0 :    return 0;
     630              : }
     631              : 
     632              : inline
     633            0 : int userGainCtrl::appLogic()
     634              : {
     635            0 :    if( shmimMonitorT::appLogic() < 0)
     636              :    {
     637            0 :       return log<software_error,-1>({__FILE__,__LINE__});
     638              :    }
     639              : 
     640            0 :    if( mcShmimMonitorT::appLogic() < 0)
     641              :    {
     642            0 :       return log<software_error,-1>({__FILE__,__LINE__});
     643              :    }
     644              : 
     645            0 :    if( limitShmimMonitorT::appLogic() < 0)
     646              :    {
     647            0 :       return log<software_error,-1>({__FILE__,__LINE__});
     648              :    }
     649              : 
     650            0 :    if( state() == stateCodes::READY || state() == stateCodes::OPERATING
     651            0 :               || state() == stateCodes::CONNECTED || state() == stateCodes::NOTCONNECTED  )
     652              :    {
     653            0 :       if(state() == stateCodes::NOTCONNECTED) state(stateCodes::CONNECTED);
     654            0 :       if(state() == stateCodes::CONNECTED) state(stateCodes::READY);
     655            0 :       if(state() == stateCodes::READY) state(stateCodes::OPERATING); //we just progress all the way through to operating so shmimMonitor will go.
     656              :    }
     657              : 
     658            0 :    if(state() == stateCodes::OPERATING)
     659              :    {
     660            0 :       if(telemeterT::appLogic() < 0)
     661              :       {
     662            0 :          log<software_error>({__FILE__, __LINE__});
     663            0 :          return 0;
     664              :       }
     665              :    }
     666              : 
     667            0 :    std::unique_lock<std::mutex> lock(m_indiMutex);
     668              : 
     669            0 :    if(shmimMonitorT::updateINDI() < 0)
     670              :    {
     671            0 :       log<software_error>({__FILE__, __LINE__});
     672              :    }
     673              : 
     674            0 :    if(mcShmimMonitorT::updateINDI() < 0)
     675              :    {
     676            0 :       log<software_error>({__FILE__, __LINE__});
     677              :    }
     678              : 
     679            0 :    if(limitShmimMonitorT::updateINDI() < 0)
     680              :    {
     681            0 :       log<software_error>({__FILE__, __LINE__});
     682              :    }
     683              : 
     684            0 :    for(size_t n=0; n < m_indiP_blockGains.size(); ++n)
     685              :    {
     686            0 :       updateIfChanged(m_indiP_blockGains[n], "current", m_modeBlockGains[n]);
     687              :    }
     688              : 
     689            0 :    for(size_t n=0; n < m_indiP_blockMCs.size(); ++n)
     690              :    {
     691            0 :       updateIfChanged(m_indiP_blockMCs[n], "current", m_modeBlockMCs[n]);
     692              :    }
     693              : 
     694            0 :    for(size_t n=0; n < m_indiP_blockLimits.size(); ++n)
     695              :    {
     696            0 :       updateIfChanged(m_indiP_blockLimits[n], "current", m_modeBlockLims[n]);
     697              :    }
     698              : 
     699            0 :    updateSingles();
     700              : 
     701            0 :    return 0;
     702            0 : }
     703              : 
     704              : inline
     705            0 : int userGainCtrl::appShutdown()
     706              : {
     707            0 :    shmimMonitorT::appShutdown();
     708            0 :    mcShmimMonitorT::appShutdown();
     709            0 :    limitShmimMonitorT::appShutdown();
     710              : 
     711            0 :    telemeterT::appShutdown();
     712              : 
     713            0 :    return 0;
     714              : }
     715              : 
     716              : 
     717              : inline
     718            0 : int userGainCtrl::getModeBlocks()
     719              : {
     720            0 :     if(m_individualModes)
     721              :     {
     722            0 :         m_modeBlockN.resize(shmimMonitorT::m_width);
     723              : 
     724            0 :         m_modeBlockNames.resize(shmimMonitorT::m_width);
     725              : 
     726            0 :         for(size_t n = 0; n < m_modeBlockNames.size(); ++n)
     727              :         {
     728            0 :             m_modeBlockNames.push_back("Block " + std::to_string(n));
     729              :         }
     730              :     }
     731              :     else
     732              :     {
     733            0 :         blockModes(m_modeBlockN, m_modeBlockNames, shmimMonitorT::m_width, m_nZern, m_splitTT);
     734              :     }
     735              : 
     736            0 :     uint16_t Nb = m_modeBlockN.size();
     737              : 
     738            0 :     m_modeBlockStart.resize(m_modeBlockN.size());
     739            0 :     m_modeBlockStart[0] = 0;
     740            0 :     for(size_t n = 1; n < m_modeBlockN.size(); ++n)
     741              :     {
     742            0 :         m_modeBlockStart[n] = m_modeBlockStart[n-1] + m_modeBlockN[n-1];
     743              :     }
     744              : 
     745            0 :     log<text_log>("loading new gain block structure");
     746              : 
     747            0 :     m_modeBlockGains.resize(Nb);
     748            0 :     m_modeBlockGainsConstant.resize(Nb);
     749              : 
     750            0 :     m_modeBlockMCs.resize(Nb);
     751            0 :     m_modeBlockMCsConstant.resize(Nb);
     752              : 
     753            0 :     m_modeBlockLims.resize(Nb);
     754            0 :     m_modeBlockLimsConstant.resize(Nb);
     755              : 
     756              :     //-- modify INDI vars --
     757            0 :     std::unique_lock<std::mutex> indilock(m_indiMutex);
     758              : 
     759            0 :     m_indiP_modes["total"] = m_modeBlockStart[m_modeBlockStart.size()-1] + m_modeBlockN[m_modeBlockN.size()-1];
     760            0 :     m_indiP_modes["blocks"] = m_modeBlockStart.size();
     761              : 
     762              :     //First just delete all existing blockXX elements
     763            0 :     for(int n=0; n < 100; ++n)
     764              :     {
     765              :         char str[16];
     766            0 :         snprintf(str, sizeof(str), "%02d", n);
     767            0 :         std::string en = "block";
     768            0 :         en += str;
     769              : 
     770            0 :         if(m_indiP_modes.find(en)) m_indiP_modes.remove(en);
     771            0 :     }
     772              : 
     773              :     //Erase existing block gains
     774            0 :     if(m_indiP_blockGains.size() > 0)
     775              :     {
     776            0 :        for(size_t n=0; n < m_indiP_blockGains.size(); ++n)
     777              :        {
     778            0 :             if(m_indiDriver) m_indiDriver->sendDelProperty(m_indiP_blockGains[n]);
     779            0 :             if(!m_indiNewCallBacks.erase(m_indiP_blockGains[n].createUniqueKey()))
     780              :             {
     781            0 :                 log<software_error>({__FILE__, __LINE__, "failed to erase " + m_indiP_blockGains[n].createUniqueKey()});
     782              :             }
     783              :        }
     784              :     }
     785            0 :     m_indiP_blockGains.clear();
     786              : 
     787              :     //Erase existing block mult. coeffs
     788            0 :     if(m_indiP_blockMCs.size() > 0)
     789              :     {
     790            0 :         for(size_t n=0; n < m_indiP_blockMCs.size(); ++n)
     791              :         {
     792            0 :             if(m_indiDriver) m_indiDriver->sendDelProperty(m_indiP_blockMCs[n]);
     793            0 :             if(!m_indiNewCallBacks.erase(m_indiP_blockMCs[n].createUniqueKey()))
     794              :             {
     795            0 :                 log<software_error>({__FILE__, __LINE__, "failed to erase " + m_indiP_blockMCs[n].createUniqueKey()});
     796              :             }
     797              :        }
     798              :     }
     799            0 :     m_indiP_blockMCs.clear();
     800              : 
     801              :     //Erase existing block limits
     802            0 :     if(m_indiP_blockLimits.size() > 0)
     803              :     {
     804            0 :         for(size_t n=0; n < m_indiP_blockLimits.size(); ++n)
     805              :         {
     806            0 :             if(m_indiDriver) m_indiDriver->sendDelProperty(m_indiP_blockLimits[n]);
     807            0 :             if(!m_indiNewCallBacks.erase(m_indiP_blockLimits[n].createUniqueKey()))
     808              :             {
     809            0 :                 log<software_error>({__FILE__, __LINE__, "failed to erase " + m_indiP_blockLimits[n].createUniqueKey()});
     810              :             }
     811              :         }
     812              :     }
     813            0 :     m_indiP_blockLimits.clear();
     814              : 
     815            0 :     m_indiP_blockGains.resize(Nb);
     816            0 :     m_indiP_blockMCs.resize(Nb);
     817            0 :     m_indiP_blockLimits.resize(Nb);
     818              : 
     819              :     //Then add in what we want.
     820            0 :     for(size_t n=0; n < Nb; ++n)
     821              :     {
     822              :         char str[16];
     823            0 :         int nn = n;
     824            0 :         snprintf(str, sizeof(str), "%02d", nn);
     825            0 :         std::string en = "block";
     826            0 :         en += str;
     827            0 :         indi::addNumberElement(m_indiP_modes, en, 0, 1, 99, "Block " + std::to_string(nn));
     828            0 :         m_indiP_modes[en] = m_modeBlockN[n];
     829              : 
     830            0 :         createStandardIndiNumber<float>( m_indiP_blockGains[n], en + "_gain", 0.0, 10.0, 0.01, "%0.3f", m_modeBlockNames[n] + " Gain", "Loop Controls");
     831            0 :         registerIndiPropertyNew( m_indiP_blockGains[n],  st_newCallBack_blockGains);
     832            0 :         if(m_indiDriver) m_indiDriver->sendSetProperty (m_indiP_blockGains[n]);
     833              : 
     834            0 :         createStandardIndiNumber<float>( m_indiP_blockMCs[n], en + "_multcoeff", 0.0, 1.0, 0.01, "%0.3f", m_modeBlockNames[n] + " Mult. Coeff", "Loop Controls");
     835            0 :         registerIndiPropertyNew( m_indiP_blockMCs[n],  st_newCallBack_blockMCs);
     836            0 :         if(m_indiDriver) m_indiDriver->sendSetProperty (m_indiP_blockMCs[n]);
     837              : 
     838            0 :         createStandardIndiNumber<float>( m_indiP_blockLimits[n], en + "_limit", 0.0, 100.0, 0.01, "%0.3f", m_modeBlockNames[n] + " Limit", "Loop Controls");
     839            0 :         registerIndiPropertyNew( m_indiP_blockLimits[n],  st_newCallBack_blockLimits);
     840            0 :         if(m_indiDriver) m_indiDriver->sendSetProperty (m_indiP_blockLimits[n]);
     841            0 :     }
     842              : 
     843            0 :     if(m_indiDriver) m_indiDriver->sendSetProperty (m_indiP_modes); //might not exist yet!
     844              : 
     845            0 :     return 0;
     846            0 : }
     847              : 
     848              : inline
     849            0 : int userGainCtrl::allocate(const gainShmimT & dummy)
     850              : {
     851              :     static_cast<void>(dummy); //be unused
     852              : 
     853            0 :     std::unique_lock<std::mutex> lock(m_modeBlockMutex);
     854              : 
     855            0 :     m_gainsCurrent.resize(shmimMonitorT::m_width, shmimMonitorT::m_height);
     856            0 :     m_gainsTarget.resize(shmimMonitorT::m_width, shmimMonitorT::m_height);
     857              : 
     858            0 :     getModeBlocks();
     859              : 
     860            0 :     pixget = getPixPointer<realT>(shmimMonitorT::m_dataType);
     861              : 
     862            0 :     return 0;
     863            0 : }
     864              : 
     865              : inline
     866            0 : int userGainCtrl::processImage( void * curr_src,
     867              :                                 const gainShmimT & dummy
     868              :                               )
     869              : {
     870              :    static_cast<void>(dummy); //be unused
     871              : 
     872            0 :    recordBlockGains();
     873              : 
     874            0 :    std::unique_lock<std::mutex> lock(m_modeBlockMutex);
     875              : 
     876            0 :    realT * data = m_gainsCurrent.data();
     877              : 
     878            0 :    for(unsigned nn=0; nn < shmimMonitorT::m_width*shmimMonitorT::m_height; ++nn)
     879              :    {
     880            0 :       data[nn] = pixget(curr_src, nn);
     881              :    }
     882              : 
     883              :    //update blocks here.
     884              : 
     885            0 :    for(size_t n =0; n < m_modeBlockStart.size(); ++n)
     886              :    {
     887            0 :       double mng = 0;
     888              : 
     889            0 :       int NN = 0;
     890              : 
     891            0 :       for(int m =0; m < m_modeBlockN[n]; ++m)
     892              :       {
     893            0 :          if(m_modeBlockStart[n] + m >= m_gainsCurrent.rows()) break;
     894            0 :          mng += m_gainsCurrent(m_modeBlockStart[n] + m,0);
     895            0 :          ++NN;
     896              :       }
     897              : 
     898            0 :       m_modeBlockGains[n] = mng / NN;
     899              : 
     900            0 :       bool constant = true;
     901              : 
     902            0 :       for(int m =0; m < m_modeBlockN[n]; ++m)
     903              :       {
     904            0 :          if(m_modeBlockStart[n] + m >= m_gainsCurrent.rows()) break;
     905            0 :          if(m_gainsCurrent(m_modeBlockStart[n] + m,0) != m_modeBlockGains[n])
     906              :          {
     907            0 :             constant = false;
     908            0 :             break;
     909              :          }
     910              :       }
     911              : 
     912            0 :       m_modeBlockGainsConstant[n] = constant;
     913              : 
     914              :    }
     915              : 
     916            0 :    for(size_t n=0; n < m_indiP_blockGains.size(); ++n)
     917              :    {
     918            0 :       updateIfChanged(m_indiP_blockGains[n], "current", m_modeBlockGains[n]);
     919              :    }
     920              : 
     921            0 :    lock.unlock();
     922              : 
     923            0 :    recordBlockGains();
     924              : 
     925            0 :    return 0;
     926            0 : }
     927              : 
     928              : inline
     929            0 : int userGainCtrl::writeGains()
     930              : {
     931            0 :    shmimMonitorT::m_imageStream.md->write=1;
     932            0 :    char * dest = static_cast<char *>(shmimMonitorT::m_imageStream.array.raw);
     933              : 
     934            0 :    memcpy(dest, m_gainsTarget.data(), shmimMonitorT::m_width*shmimMonitorT::m_height*shmimMonitorT::m_typeSize  );
     935              : 
     936              :    //Set the time of last write
     937            0 :    clock_gettime(CLOCK_REALTIME, &shmimMonitorT::m_imageStream.md->writetime);
     938              : 
     939              :    //Set the image acquisition timestamp
     940            0 :    shmimMonitorT::m_imageStream.md->atime = shmimMonitorT::m_imageStream.md->writetime;
     941              : 
     942              :    //Update cnt0
     943            0 :    shmimMonitorT::m_imageStream.md->cnt0++;
     944              : 
     945              :    //And post
     946            0 :    shmimMonitorT::m_imageStream.md->write=0;
     947            0 :    ImageStreamIO_sempost(&(shmimMonitorT::m_imageStream),-1);
     948              : 
     949            0 :    return 0;
     950              : }
     951              : 
     952            0 : int userGainCtrl::setBlockGain( int n,
     953              :                                 float g
     954              :                               )
     955              : {
     956            0 :    std::unique_lock<std::mutex> lock(m_modeBlockMutex);
     957              : 
     958            0 :    m_gainsTarget = m_gainsCurrent;
     959              : 
     960              :    //Apply a delta to each mode in the block
     961              :    //to preserve intra-block differences, unless g is 0
     962            0 :    if(g > 0)
     963              :    {
     964            0 :       for(int m =0; m < m_modeBlockN[n]; ++m)
     965              :       {
     966            0 :          if(m_modeBlockStart[n] + m > m_gainsTarget.rows() -1) break;
     967            0 :          float newg = m_gainsCurrent(m_modeBlockStart[n] + m,0) + (g - m_modeBlockGains[n]);
     968            0 :          if(newg < 0) newg = 0;
     969            0 :          m_gainsTarget(m_modeBlockStart[n] + m,0) = newg;
     970              :       }
     971              :    }
     972              :    else
     973              :    {
     974            0 :       for(int m =0; m < m_modeBlockN[n]; ++m)
     975              :       {
     976            0 :          if(m_modeBlockStart[n] + m > m_gainsTarget.rows() -1) break;
     977              : 
     978            0 :          m_gainsTarget(m_modeBlockStart[n] + m,0) = 0;
     979              :       }
     980              :    }
     981              :    //lock.unlock();
     982            0 :    recordBlockGains(true);
     983            0 :    writeGains();
     984            0 :    return 0;
     985            0 : }
     986              : 
     987              : inline
     988            0 : int userGainCtrl::allocate(const multcoeffShmimT & dummy)
     989              : {
     990              :     static_cast<void>(dummy); //be unused
     991              : 
     992            0 :     int n = 0;
     993              : 
     994            0 :     while( mcShmimMonitorT::m_width != shmimMonitorT::m_width && n < 100 )
     995              :     {
     996            0 :         mx::sys::milliSleep(100);
     997            0 :         ++n;
     998              :     }
     999              : 
    1000            0 :     if( mcShmimMonitorT::m_width != shmimMonitorT::m_width )
    1001              :     {
    1002            0 :         return -1;
    1003              :     }
    1004              : 
    1005            0 :     std::unique_lock<std::mutex> lock(m_indiMutex);
    1006              : 
    1007            0 :     m_mcsCurrent.resize(mcShmimMonitorT::m_width, mcShmimMonitorT::m_height);
    1008            0 :     m_mcsTarget.resize(mcShmimMonitorT::m_width, mcShmimMonitorT::m_height);
    1009              : 
    1010            0 :     mc_pixget = getPixPointer<realT>(mcShmimMonitorT::m_dataType);
    1011              : 
    1012            0 :     return 0;
    1013            0 : }
    1014              : 
    1015              : inline
    1016            0 : int userGainCtrl::processImage( void * curr_src,
    1017              :                                 const multcoeffShmimT & dummy
    1018              :                               )
    1019              : {
    1020              :    static_cast<void>(dummy); //be unused
    1021              : 
    1022            0 :    recordBlockGains();
    1023              : 
    1024            0 :    std::unique_lock<std::mutex> lock(m_modeBlockMutex);
    1025              : 
    1026            0 :    realT * data = m_mcsCurrent.data();
    1027              : 
    1028            0 :    for(unsigned nn=0; nn < mcShmimMonitorT::m_width*mcShmimMonitorT::m_height; ++nn)
    1029              :    {
    1030            0 :       data[nn] = mc_pixget(curr_src, nn);
    1031              :    }
    1032              : 
    1033              :    //update blocks here.
    1034              : 
    1035            0 :    for(size_t n =0; n < m_modeBlockStart.size(); ++n)
    1036              :    {
    1037            0 :       double mng = 0;
    1038              : 
    1039            0 :       int NN = 0;
    1040            0 :       for(int m =0; m < m_modeBlockN[n]; ++m)
    1041              :       {
    1042            0 :          if(m_modeBlockStart[n] + m >= m_mcsCurrent.rows()) break;
    1043            0 :          mng += m_mcsCurrent(m_modeBlockStart[n] + m,0);
    1044            0 :          ++NN;
    1045              :       }
    1046              : 
    1047            0 :       m_modeBlockMCs[n] = mng / NN;
    1048              : 
    1049              : 
    1050            0 :       bool constant = true;
    1051              : 
    1052            0 :       for(int m =0; m < m_modeBlockN[n]; ++m)
    1053              :       {
    1054            0 :          if(m_modeBlockStart[n] + m >= m_mcsCurrent.rows()) break;
    1055            0 :          if(m_mcsCurrent(m_modeBlockStart[n] + m,0) != m_modeBlockMCs[n])
    1056              :          {
    1057            0 :             constant = false;
    1058            0 :             break;
    1059              :          }
    1060              :       }
    1061              : 
    1062            0 :       m_modeBlockMCsConstant[n] = constant;
    1063              :    }
    1064              : 
    1065            0 :    for(size_t n=0; n < m_indiP_blockMCs.size(); ++n)
    1066              :    {
    1067            0 :       updateIfChanged(m_indiP_blockMCs[n], "current", m_modeBlockMCs[n]);
    1068              :    }
    1069              : 
    1070            0 :    lock.unlock();
    1071              : 
    1072            0 :    recordBlockGains();
    1073              : 
    1074            0 :    return 0;
    1075            0 : }
    1076              : 
    1077              : inline
    1078            0 : int userGainCtrl::writeMCs()
    1079              : {
    1080            0 :    mcShmimMonitorT::m_imageStream.md->write=1;
    1081            0 :    char * dest = static_cast<char *>(mcShmimMonitorT::m_imageStream.array.raw);
    1082              : 
    1083            0 :    memcpy(dest, m_mcsTarget.data(), mcShmimMonitorT::m_width*mcShmimMonitorT::m_height*mcShmimMonitorT::m_typeSize  );
    1084              : 
    1085              :    //Set the time of last write
    1086            0 :    clock_gettime(CLOCK_REALTIME, &mcShmimMonitorT::m_imageStream.md->writetime);
    1087              : 
    1088              :    //Set the image acquisition timestamp
    1089            0 :    mcShmimMonitorT::m_imageStream.md->atime = mcShmimMonitorT::m_imageStream.md->writetime;
    1090              : 
    1091              :    //Update cnt0
    1092            0 :    mcShmimMonitorT::m_imageStream.md->cnt0++;
    1093              : 
    1094              :    //And post
    1095            0 :    mcShmimMonitorT::m_imageStream.md->write=0;
    1096            0 :    ImageStreamIO_sempost(&(mcShmimMonitorT::m_imageStream),-1);
    1097              : 
    1098            0 :    return 0;
    1099              : }
    1100              : 
    1101            0 : int userGainCtrl::setBlockMC( int n,
    1102              :                               float mc
    1103              :                             )
    1104              : {
    1105            0 :    std::unique_lock<std::mutex> lock(m_modeBlockMutex);
    1106              : 
    1107            0 :    m_mcsTarget = m_mcsCurrent;
    1108              : 
    1109              :    //Apply a delta to each mode in the block
    1110              :    //to preserve intra-block differences
    1111            0 :    for(int m =0; m < m_modeBlockN[n]; ++m)
    1112              :    {
    1113            0 :       if(m_modeBlockStart[n] + m > m_mcsTarget.rows() -1) break;
    1114            0 :       m_mcsTarget(m_modeBlockStart[n] + m,0) = m_mcsCurrent(m_modeBlockStart[n] + m,0) + (mc- m_modeBlockMCs[n]);
    1115              :    }
    1116            0 :    lock.unlock();
    1117            0 :    recordBlockGains(true);
    1118            0 :    writeMCs();
    1119            0 :    return 0;
    1120            0 : }
    1121              : 
    1122              : inline
    1123            0 : int userGainCtrl::allocate(const limitShmimT & dummy)
    1124              : {
    1125              :     static_cast<void>(dummy); //be unused
    1126              : 
    1127            0 :     int n = 0;
    1128              : 
    1129            0 :     while( limitShmimMonitorT::m_width != shmimMonitorT::m_width && n < 100 )
    1130              :     {
    1131            0 :         mx::sys::milliSleep(100);
    1132            0 :         ++n;
    1133              :     }
    1134              : 
    1135            0 :     if( limitShmimMonitorT::m_width != shmimMonitorT::m_width )
    1136              :     {
    1137            0 :         return -1;
    1138              :     }
    1139              : 
    1140            0 :     std::unique_lock<std::mutex> lock(m_indiMutex);
    1141              : 
    1142            0 :     m_limitsCurrent.resize(limitShmimMonitorT::m_width, limitShmimMonitorT::m_height);
    1143            0 :     m_limitsTarget.resize(limitShmimMonitorT::m_width, limitShmimMonitorT::m_height);
    1144              : 
    1145            0 :     limit_pixget = getPixPointer<realT>(limitShmimMonitorT::m_dataType);
    1146              : 
    1147            0 :     return 0;
    1148            0 : }
    1149              : 
    1150              : inline
    1151            0 : int userGainCtrl::processImage( void * curr_src,
    1152              :                                 const limitShmimT & dummy
    1153              :                               )
    1154              : {
    1155              :    static_cast<void>(dummy); //be unused
    1156              : 
    1157            0 :    recordBlockGains();
    1158              : 
    1159            0 :    std::unique_lock<std::mutex> lock(m_modeBlockMutex);
    1160              : 
    1161            0 :    realT * data = m_limitsCurrent.data();
    1162              : 
    1163            0 :    for(unsigned nn=0; nn < limitShmimMonitorT::m_width*limitShmimMonitorT::m_height; ++nn)
    1164              :    {
    1165            0 :       data[nn] = limit_pixget(curr_src, nn);
    1166              :    }
    1167              : 
    1168              :    //update blocks here.
    1169              : 
    1170            0 :    for(size_t n =0; n < m_modeBlockStart.size(); ++n)
    1171              :    {
    1172            0 :       double mng = 0;
    1173              : 
    1174            0 :       int NN = 0;
    1175            0 :       for(int m =0; m < m_modeBlockN[n]; ++m)
    1176              :       {
    1177            0 :          if(m_modeBlockStart[n] + m >= m_limitsCurrent.rows()) break;
    1178            0 :          mng += m_limitsCurrent(m_modeBlockStart[n] + m,0);
    1179            0 :          ++NN;
    1180              :       }
    1181              : 
    1182            0 :       m_modeBlockLims[n] = mng / NN;
    1183              : 
    1184            0 :       bool constant = true;
    1185              : 
    1186            0 :       for(int m =0; m < m_modeBlockN[n]; ++m)
    1187              :       {
    1188            0 :          if(m_modeBlockStart[n] + m >= m_limitsCurrent.rows()) break;
    1189            0 :          if(m_limitsCurrent(m_modeBlockStart[n] + m,0) != m_modeBlockLims[n])
    1190              :          {
    1191            0 :             constant = false;
    1192            0 :             break;
    1193              :          }
    1194              :       }
    1195              : 
    1196            0 :       m_modeBlockLimsConstant[n] = constant;
    1197              :    }
    1198              : 
    1199            0 :    for(size_t n=0; n < m_indiP_blockLimits.size(); ++n)
    1200              :    {
    1201            0 :       updateIfChanged(m_indiP_blockLimits[n], "current", m_modeBlockLims[n]);
    1202              :    }
    1203              : 
    1204            0 :    lock.unlock();
    1205              : 
    1206            0 :    recordBlockGains();
    1207              : 
    1208            0 :    return 0;
    1209            0 : }
    1210              : 
    1211              : inline
    1212            0 : int userGainCtrl::writeLimits()
    1213              : {
    1214            0 :    limitShmimMonitorT::m_imageStream.md->write=1;
    1215            0 :    char * dest = static_cast<char *>(limitShmimMonitorT::m_imageStream.array.raw);// + next_cnt1*m_width*m_height*m_typeSize;
    1216              : 
    1217            0 :    memcpy(dest, m_limitsTarget.data(), limitShmimMonitorT::m_width*limitShmimMonitorT::m_height*limitShmimMonitorT::m_typeSize  );
    1218              : 
    1219              :    //Set the time of last write
    1220            0 :    clock_gettime(CLOCK_REALTIME, &limitShmimMonitorT::m_imageStream.md->writetime);
    1221              : 
    1222              :    //Set the image acquisition timestamp
    1223            0 :    limitShmimMonitorT::m_imageStream.md->atime = limitShmimMonitorT::m_imageStream.md->writetime;
    1224              : 
    1225              :    //Update cnt0
    1226            0 :    limitShmimMonitorT::m_imageStream.md->cnt0++;
    1227              : 
    1228              :    //And post
    1229            0 :    limitShmimMonitorT::m_imageStream.md->write=0;
    1230            0 :    ImageStreamIO_sempost(&(limitShmimMonitorT::m_imageStream),-1);
    1231              : 
    1232            0 :    return 0;
    1233              : }
    1234              : 
    1235            0 : int userGainCtrl::setBlockLimit( int n,
    1236              :                                  float l
    1237              :                                )
    1238              : {
    1239            0 :    std::unique_lock<std::mutex> lock(m_modeBlockMutex);
    1240              : 
    1241            0 :    m_limitsTarget = m_limitsCurrent;
    1242              : 
    1243              :    //Apply a delta to each mode in the block
    1244              :    //to preserve intra-block differences
    1245            0 :    for(int m =0; m < m_modeBlockN[n]; ++m)
    1246              :    {
    1247            0 :       if(m_modeBlockStart[n] + m > m_limitsTarget.rows() -1) break;
    1248            0 :       m_limitsTarget(m_modeBlockStart[n] + m,0) = m_limitsCurrent(m_modeBlockStart[n] + m,0) + (l- m_modeBlockLims[n]);
    1249              :    }
    1250            0 :    lock.unlock();
    1251            0 :    recordBlockGains(true);
    1252            0 :    writeLimits();
    1253              : 
    1254            0 :    return 0;
    1255            0 : }
    1256              : 
    1257            0 : int userGainCtrl::setSingleModeNo ( int m )
    1258              : {
    1259            0 :    m_singleModeNo = m;
    1260              : 
    1261            0 :    updateIfChanged(m_indiP_singleModeNo, "current", m);
    1262              : 
    1263            0 :    if(m_singleModeNo < 0 || m_singleModeNo >= m_gainsCurrent.rows()) return -1;
    1264            0 :    float g =  m_gainsCurrent (m_singleModeNo,0);
    1265              : 
    1266            0 :    updateIfChanged(m_indiP_singleGain, std::vector<std::string>({"current", "target"}), std::vector<float>({g,g}));
    1267              : 
    1268            0 :    if(m_singleModeNo < 0 || m_singleModeNo >= m_mcsCurrent.rows()) return -1;
    1269            0 :    float mc =  m_mcsCurrent(m_singleModeNo,0);
    1270              : 
    1271            0 :    updateIfChanged(m_indiP_singleMC, std::vector<std::string>({"current", "target"}), std::vector<float>({mc,mc}));
    1272              : 
    1273            0 :    return 0;
    1274            0 : }
    1275              : 
    1276            0 : int userGainCtrl::setSingleGain( float g )
    1277              : {
    1278            0 :    if(m_singleModeNo < 0 || m_singleModeNo >= m_gainsCurrent.rows()) return -1;
    1279            0 :    recordBlockGains();
    1280            0 :    std::unique_lock<std::mutex> lock(m_modeBlockMutex);
    1281            0 :    m_gainsTarget(m_singleModeNo,0) = g;
    1282            0 :    lock.unlock();
    1283            0 :    recordBlockGains(true);
    1284            0 :    writeGains();
    1285            0 :    return 0;
    1286            0 : }
    1287              : 
    1288            0 : int userGainCtrl::setSingleMC( float mc )
    1289              : {
    1290            0 :    if(m_singleModeNo < 0 || m_singleModeNo >= m_mcsCurrent.rows()) return -1;
    1291            0 :    recordBlockGains();
    1292            0 :    std::unique_lock<std::mutex> lock(m_modeBlockMutex);
    1293            0 :    m_mcsTarget(m_singleModeNo,0) = mc;
    1294            0 :    lock.unlock();
    1295            0 :    recordBlockGains(true);
    1296            0 :    writeMCs();
    1297            0 :    return 0;
    1298            0 : }
    1299              : 
    1300            0 : void userGainCtrl::updateSingles()
    1301              : {
    1302            0 :    if(m_singleModeNo < 0 || m_singleModeNo >= m_gainsCurrent.rows()) return;
    1303            0 :    float g =  m_gainsCurrent (m_singleModeNo,0);
    1304              : 
    1305            0 :    updateIfChanged(m_indiP_singleGain, std::vector<std::string>({"current", "target"}), std::vector<float>({g,g}));
    1306              : 
    1307            0 :    if(m_singleModeNo < 0 || m_singleModeNo >= m_mcsCurrent.rows()) return;
    1308            0 :    float mc =  m_mcsCurrent(m_singleModeNo,0);
    1309              : 
    1310            0 :    updateIfChanged(m_indiP_singleMC, std::vector<std::string>({"current", "target"}), std::vector<float>({mc,mc}));
    1311              : 
    1312            0 : }
    1313              : 
    1314            0 : void userGainCtrl::powerLawIndex( float pli )
    1315              : {
    1316            0 :     if(pli < 0)
    1317              :     {
    1318            0 :         pli = 0;
    1319              :     }
    1320              : 
    1321            0 :     m_powerLawIndex = pli;
    1322            0 :     updateIfChanged(m_indiP_powerLawIndex, std::vector<std::string>({"current", "target"}), std::vector<float>({pli,pli}));
    1323            0 : }
    1324              : 
    1325            0 : void userGainCtrl::powerLawFloor( float plf )
    1326              : {
    1327            0 :     m_powerLawFloor = plf;
    1328              : 
    1329            0 :     updateIfChanged(m_indiP_powerLawFloor, std::vector<std::string>({"current", "target"}), std::vector<float>({plf,plf}));
    1330            0 : }
    1331              : 
    1332            0 : void userGainCtrl::powerLawSet()
    1333              : {
    1334            0 :     uint16_t block0 = 0;
    1335              : 
    1336            0 :     if(m_nZern > 0)
    1337              :     {
    1338            0 :         if(m_nZern > 1)
    1339              :         {
    1340            0 :             if(m_splitTT)
    1341              :             {
    1342            0 :                 block0 = 2;
    1343              :             }
    1344              :             else
    1345              :             {
    1346            0 :                 block0 = 1;
    1347              :             }
    1348              :         }
    1349              : 
    1350            0 :         if(m_nZern > 2)
    1351              :         {
    1352            0 :             ++block0;
    1353              :         }
    1354              : 
    1355            0 :         if(m_nZern > 3)
    1356              :         {
    1357            0 :             ++block0;
    1358              :         }
    1359              :         //Now have accounted for T/T and focus.
    1360              : 
    1361            0 :         uint16_t currb = 1;
    1362            0 :         while(modesAtBlock(currb) < m_nZern)
    1363              :         {
    1364            0 :             ++currb;
    1365            0 :             ++block0;
    1366              :         }
    1367              :     }
    1368              : 
    1369            0 :     if(block0 >= m_modeBlockStart.size())
    1370              :     {
    1371            0 :         return;
    1372              :     }
    1373              : 
    1374            0 :     if(m_powerLawIndex < 0)
    1375              :     {
    1376            0 :         m_powerLawIndex = 0;
    1377              :     }
    1378              : 
    1379            0 :     float mode0 = m_modeBlockStart[block0] + 0.5*m_modeBlockN[block0];
    1380            0 :     float gain0 = m_modeBlockGains[block0];
    1381            0 :     for(size_t n=block0+1; n < m_modeBlockStart.size(); ++n)
    1382              :     {
    1383            0 :         float mode = m_modeBlockStart[n] + 0.5*m_modeBlockN[n];
    1384              : 
    1385            0 :         float imd1=(mode-mode0)/(m_totalNModes-mode0);
    1386            0 :         float imd2=pow(1.0-imd1, -m_powerLawIndex) * gain0;
    1387            0 :         float gain=(1-m_powerLawFloor)*imd2+m_powerLawFloor;
    1388              : 
    1389            0 :         if(gain < 0) gain = 0;
    1390              : 
    1391            0 :         setBlockGain(n, gain);
    1392              : 
    1393              :         //Now wait on the update, otherwise the next command can overwrite from m_gainsCurrent
    1394            0 :         int nt = 0;
    1395            0 :         while(fabs(m_modeBlockGains[n] - gain) > 1e-5 && nt < 100)
    1396              :         {
    1397            0 :             mx::sys::milliSleep(5);
    1398            0 :             ++nt;
    1399              :         }
    1400              :     }
    1401              : 
    1402            0 :     log<text_log>("Set power law: " + std::to_string(m_powerLawIndex) + " " + std::to_string(m_powerLawFloor) +
    1403            0 :                     " starting from block " + std::to_string(block0) + " " + std::to_string(gain0));
    1404              : 
    1405              : 
    1406              : }
    1407              : 
    1408            3 : INDI_NEWCALLBACK_DEFN(userGainCtrl, m_indiP_zeroAll)(const pcf::IndiProperty &ipRecv)
    1409              : {
    1410            3 :     INDI_VALIDATE_CALLBACK_PROPS(m_indiP_zeroAll, ipRecv);
    1411              : 
    1412              :    if(!ipRecv.find("request")) return 0;
    1413              : 
    1414              :    if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
    1415              :    {
    1416              :       std::unique_lock<std::mutex> lock(m_indiMutex);
    1417              : 
    1418              :       std::cerr << "Got zero all\n";
    1419              :       m_gainsTarget.setZero();
    1420              :       writeGains();
    1421              : 
    1422              :       updateSwitchIfChanged(m_indiP_zeroAll, "request", pcf::IndiElement::Off, INDI_IDLE);
    1423              :    }
    1424              : 
    1425              :    return 0;
    1426              : }
    1427              : 
    1428            3 : INDI_NEWCALLBACK_DEFN(userGainCtrl, m_indiP_singleModeNo)(const pcf::IndiProperty &ipRecv)
    1429              : {
    1430            3 :     INDI_VALIDATE_CALLBACK_PROPS(m_indiP_singleModeNo, ipRecv);
    1431              : 
    1432              :    int target;
    1433              : 
    1434              :    if( indiTargetUpdate( m_indiP_singleModeNo, target, ipRecv, true) < 0)
    1435              :    {
    1436              :       log<software_error>({__FILE__,__LINE__});
    1437              :       return -1;
    1438              :    }
    1439              : 
    1440              :    setSingleModeNo(target);
    1441              : 
    1442              :    return 0;
    1443              : }
    1444              : 
    1445            3 : INDI_NEWCALLBACK_DEFN(userGainCtrl, m_indiP_singleGain)(const pcf::IndiProperty &ipRecv)
    1446              : {
    1447            3 :    INDI_VALIDATE_CALLBACK_PROPS(m_indiP_singleGain, ipRecv);
    1448              : 
    1449              :    float target;
    1450              : 
    1451              :    if( indiTargetUpdate( m_indiP_singleGain, target, ipRecv, true) < 0)
    1452              :    {
    1453              :       log<software_error>({__FILE__,__LINE__});
    1454              :       return -1;
    1455              :    }
    1456              : 
    1457              :    setSingleGain(target);
    1458              : 
    1459              :    return 0;
    1460              : }
    1461              : 
    1462            3 : INDI_NEWCALLBACK_DEFN(userGainCtrl, m_indiP_singleMC)(const pcf::IndiProperty &ipRecv)
    1463              : {
    1464            3 :    INDI_VALIDATE_CALLBACK_PROPS(m_indiP_singleMC, ipRecv);
    1465              : 
    1466              :    float target;
    1467              : 
    1468              :    if( indiTargetUpdate( m_indiP_singleMC, target, ipRecv, true) < 0)
    1469              :    {
    1470              :       log<software_error>({__FILE__,__LINE__});
    1471              :       return -1;
    1472              :    }
    1473              : 
    1474              :    setSingleMC(target);
    1475              : 
    1476              :    return 0;
    1477              : }
    1478              : 
    1479            0 : INDI_NEWCALLBACK_DEFN(userGainCtrl, m_indiP_powerLawIndex)(const pcf::IndiProperty &ipRecv)
    1480              : {
    1481            0 :    INDI_VALIDATE_CALLBACK_PROPS(m_indiP_powerLawIndex, ipRecv);
    1482              : 
    1483              :    float target;
    1484              : 
    1485              :    if( indiTargetUpdate( m_indiP_powerLawIndex, target, ipRecv, true) < 0)
    1486              :    {
    1487              :       log<software_error>({__FILE__,__LINE__});
    1488              :       return -1;
    1489              :    }
    1490              : 
    1491              :    powerLawIndex(target);
    1492              : 
    1493              :    return 0;
    1494              : }
    1495              : 
    1496            0 : INDI_NEWCALLBACK_DEFN(userGainCtrl, m_indiP_powerLawFloor)(const pcf::IndiProperty &ipRecv)
    1497              : {
    1498            0 :    INDI_VALIDATE_CALLBACK_PROPS(m_indiP_powerLawFloor, ipRecv);
    1499              : 
    1500              :    float target;
    1501              : 
    1502              :    if( indiTargetUpdate( m_indiP_powerLawFloor, target, ipRecv, true) < 0)
    1503              :    {
    1504              :       log<software_error>({__FILE__,__LINE__});
    1505              :       return -1;
    1506              :    }
    1507              : 
    1508              :    powerLawFloor(target);
    1509              : 
    1510              :    return 0;
    1511              : }
    1512              : 
    1513            0 : INDI_NEWCALLBACK_DEFN(userGainCtrl, m_indiP_powerLawSet)(const pcf::IndiProperty &ipRecv)
    1514              : {
    1515            0 :     INDI_VALIDATE_CALLBACK_PROPS(m_indiP_powerLawSet, ipRecv);
    1516              : 
    1517              :     if(!ipRecv.find("request")) return 0;
    1518              : 
    1519              :     if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
    1520              :     {
    1521              :         std::unique_lock<std::mutex> lock(m_indiMutex);
    1522              : 
    1523              :         std::cerr << "Got Power Law\n";
    1524              :         powerLawSet();
    1525              : 
    1526              :         updateSwitchIfChanged(m_indiP_powerLawSet, "request", pcf::IndiElement::Off, INDI_IDLE);
    1527              :     }
    1528              : 
    1529              :     return 0;
    1530              : }
    1531              : 
    1532            0 : int userGainCtrl::st_newCallBack_blockGains( void * app,
    1533              :                                                const pcf::IndiProperty &ipRecv
    1534              :                                              )
    1535              : {
    1536            0 :    userGainCtrl * _app = static_cast<userGainCtrl *>(app);
    1537            0 :    return _app->newCallBack_blockGains(ipRecv);
    1538              : }
    1539              : 
    1540            3 : int userGainCtrl::newCallBack_blockGains( const pcf::IndiProperty &ipRecv )
    1541              : {
    1542            3 :    if(ipRecv.getDevice() != m_configName)
    1543              :    {
    1544              :       #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
    1545              :       log<software_error>({__FILE__, __LINE__, "wrong INDI device"});
    1546              :       #endif
    1547              : 
    1548            1 :       return -1;
    1549              :    }
    1550              : 
    1551            2 :    if(ipRecv.getName().find("block") != 0)
    1552              :    {
    1553              :       #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
    1554              :       log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
    1555              :       #endif
    1556              : 
    1557            1 :       return -1;
    1558              :    }
    1559              : 
    1560            1 :    if(ipRecv.getName().find("_gain") != 7)
    1561              :    {
    1562              :       #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
    1563              :       log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
    1564              :       #endif
    1565              : 
    1566            0 :       return -1;
    1567              :    }
    1568              : 
    1569            1 :    if(ipRecv.getName().size() != 12)
    1570              :    {
    1571              :       #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
    1572              :       log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
    1573              :       #endif
    1574              : 
    1575            0 :       return -1;
    1576              :    }
    1577              : 
    1578              :    #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    1579            1 :    return 0;
    1580              :    #endif
    1581              : 
    1582              :    int n = std::stoi(ipRecv.getName().substr(5,2));
    1583              : 
    1584              :    float current = -1;
    1585              :    float target = -1;
    1586              : 
    1587              :    if(ipRecv.find("current"))
    1588              :    {
    1589              :       current = ipRecv["current"].get<double>();
    1590              :    }
    1591              : 
    1592              :    if(ipRecv.find("target"))
    1593              :    {
    1594              :       target = ipRecv["target"].get<double>();
    1595              :    }
    1596              : 
    1597              :    if(target == -1) target = current;
    1598              : 
    1599              :    if(target == -1)
    1600              :    {
    1601              :       return 0;
    1602              :    }
    1603              : 
    1604              :    updateIfChanged(m_indiP_blockGains[n], "target", target);
    1605              : 
    1606              :    return setBlockGain(n, target);
    1607              : 
    1608              : }
    1609              : 
    1610            0 : int userGainCtrl::st_newCallBack_blockMCs( void * app,
    1611              :                                              const pcf::IndiProperty &ipRecv
    1612              :                                            )
    1613              : {
    1614            0 :    userGainCtrl * _app = static_cast<userGainCtrl *>(app);
    1615            0 :    return _app->newCallBack_blockMCs(ipRecv);
    1616              : }
    1617              : 
    1618            3 : int userGainCtrl::newCallBack_blockMCs( const pcf::IndiProperty &ipRecv )
    1619              : {
    1620            3 :    if(ipRecv.getDevice() != m_configName)
    1621              :    {
    1622              :       #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
    1623              :       log<software_error>({__FILE__, __LINE__, "wrong INDI device"});
    1624              :       #endif
    1625              : 
    1626            1 :       return -1;
    1627              :    }
    1628              : 
    1629            2 :    if(ipRecv.getName().find("block") != 0)
    1630              :    {
    1631              :       #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
    1632              :       log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
    1633              :       #endif
    1634              : 
    1635            1 :       return -1;
    1636              :    }
    1637              : 
    1638            1 :    if(ipRecv.getName().find("_multcoeff") != 7)
    1639              :    {
    1640              :       #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
    1641              :       log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
    1642              :       #endif
    1643              : 
    1644            0 :       return -1;
    1645              :    }
    1646              : 
    1647            1 :    if(ipRecv.getName().size() != 17)
    1648              :    {
    1649              :       #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
    1650              :       log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
    1651              :       #endif
    1652              : 
    1653            0 :       return -1;
    1654              :    }
    1655              : 
    1656              :    #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    1657            1 :    return 0;
    1658              :    #endif
    1659              : 
    1660              :    int n = std::stoi(ipRecv.getName().substr(5,2));
    1661              : 
    1662              :    float current = -1;
    1663              :    float target = -1;
    1664              : 
    1665              :    if(ipRecv.find("current"))
    1666              :    {
    1667              :       current = ipRecv["current"].get<double>();
    1668              :    }
    1669              : 
    1670              :    if(ipRecv.find("target"))
    1671              :    {
    1672              :       target = ipRecv["target"].get<double>();
    1673              :    }
    1674              : 
    1675              :    if(target == -1) target = current;
    1676              : 
    1677              :    if(target == -1)
    1678              :    {
    1679              :       return 0;
    1680              :    }
    1681              : 
    1682              :    updateIfChanged(m_indiP_blockMCs[n], "target", target);
    1683              : 
    1684              :    return setBlockMC(n, target);
    1685              : 
    1686              : }
    1687              : 
    1688            0 : int userGainCtrl::st_newCallBack_blockLimits( void * app,
    1689              :                                               const pcf::IndiProperty &ipRecv
    1690              :                                            )
    1691              : {
    1692            0 :    userGainCtrl * _app = static_cast<userGainCtrl *>(app);
    1693            0 :    return _app->newCallBack_blockLimits(ipRecv);
    1694              : }
    1695              : 
    1696            3 : int userGainCtrl::newCallBack_blockLimits( const pcf::IndiProperty &ipRecv )
    1697              : {
    1698            3 :    if(ipRecv.getDevice() != m_configName)
    1699              :    {
    1700              :       #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
    1701              :       log<software_error>({__FILE__, __LINE__, "wrong INDI device"});
    1702              :       #endif
    1703              : 
    1704            1 :       return -1;
    1705              :    }
    1706              : 
    1707            2 :    if(ipRecv.getName().find("block") != 0)
    1708              :    {
    1709              :       #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
    1710              :       log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
    1711              :       #endif
    1712              : 
    1713            1 :       return -1;
    1714              :    }
    1715              : 
    1716            1 :    if(ipRecv.getName().find("_limit") != 7)
    1717              :    {
    1718              :       #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
    1719              :       log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
    1720              :       #endif
    1721              : 
    1722            0 :       return -1;
    1723              :    }
    1724              : 
    1725            1 :     if(ipRecv.getName().size() != 13)
    1726              :    {
    1727              :       #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
    1728              :       log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
    1729              :       #endif
    1730              : 
    1731            0 :       return -1;
    1732              :    }
    1733              : 
    1734              :    #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
    1735            1 :    return 0;
    1736              :    #endif
    1737              : 
    1738              : 
    1739              :    int n = std::stoi(ipRecv.getName().substr(5,2));
    1740              : 
    1741              :    float current = -1;
    1742              :    float target = -1;
    1743              : 
    1744              :    if(ipRecv.find("current"))
    1745              :    {
    1746              :       current = ipRecv["current"].get<double>();
    1747              :    }
    1748              : 
    1749              :    if(ipRecv.find("target"))
    1750              :    {
    1751              :       target = ipRecv["target"].get<double>();
    1752              :    }
    1753              : 
    1754              :    if(target == -1) target = current;
    1755              : 
    1756              :    if(target == -1)
    1757              :    {
    1758              :       return 0;
    1759              :    }
    1760              : 
    1761              :    updateIfChanged(m_indiP_blockLimits[n], "target", target);
    1762              : 
    1763              :    return setBlockLimit(n, target);
    1764              : 
    1765              : }
    1766              : 
    1767              : inline
    1768            0 : int userGainCtrl::checkRecordTimes()
    1769              : {
    1770            0 :    return telemeterT::checkRecordTimes(telem_blockgains());
    1771              : }
    1772              : 
    1773              : inline
    1774            0 : int userGainCtrl::recordTelem( const telem_blockgains * )
    1775              : {
    1776            0 :    return recordBlockGains(true);
    1777              : }
    1778              : 
    1779              : inline
    1780            0 : int userGainCtrl::recordBlockGains( bool force )
    1781              : {
    1782            0 :    static std::vector<float> modeBlockGains;
    1783            0 :    static std::vector<uint8_t> modeBlockGainsConstant;
    1784              : 
    1785            0 :    static std::vector<float> modeBlockMCs;
    1786            0 :    static std::vector<uint8_t> modeBlockMCsConstant;
    1787              : 
    1788            0 :    static std::vector<float> modeBlockLims;
    1789            0 :    static std::vector<uint8_t> modeBlockLimsConstant;
    1790              : 
    1791            0 :    if(!force)
    1792              :    {
    1793            0 :       if(!(m_modeBlockGains == modeBlockGains)) force = true;
    1794              :    }
    1795              : 
    1796            0 :    if(!force)
    1797              :    {
    1798            0 :       if(!(m_modeBlockGainsConstant == modeBlockGainsConstant)) force = true;
    1799              :    }
    1800              : 
    1801            0 :    if(!force)
    1802              :    {
    1803            0 :       if(!(m_modeBlockMCs == modeBlockMCs)) force = true;
    1804              :    }
    1805              : 
    1806            0 :    if(!force)
    1807              :    {
    1808            0 :       if(!(m_modeBlockMCsConstant == modeBlockMCsConstant)) force = true;
    1809              :    }
    1810              : 
    1811            0 :    if(!force)
    1812              :    {
    1813            0 :       if(!(m_modeBlockLims == modeBlockLims)) force = true;
    1814              :    }
    1815              : 
    1816            0 :    if(!force)
    1817              :    {
    1818            0 :       if(!(m_modeBlockLimsConstant == modeBlockLimsConstant)) force = true;
    1819              :    }
    1820              : 
    1821            0 :    if(force)
    1822              :    {
    1823            0 :       telem<telem_blockgains>({m_modeBlockGains, m_modeBlockGainsConstant, m_modeBlockMCs, m_modeBlockMCsConstant, m_modeBlockLims, m_modeBlockLimsConstant});
    1824            0 :       modeBlockGains = m_modeBlockGains;
    1825            0 :       modeBlockGainsConstant = m_modeBlockGainsConstant;
    1826            0 :       modeBlockMCs = m_modeBlockMCs;
    1827            0 :       modeBlockMCsConstant = m_modeBlockMCsConstant;
    1828            0 :       modeBlockLims = m_modeBlockLims;
    1829            0 :       modeBlockLimsConstant = m_modeBlockLimsConstant;
    1830              :    }
    1831              : 
    1832            0 :    return 0;
    1833              : }
    1834              : 
    1835              : } //namespace app
    1836              : } //namespace MagAOX
    1837              : 
    1838              : #endif //userGainCtrl_hpp
        

Generated by: LCOV version 2.0-1