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

            Line data    Source code
       1              : /** \file shmimIntegrator.hpp
       2              :  * \brief The MagAO-X generic ImageStreamIO stream integrator
       3              :  *
       4              :  * \ingroup app_files
       5              :  */
       6              : 
       7              : #ifndef shmimIntegrator_hpp
       8              : #define shmimIntegrator_hpp
       9              : 
      10              : #include <limits>
      11              : 
      12              : #include <mx/improc/eigenCube.hpp>
      13              : #include <mx/improc/eigenImage.hpp>
      14              : 
      15              : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
      16              : #include "../../magaox_git_version.h"
      17              : 
      18              : namespace MagAOX
      19              : {
      20              : namespace app
      21              : {
      22              : 
      23              : struct darkShmimT
      24              : {
      25            0 :     static std::string configSection()
      26              :     {
      27            0 :         return "darkShmim";
      28              :     };
      29              : 
      30            0 :     static std::string indiPrefix()
      31              :     {
      32            0 :         return "dark";
      33              :     };
      34              : };
      35              : 
      36              : struct dark2ShmimT
      37              : {
      38            0 :     static std::string configSection()
      39              :     {
      40            0 :         return "dark2Shmim";
      41              :     };
      42              : 
      43            0 :     static std::string indiPrefix()
      44              :     {
      45            0 :         return "dark2";
      46              :     };
      47              : };
      48              : 
      49              : /** \defgroup shmimIntegrator ImageStreamIO Stream Integrator
      50              :  * \brief Integrates (i.e. averages) an ImageStreamIO image stream.
      51              :  *
      52              :  * <a href="../handbook/operating/software/apps/shmimIntegrator.html">Application Documentation</a>
      53              :  *
      54              :  * \ingroup apps
      55              :  *
      56              :  */
      57              : 
      58              : /** \defgroup shmimIntegrator_files ImageStreamIO Stream Integrator
      59              :  * \ingroup shmimIntegrator
      60              :  */
      61              : 
      62              : /** MagAO-X application to control integrating (averaging) an ImageStreamIO stream
      63              :  *
      64              :  * \ingroup shmimIntegrator
      65              :  *
      66              :  */
      67              : class shmimIntegrator : public MagAOXApp<true>,
      68              :                         public dev::shmimMonitor<shmimIntegrator>,
      69              :                         public dev::shmimMonitor<shmimIntegrator, darkShmimT>,
      70              :                         public dev::shmimMonitor<shmimIntegrator, dark2ShmimT>,
      71              :                         public dev::frameGrabber<shmimIntegrator>,
      72              :                         public dev::telemeter<shmimIntegrator>
      73              : {
      74              : 
      75              :     // Give the test harness access.
      76              :     friend class shmimIntegrator_test;
      77              : 
      78              :     friend class dev::shmimMonitor<shmimIntegrator>;
      79              :     friend class dev::shmimMonitor<shmimIntegrator, darkShmimT>;
      80              :     friend class dev::shmimMonitor<shmimIntegrator, dark2ShmimT>;
      81              :     friend class dev::frameGrabber<shmimIntegrator>;
      82              :     friend class dev::telemeter<shmimIntegrator>;
      83              : 
      84              :     // The base shmimMonitor type
      85              :     typedef dev::shmimMonitor<shmimIntegrator> shmimMonitorT;
      86              : 
      87              :     // The dark shmimMonitor type
      88              :     typedef dev::shmimMonitor<shmimIntegrator, darkShmimT> darkMonitorT;
      89              : 
      90              :     // The dark shmimMonitor type for a 2nd dark
      91              :     typedef dev::shmimMonitor<shmimIntegrator, dark2ShmimT> dark2MonitorT;
      92              : 
      93              :     // The base frameGrabber type
      94              :     typedef dev::frameGrabber<shmimIntegrator> frameGrabberT;
      95              : 
      96              :     typedef dev::telemeter<shmimIntegrator> telemeterT;
      97              : 
      98              :     /// Floating point type in which to do all calculations.
      99              :     typedef float realT;
     100              : 
     101              :   public:
     102              :     /** \name app::dev Configurations
     103              :      *@{
     104              :      */
     105              : 
     106              :     static constexpr bool c_frameGrabber_flippable =
     107              :         false; ///< app:dev config to tell framegrabber these images can not be flipped
     108              : 
     109              :     ///@}
     110              : 
     111              :   protected:
     112              :     /** \name Configurable Parameters
     113              :      *@{
     114              :      */
     115              : 
     116              :     unsigned m_nAverageDefault{ 10 }; ///< The number of frames to average.  Default 10.
     117              : 
     118              :     std::string m_fpsSource; ///< Device name for getting fps if time-based averaging is used.  This device should have
     119              :                              ///< *.fps.current.
     120              : 
     121              :     float m_avgTime{ 0 }; ///< If non zero, then m_nAverage adjusts automatically to keep a constant averaging time
     122              :                           ///< [sec].  Default 0.
     123              : 
     124              :     unsigned m_nUpdate{ 0 }; ///< The rate at which to update the average.  If 0 < m_nUpdate < m_nAverage then this is a
     125              :                              ///< moving averager. Default 0.
     126              : 
     127              :     bool m_continuous{ true }; ///< Set to false in configuration to have this run once then stop until triggered.
     128              : 
     129              :     bool m_running{ true }; ///< Set to false in configuration to have it not start averaging until triggered.
     130              : 
     131              :     std::string m_stateSource; ///< The source of the state string used for file management
     132              : 
     133              :     bool m_fileSaver{ false }; ///< Set to true in configuration to have this save and reload files automatically.
     134              : 
     135              :     ///@}
     136              : 
     137              :     mx::improc::eigenCube<realT> m_accumImages; ///< Cube used to accumulate images
     138              : 
     139              :     mx::improc::eigenImage<realT> m_avgImage; ///< The average image.
     140              : 
     141              :     unsigned m_nAverage{ 10 };
     142              : 
     143              :     float m_fps{ 0 }; ///< Current FPS from the FPS source.
     144              : 
     145              :     unsigned m_nprocessed{ 0 };
     146              :     size_t   m_currImage{ 0 };
     147              :     size_t   m_sinceUpdate{ 0 };
     148              :     bool     m_updated{ false };
     149              : 
     150              :     bool        m_imageValid{ false };
     151              :     std::string m_stateString;
     152              :     bool        m_stateStringValid{ false };
     153              :     bool        m_stateStringValidOnStart{ false };
     154              :     bool        m_stateStringChanged{ false };
     155              :     std::string m_fileSaveDir;
     156              : 
     157              :     sem_t m_smSemaphore{ 0 }; ///< Semaphore used to synchronize the fg thread and the sm thread.
     158              : 
     159              :     realT ( *pixget )( void *, size_t ){
     160              :         nullptr }; ///< Pointer to a function to extract the image data as our desired type realT.
     161              : 
     162              :     /// Mutex for locking dark operations.
     163              :     std::mutex m_darkMutex;
     164              : 
     165              :     mx::improc::eigenImage<realT> m_darkImage;
     166              :     bool                          m_darkSet{ false };
     167              :     bool                          m_darkValid{ false };
     168              :     realT ( *dark_pixget )( void *, size_t ){
     169              :         nullptr }; ///< Pointer to a function to extract the image data as our desired type realT.
     170              : 
     171              :     mx::improc::eigenImage<realT> m_dark2Image;
     172              :     bool                          m_dark2Set{ false };
     173              :     bool                          m_dark2Valid{ false };
     174              :     realT ( *dark2_pixget )( void *, size_t ){
     175              :         nullptr }; ///< Pointer to a function to extract the image data as our desired type realT.
     176              : 
     177              :   public:
     178              :     /// Default c'tor.
     179              :     shmimIntegrator();
     180              : 
     181              :     /// D'tor, declared and defined for noexcept.
     182            0 :     ~shmimIntegrator() noexcept
     183            0 :     {
     184            0 :     }
     185              : 
     186              :     virtual void setupConfig();
     187              : 
     188              :     /// Implementation of loadConfig logic, separated for testing.
     189              :     /** This is called by loadConfig().
     190              :      */
     191              :     int loadConfigImpl(
     192              :         mx::app::appConfigurator &_config /**< [in] an application configuration from which to load values*/ );
     193              : 
     194              :     virtual void loadConfig();
     195              : 
     196              :     /// Startup function
     197              :     /**
     198              :      *
     199              :      */
     200              :     virtual int appStartup();
     201              : 
     202              :     /// Implementation of the FSM for shmimIntegrator.
     203              :     /**
     204              :      * \returns 0 on no critical error
     205              :      * \returns -1 on an error requiring shutdown
     206              :      */
     207              :     virtual int appLogic();
     208              : 
     209              :     /// Shutdown the app.
     210              :     /**
     211              :      *
     212              :      */
     213              :     virtual int appShutdown();
     214              : 
     215              :   protected:
     216              :     int allocate( const dev::shmimT &dummy /**< [in] tag to differentiate shmimMonitor parents.*/ );
     217              : 
     218              :     int processImage( void              *curr_src, ///< [in] pointer to start of current frame.
     219              :                       const dev::shmimT &dummy     ///< [in] tag to differentiate shmimMonitor parents.
     220              :     );
     221              : 
     222              :     int allocate( const darkShmimT &dummy /**< [in] tag to differentiate shmimMonitor parents.*/ );
     223              : 
     224              :     int processImage( void             *curr_src, ///< [in] pointer to start of current frame.
     225              :                       const darkShmimT &dummy     ///< [in] tag to differentiate shmimMonitor parents.
     226              :     );
     227              : 
     228              :     int allocate( const dark2ShmimT &dummy /**< [in] tag to differentiate shmimMonitor parents.*/ );
     229              : 
     230              :     int processImage( void              *curr_src, ///< [in] pointer to start of current frame.
     231              :                       const dark2ShmimT &dummy     ///< [in] tag to differentiate shmimMonitor parents.
     232              :     );
     233              : 
     234              :     int findMatchingDark();
     235              : 
     236              :     /** \name dev::frameGrabber interface
     237              :      *
     238              :      * @{
     239              :      */
     240              : 
     241              :     /// Implementation of the framegrabber configureAcquisition interface
     242              :     /**
     243              :      * \returns 0 on success
     244              :      * \returns -1 on error
     245              :      */
     246              :     int configureAcquisition();
     247              : 
     248              :     /// Implementation of the framegrabber fps interface
     249              :     /**
     250              :      * \todo this needs to infer the stream fps and return it
     251              :      */
     252            0 :     float fps()
     253              :     {
     254            0 :         if( m_fps > 0 && m_nAverage > 0 && ( m_running || m_continuous ) )
     255              :         {
     256            0 :             return m_fps / m_nAverage;
     257              :         }
     258              :         else
     259              :         { // this will cause a default averaging
     260              : 
     261            0 :             return 1.0;
     262              :         }
     263              :     }
     264              : 
     265              :     /// Implementation of the framegrabber startAcquisition interface
     266              :     /**
     267              :      * \returns 0 on success
     268              :      * \returns -1 on error
     269              :      */
     270              :     int startAcquisition();
     271              : 
     272              :     /// Implementation of the framegrabber acquireAndCheckValid interface
     273              :     /**
     274              :      * \returns 0 on success
     275              :      * \returns -1 on error
     276              :      */
     277              :     int acquireAndCheckValid();
     278              : 
     279              :     /// Implementation of the framegrabber loadImageIntoStream interface
     280              :     /**
     281              :      * \returns 0 on success
     282              :      * \returns -1 on error
     283              :      */
     284              :     int loadImageIntoStream( void *dest /**< [in] */ );
     285              : 
     286              :     /// Implementation of the framegrabber reconfig interface
     287              :     /**
     288              :      * \returns 0 on success
     289              :      * \returns -1 on error
     290              :      */
     291              :     int reconfig();
     292              : 
     293              :     ///@}
     294              : 
     295              :     pcf::IndiProperty m_indiP_nAverage;
     296              : 
     297              :     pcf::IndiProperty m_indiP_avgTime;
     298              : 
     299              :     pcf::IndiProperty m_indiP_nUpdate;
     300              : 
     301              :     pcf::IndiProperty m_indiP_startAveraging;
     302              : 
     303            0 :     INDI_NEWCALLBACK_DECL( shmimIntegrator, m_indiP_nAverage );
     304            0 :     INDI_NEWCALLBACK_DECL( shmimIntegrator, m_indiP_avgTime );
     305            0 :     INDI_NEWCALLBACK_DECL( shmimIntegrator, m_indiP_nUpdate );
     306            0 :     INDI_NEWCALLBACK_DECL( shmimIntegrator, m_indiP_startAveraging );
     307              : 
     308              :     pcf::IndiProperty m_indiP_fpsSource;
     309            0 :     INDI_SETCALLBACK_DECL( shmimIntegrator, m_indiP_fpsSource );
     310              : 
     311              :     pcf::IndiProperty m_indiP_fps; ///< this integrator's FPS
     312              : 
     313              :     pcf::IndiProperty m_indiP_stateSource;
     314            0 :     INDI_SETCALLBACK_DECL( shmimIntegrator, m_indiP_stateSource );
     315              : 
     316              :     pcf::IndiProperty m_indiP_imageValid;
     317              : 
     318              :     /** \name Telemeter Interface
     319              :      *
     320              :      * @{
     321              :      */
     322              :     int checkRecordTimes();
     323              : 
     324              :     int recordTelem( const telem_fgtimings * );
     325              : 
     326              :     ///@}
     327              : };
     328              : 
     329              : inline shmimIntegrator::shmimIntegrator() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
     330              : {
     331              :     darkMonitorT::m_getExistingFirst = true;
     332              :     return;
     333              : }
     334              : 
     335            0 : inline void shmimIntegrator::setupConfig()
     336              : {
     337            0 :     shmimMonitorT::setupConfig( config );
     338            0 :     darkMonitorT::setupConfig( config );
     339            0 :     dark2MonitorT::setupConfig( config );
     340              : 
     341            0 :     frameGrabberT::setupConfig( config );
     342            0 :     telemeterT::setupConfig( config );
     343              : 
     344            0 :     config.add( "integrator.nAverage",
     345              :                 "",
     346              :                 "integrator.nAverage",
     347              :                 argType::Required,
     348              :                 "integrator",
     349              :                 "nAverage",
     350              :                 false,
     351              :                 "unsigned",
     352              :                 "The default number of frames to average.  Default 10. Can be changed via INDI." );
     353            0 :     config.add(
     354              :         "integrator.fpsSource",
     355              :         "",
     356              :         "integrator.fpsSource",
     357              :         argType::Required,
     358              :         "integrator",
     359              :         "fpsSource",
     360              :         false,
     361              :         "string",
     362              :         "Device name for getting fps if time-based averaging is used.  This device should have *.fps.current." );
     363              : 
     364            0 :     config.add( "integrator.avgTime",
     365              :                 "",
     366              :                 "integrator.avgTime",
     367              :                 argType::Required,
     368              :                 "integrator",
     369              :                 "avgTime",
     370              :                 false,
     371              :                 "float",
     372              :                 "If non zero, then m_nAverage adjusts automatically to keep a constant averaging time [sec].  Default "
     373              :                 "0. Can be changed via INDI." );
     374              : 
     375            0 :     config.add( "integrator.nUpdate",
     376              :                 "",
     377              :                 "integrator.nUpdate",
     378              :                 argType::Required,
     379              :                 "integrator",
     380              :                 "nUpdate",
     381              :                 false,
     382              :                 "unsigned",
     383              :                 "The rate at which to update the average.  If 0 < m_nUpdate < m_nAverage then this is a moving "
     384              :                 "averager. Default 0.  If 0, then it is a simple average." );
     385              : 
     386            0 :     config.add( "integrator.continuous",
     387              :                 "",
     388              :                 "integrator.continuous",
     389              :                 argType::Required,
     390              :                 "integrator",
     391              :                 "continuous",
     392              :                 false,
     393              :                 "bool",
     394              :                 "Flag controlling whether averaging is continuous or only when triggered.  Default true." );
     395            0 :     config.add( "integrator.running",
     396              :                 "",
     397              :                 "integrator.running",
     398              :                 argType::Required,
     399              :                 "integrator",
     400              :                 "running",
     401              :                 false,
     402              :                 "bool",
     403              :                 "Flag controlling whether averaging is running at startup.  Default true." );
     404              : 
     405            0 :     config.add( "integrator.stateSource",
     406              :                 "",
     407              :                 "integrator.stateSource",
     408              :                 argType::Required,
     409              :                 "integrator",
     410              :                 "stateSource",
     411              :                 false,
     412              :                 "string",
     413              :                 "///< Device name for getting the state string for file management.  This device should have "
     414              :                 "*.state_string.current." );
     415            0 :     config.add( "integrator.fileSaver",
     416              :                 "",
     417              :                 "integrator.fileSaver",
     418              :                 argType::Required,
     419              :                 "integrator",
     420              :                 "fileSaver",
     421              :                 false,
     422              :                 "bool",
     423              :                 "Flag controlling whether this saves and reloads files automatically.  Default false." );
     424            0 : }
     425              : 
     426            0 : inline int shmimIntegrator::loadConfigImpl( mx::app::appConfigurator &_config )
     427              : {
     428              : 
     429            0 :     shmimMonitorT::loadConfig( config );
     430            0 :     darkMonitorT::loadConfig( config );
     431            0 :     dark2MonitorT::loadConfig( config );
     432              : 
     433            0 :     frameGrabberT::loadConfig( config );
     434            0 :     telemeterT::loadConfig( config );
     435              : 
     436            0 :     _config( m_nAverageDefault, "integrator.nAverage" );
     437            0 :     m_nAverage = m_nAverageDefault;
     438            0 :     _config( m_fpsSource, "integrator.fpsSource" );
     439            0 :     _config( m_avgTime, "integrator.avgTime" );
     440            0 :     _config( m_nUpdate, "integrator.nUpdate" );
     441              : 
     442            0 :     _config( m_continuous, "integrator.continuous" );
     443              : 
     444            0 :     _config( m_running, "integrator.running" );
     445              : 
     446            0 :     _config( m_stateSource, "integrator.stateSource" );
     447            0 :     _config( m_fileSaver, "integrator.fileSaver" );
     448              : 
     449            0 :     return 0;
     450              : }
     451              : 
     452            0 : inline void shmimIntegrator::loadConfig()
     453              : {
     454            0 :     loadConfigImpl( config );
     455            0 : }
     456              : 
     457            0 : inline int shmimIntegrator::appStartup()
     458              : {
     459              : 
     460            0 :     createStandardIndiNumber<unsigned>(
     461            0 :         m_indiP_nAverage, "nAverage", 1, std::numeric_limits<unsigned>::max(), 1, "%u" );
     462            0 :     m_indiP_nAverage["current"].set( m_nAverage );
     463            0 :     m_indiP_nAverage["target"].set( m_nAverage );
     464              : 
     465            0 :     if( registerIndiPropertyNew( m_indiP_nAverage, INDI_NEWCALLBACK( m_indiP_nAverage ) ) < 0 )
     466              :     {
     467            0 :         log<software_error>( { __FILE__, __LINE__ } );
     468            0 :         return -1;
     469              :     }
     470              : 
     471            0 :     createStandardIndiNumber<float>( m_indiP_avgTime, "avgTime", 0, std::numeric_limits<float>::max(), 0, "%0.1f" );
     472            0 :     m_indiP_avgTime["current"].set( m_avgTime );
     473            0 :     m_indiP_avgTime["target"].set( m_avgTime );
     474              : 
     475            0 :     if( registerIndiPropertyNew( m_indiP_avgTime, INDI_NEWCALLBACK( m_indiP_avgTime ) ) < 0 )
     476              :     {
     477            0 :         log<software_error>( { __FILE__, __LINE__ } );
     478            0 :         return -1;
     479              :     }
     480              : 
     481            0 :     createStandardIndiNumber<unsigned>( m_indiP_nUpdate, "nUpdate", 1, std::numeric_limits<unsigned>::max(), 1, "%u" );
     482            0 :     m_indiP_nUpdate["current"].set( m_nUpdate );
     483            0 :     m_indiP_nUpdate["target"].set( m_nUpdate );
     484              : 
     485            0 :     if( registerIndiPropertyNew( m_indiP_nUpdate, INDI_NEWCALLBACK( m_indiP_nUpdate ) ) < 0 )
     486              :     {
     487            0 :         log<software_error>( { __FILE__, __LINE__ } );
     488            0 :         return -1;
     489              :     }
     490              : 
     491            0 :     createStandardIndiToggleSw( m_indiP_startAveraging, "start" );
     492            0 :     if( registerIndiPropertyNew( m_indiP_startAveraging, INDI_NEWCALLBACK( m_indiP_startAveraging ) ) < 0 )
     493              :     {
     494            0 :         log<software_error>( { __FILE__, __LINE__ } );
     495            0 :         return -1;
     496              :     }
     497              : 
     498            0 :     if( m_fpsSource != "" )
     499              :     {
     500            0 :         REG_INDI_SETPROP( m_indiP_fpsSource, m_fpsSource, std::string( "fps" ) );
     501              :     }
     502              : 
     503            0 :     CREATE_REG_INDI_RO_NUMBER( m_indiP_fps, "fps", "", "" );
     504            0 :     m_indiP_fps.add( pcf::IndiElement( "current" ) );
     505            0 :     m_indiP_fps["current"].set( 0 );
     506              : 
     507            0 :     if( m_fileSaver == true && m_stateSource != "" )
     508              :     {
     509            0 :         REG_INDI_SETPROP( m_indiP_stateSource, m_stateSource, std::string( "state_string" ) );
     510              : 
     511            0 :         createROIndiText( m_indiP_imageValid, "image_valid", "flag", "Image Valid", "Image", "Valid" );
     512            0 :         if( !m_imageValid ) // making sure we stay up with default
     513              :         {
     514            0 :             m_indiP_imageValid["flag"] = "no";
     515              :         }
     516              :         else
     517              :         {
     518            0 :             m_indiP_imageValid["flag"] = "yes";
     519              :         }
     520              : 
     521            0 :         if( registerIndiPropertyReadOnly( m_indiP_imageValid ) < 0 )
     522              :         {
     523            0 :             log<software_error>( { __FILE__, __LINE__ } );
     524            0 :             return -1;
     525              :         }
     526              : 
     527            0 :         m_fileSaveDir = m_calibDir + "/" + m_configName;
     528              : 
     529              :         // Create save directory.
     530            0 :         errno = 0;
     531            0 :         if( mkdir( m_fileSaveDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) < 0 )
     532              :         {
     533            0 :             if( errno != EEXIST )
     534              :             {
     535            0 :                 std::stringstream logss;
     536            0 :                 logss << "Failed to create image directory (" << m_fileSaveDir
     537            0 :                       << ").  Errno says: " << strerror( errno );
     538            0 :                 log<software_critical>( { __FILE__, __LINE__, errno, 0, logss.str() } );
     539              : 
     540            0 :                 return -1;
     541            0 :             }
     542              :         }
     543              :     }
     544              : 
     545            0 :     if( sem_init( &m_smSemaphore, 0, 0 ) < 0 )
     546              :     {
     547            0 :         log<software_critical>( { __FILE__, __LINE__, errno, 0, "Initializing S.M. semaphore" } );
     548            0 :         return -1;
     549              :     }
     550              : 
     551            0 :     if( shmimMonitorT::appStartup() < 0 )
     552              :     {
     553            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
     554              :     }
     555              : 
     556            0 :     if( darkMonitorT::appStartup() < 0 )
     557              :     {
     558            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
     559              :     }
     560              : 
     561            0 :     if( dark2MonitorT::appStartup() < 0 )
     562              :     {
     563            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
     564              :     }
     565              : 
     566            0 :     if( frameGrabberT::appStartup() < 0 )
     567              :     {
     568            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
     569              :     }
     570              : 
     571            0 :     if( telemeterT::appStartup() < 0 )
     572              :     {
     573            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
     574              :     }
     575              : 
     576            0 :     state( stateCodes::READY );
     577              : 
     578            0 :     return 0;
     579              : }
     580              : 
     581            0 : inline int shmimIntegrator::appLogic()
     582              : {
     583            0 :     if( shmimMonitorT::appLogic() < 0 )
     584              :     {
     585            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
     586              :     }
     587              : 
     588            0 :     if( darkMonitorT::appLogic() < 0 )
     589              :     {
     590            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
     591              :     }
     592              : 
     593            0 :     if( dark2MonitorT::appLogic() < 0 )
     594              :     {
     595            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
     596              :     }
     597              : 
     598            0 :     if( frameGrabberT::appLogic() < 0 )
     599              :     {
     600            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
     601              :     }
     602              : 
     603            0 :     if( telemeterT::appLogic() < 0 )
     604              :     {
     605            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
     606              :     }
     607              : 
     608            0 :     std::unique_lock<std::mutex> lock( m_indiMutex );
     609              : 
     610            0 :     if( shmimMonitorT::updateINDI() < 0 )
     611              :     {
     612            0 :         log<software_error>( { __FILE__, __LINE__ } );
     613              :     }
     614              : 
     615            0 :     if( darkMonitorT::updateINDI() < 0 )
     616              :     {
     617            0 :         log<software_error>( { __FILE__, __LINE__ } );
     618              :     }
     619              : 
     620            0 :     if( dark2MonitorT::updateINDI() < 0 )
     621              :     {
     622            0 :         log<software_error>( { __FILE__, __LINE__ } );
     623              :     }
     624              : 
     625            0 :     if( frameGrabberT::updateINDI() < 0 )
     626              :     {
     627            0 :         log<software_error>( { __FILE__, __LINE__ } );
     628              :     }
     629              : 
     630            0 :     if( m_running == false )
     631              :     {
     632            0 :         state( stateCodes::READY );
     633            0 :         updateSwitchIfChanged( m_indiP_startAveraging, "toggle", pcf::IndiElement::Off, INDI_IDLE );
     634              : 
     635            0 :         if( m_fileSaver )
     636              :         {
     637            0 :             if( m_stateStringChanged ) // So if not running and the state has changed, we check
     638              :             {
     639            0 :                 if( findMatchingDark() < 0 )
     640              :                 {
     641            0 :                     log<software_error>( { __FILE__, __LINE__ } );
     642              :                 }
     643              :             }
     644              : 
     645            0 :             if( !m_imageValid )
     646              :             {
     647            0 :                 updateIfChanged( m_indiP_imageValid, "flag", "no" );
     648              :             }
     649              :             else
     650              :             {
     651            0 :                 updateIfChanged( m_indiP_imageValid, "flag", "yes" );
     652              :             }
     653              :         }
     654              :     }
     655              :     else
     656              :     {
     657            0 :         state( stateCodes::OPERATING );
     658            0 :         updateSwitchIfChanged( m_indiP_startAveraging, "toggle", pcf::IndiElement::On, INDI_BUSY );
     659              :     }
     660              : 
     661            0 :     updateIfChanged( m_indiP_nAverage, "current", m_nAverage, INDI_IDLE );
     662            0 :     updateIfChanged( m_indiP_nAverage, "target", m_nAverage, INDI_IDLE );
     663              : 
     664            0 :     updateIfChanged( m_indiP_avgTime, "current", m_avgTime, INDI_IDLE );
     665            0 :     updateIfChanged( m_indiP_avgTime, "target", m_avgTime, INDI_IDLE );
     666              : 
     667            0 :     updateIfChanged( m_indiP_nUpdate, "current", m_nUpdate, INDI_IDLE );
     668            0 :     updateIfChanged( m_indiP_nUpdate, "target", m_nUpdate, INDI_IDLE );
     669              : 
     670            0 :     updateIfChanged( m_indiP_fps, "current", fps(), INDI_OK );
     671              : 
     672            0 :     return 0;
     673            0 : }
     674              : 
     675            0 : inline int shmimIntegrator::appShutdown()
     676              : {
     677            0 :     shmimMonitorT::appShutdown();
     678              : 
     679            0 :     darkMonitorT::appShutdown();
     680              : 
     681            0 :     dark2MonitorT::appShutdown();
     682              : 
     683            0 :     frameGrabberT::appShutdown();
     684              : 
     685            0 :     telemeterT::appShutdown();
     686              : 
     687            0 :     return 0;
     688              : }
     689              : 
     690            0 : inline int shmimIntegrator::allocate( const dev::shmimT &dummy )
     691              : {
     692              :     static_cast<void>( dummy ); // be unused
     693              : 
     694              :     // This whole thing could invalidate the dark.
     695            0 :     std::unique_lock<std::mutex> lock( m_darkMutex ); // Lock the mutex before messing with the dark.
     696              : 
     697            0 :     if( m_avgTime > 0 && m_fps > 0 )
     698              :     {
     699            0 :         m_nAverage = m_avgTime * m_fps;
     700            0 :         if( m_nAverage <= 0 )
     701              :         {
     702            0 :             m_nAverage = 1;
     703              :         }
     704            0 :         log<text_log>( "set nAverage to " + std::to_string( m_nAverage ) + " based on FPS", logPrio::LOG_NOTICE );
     705              :     }
     706            0 :     else if( m_avgTime > 0 && m_fps == 0 ) // Haven't gotten the update yet so we keep going for now
     707              :     {
     708            0 :         if( m_nAverage != m_nAverageDefault )
     709              :         {
     710            0 :             m_nAverage = m_nAverageDefault;
     711            0 :             log<text_log>( "set nAverage to default " + std::to_string( m_nAverage ), logPrio::LOG_NOTICE );
     712              :         }
     713              :     }
     714              : 
     715            0 :     if( m_nUpdate > 0 )
     716              :     {
     717            0 :         m_accumImages.resize( shmimMonitorT::m_width, shmimMonitorT::m_height, m_nAverage );
     718            0 :         m_accumImages.setZero();
     719              :     }
     720              :     else
     721              :     {
     722            0 :         m_accumImages.resize( 1, 1, 1 );
     723              :     }
     724              : 
     725            0 :     m_nprocessed  = 0;
     726            0 :     m_currImage   = 0;
     727            0 :     m_sinceUpdate = 0;
     728              : 
     729            0 :     m_avgImage.resize( shmimMonitorT::m_width, shmimMonitorT::m_height );
     730              :     // m_avgImage.setZero();
     731              : 
     732            0 :     pixget = getPixPointer<realT>( shmimMonitorT::m_dataType );
     733              : 
     734            0 :     if( pixget == nullptr )
     735              :     {
     736            0 :         log<software_error>( { __FILE__, __LINE__, "bad data type" } );
     737            0 :         return -1;
     738              :     }
     739              : 
     740            0 :     updateIfChanged( m_indiP_nAverage, "current", m_nAverage, INDI_IDLE );
     741            0 :     updateIfChanged( m_indiP_nAverage, "target", m_nAverage, INDI_IDLE );
     742              : 
     743            0 :     updateIfChanged( m_indiP_avgTime, "current", m_avgTime, INDI_IDLE );
     744            0 :     updateIfChanged( m_indiP_avgTime, "target", m_avgTime, INDI_IDLE );
     745              : 
     746            0 :     updateIfChanged( m_indiP_nUpdate, "current", m_nUpdate, INDI_IDLE );
     747            0 :     updateIfChanged( m_indiP_nUpdate, "target", m_nUpdate, INDI_IDLE );
     748              : 
     749            0 :     if( darkMonitorT::m_width == shmimMonitorT::m_width || darkMonitorT::m_height == shmimMonitorT::m_height )
     750              :     {
     751            0 :         m_darkValid = true;
     752              :     }
     753              :     else
     754              :     {
     755            0 :         m_darkValid = false;
     756              :     }
     757              : 
     758            0 :     if( dark2MonitorT::m_width == shmimMonitorT::m_width || dark2MonitorT::m_height == shmimMonitorT::m_height )
     759              :     {
     760            0 :         m_dark2Valid = true;
     761              :     }
     762              :     else
     763              :     {
     764            0 :         m_dark2Valid = false;
     765              :     }
     766              : 
     767            0 :     m_reconfig = true;
     768              : 
     769            0 :     return 0;
     770            0 : }
     771              : 
     772            0 : inline int shmimIntegrator::processImage( void *curr_src, const dev::shmimT &dummy )
     773              : {
     774              :     static_cast<void>( dummy ); // be unused
     775              : 
     776            0 :     if( !m_running )
     777            0 :         return 0;
     778              : 
     779            0 :     if( m_nUpdate == 0 )
     780              :     {
     781            0 :         if( m_updated )
     782            0 :             return 0;
     783            0 :         if( m_sinceUpdate == 0 )
     784            0 :             m_avgImage.setZero();
     785              : 
     786            0 :         realT *data = m_avgImage.data();
     787              : 
     788            0 :         for( unsigned nn = 0; nn < shmimMonitorT::m_width * shmimMonitorT::m_height; ++nn )
     789              :         {
     790            0 :             data[nn] += pixget( curr_src, nn );
     791              :         }
     792            0 :         ++m_sinceUpdate;
     793            0 :         if( m_sinceUpdate >= m_nAverage )
     794              :         {
     795            0 :             m_avgImage /= m_nAverage; ///\todo should this be /= m_sinceUpdate?
     796              : 
     797            0 :             if( ( m_darkSet && m_darkValid ) && !( m_dark2Set && m_dark2Valid ) )
     798              :             {
     799            0 :                 std::unique_lock<std::mutex> lock( m_darkMutex ); // Lock the mutex before messing with the dark.
     800            0 :                 m_avgImage -= m_darkImage;
     801            0 :             }
     802            0 :             else if( !( m_darkSet && m_darkValid ) && ( m_dark2Set && m_dark2Valid ) )
     803              :             {
     804            0 :                 std::unique_lock<std::mutex> lock( m_darkMutex ); // Lock the mutex before messing with the dark.
     805            0 :                 m_avgImage -= m_dark2Image;
     806            0 :             }
     807            0 :             else if( ( m_darkSet && m_darkValid ) && ( m_dark2Set && m_dark2Valid ) )
     808              :             {
     809            0 :                 std::unique_lock<std::mutex> lock( m_darkMutex ); // Lock the mutex before messing with the dark.
     810            0 :                 m_avgImage -= m_darkImage + m_dark2Image;
     811            0 :             }
     812              : 
     813            0 :             m_updated = true;
     814              : 
     815              :             // Now tell the f.g. to get going
     816            0 :             if( sem_post( &m_smSemaphore ) < 0 )
     817              :             {
     818            0 :                 log<software_critical>( { __FILE__, __LINE__, errno, 0, "Error posting to semaphore" } );
     819            0 :                 return -1;
     820              :             }
     821              : 
     822            0 :             m_sinceUpdate = 0;
     823            0 :             if( !m_continuous )
     824              :             {
     825            0 :                 m_running = false;
     826            0 :                 if( m_fileSaver )
     827              :                 {
     828            0 :                     if( m_stateStringChanged || !m_stateStringValid || !m_stateStringValidOnStart )
     829              :                     {
     830            0 :                         m_imageValid = false;
     831            0 :                         log<text_log>( "state changed during acquisition, not saving", logPrio::LOG_NOTICE );
     832              :                     }
     833              :                     else
     834              :                     {
     835            0 :                         m_imageValid         = true;
     836            0 :                         m_stateStringChanged = false;
     837              : 
     838              :                         ///\todo this should happen in a different less-real-time thread.
     839              :                         // Otherwise we save:
     840              :                         timespec fts;
     841            0 :                         clock_gettime( CLOCK_REALTIME, &fts );
     842              : 
     843              :                         tm uttime; // The broken down time.
     844              : 
     845            0 :                         if( gmtime_r( &fts.tv_sec, &uttime ) == 0 )
     846              :                         {
     847              :                             // Yell at operator but keep going
     848            0 :                             log<software_alert>( { __FILE__,
     849              :                                                    __LINE__,
     850            0 :                                                    errno,
     851              :                                                    0,
     852              :                                                    "gmtime_r error.  possible loss of timing information." } );
     853              :                         }
     854              : 
     855            0 :                         char cts[] = "YYYYMMDDHHMMSSNNNNNNNNN";
     856            0 :                         int  rv    = snprintf( cts,
     857              :                                            sizeof( cts ),
     858              :                                            "%04i%02i%02i%02i%02i%02i%09i",
     859            0 :                                            uttime.tm_year + 1900,
     860            0 :                                            uttime.tm_mon + 1,
     861              :                                            uttime.tm_mday,
     862              :                                            uttime.tm_hour,
     863              :                                            uttime.tm_min,
     864              :                                            uttime.tm_sec,
     865            0 :                                            static_cast<int>( fts.tv_nsec ) );
     866              : 
     867            0 :                         if( rv != sizeof( cts ) - 1 )
     868              :                         {
     869              :                             // Something is very wrong.  Keep going to try to get it on disk.
     870            0 :                             log<software_alert>(
     871            0 :                                 { __FILE__, __LINE__, errno, rv, "did not write enough chars to timestamp" } );
     872              :                         }
     873              : 
     874              :                         std::string fname =
     875            0 :                             m_fileSaveDir + "/" + m_configName + "_" + m_stateString + "__T" + cts + ".fits";
     876              : 
     877            0 :                         mx::fits::fitsFile<float> ff;
     878            0 :                         ff.write( fname, m_avgImage );
     879            0 :                         log<text_log>( "Wrote " + fname );
     880            0 :                     }
     881              :                 }
     882              :             }
     883              :         }
     884              :     }
     885              :     else
     886              :     {
     887            0 :         realT *data = m_accumImages.image( m_currImage ).data();
     888              : 
     889            0 :         for( unsigned nn = 0; nn < shmimMonitorT::m_width * shmimMonitorT::m_height; ++nn )
     890              :         {
     891            0 :             data[nn] = pixget( curr_src, nn );
     892              :         }
     893            0 :         ++m_nprocessed;
     894            0 :         ++m_currImage;
     895            0 :         if( m_currImage >= m_nAverage )
     896            0 :             m_currImage = 0;
     897              : 
     898            0 :         if( m_nprocessed < m_nAverage ) // Check that we are burned in on first pass through cube
     899              :         {
     900            0 :             return 0;
     901              :         }
     902              : 
     903            0 :         ++m_sinceUpdate;
     904              : 
     905            0 :         if( m_sinceUpdate >= m_nUpdate )
     906              :         {
     907            0 :             if( m_updated )
     908              :             {
     909            0 :                 return 0; // In case f.g. thread is behind, we skip and come back.
     910              :             }
     911              :             // Don't use eigenCube functions to avoid any omp
     912            0 :             m_avgImage.setZero();
     913            0 :             for( size_t n = 0; n < m_nAverage; ++n )
     914              :             {
     915            0 :                 for( size_t ii = 0; ii < shmimMonitorT::m_width; ++ii )
     916              :                 {
     917            0 :                     for( size_t jj = 0; jj < shmimMonitorT::m_height; ++jj )
     918              :                     {
     919            0 :                         m_avgImage( ii, jj ) += m_accumImages.image( n )( ii, jj );
     920              :                     }
     921              :                 }
     922              :             }
     923            0 :             m_avgImage /= m_nAverage;
     924              : 
     925            0 :             if( m_darkValid && m_darkSet )
     926              :             {
     927            0 :                 std::unique_lock<std::mutex> lock( m_darkMutex ); // Lock the mutex before messing with the dark.
     928            0 :                 m_avgImage -= m_darkImage;
     929            0 :             }
     930              : 
     931            0 :             m_updated = true;
     932              : 
     933              :             // Now tell the f.g. to get going
     934            0 :             if( sem_post( &m_smSemaphore ) < 0 )
     935              :             {
     936            0 :                 log<software_critical>( { __FILE__, __LINE__, errno, 0, "Error posting to semaphore" } );
     937            0 :                 return -1;
     938              :             }
     939              : 
     940            0 :             m_sinceUpdate = 0;
     941              :         }
     942              :     }
     943            0 :     return 0;
     944              : }
     945              : 
     946            0 : inline int shmimIntegrator::allocate( const darkShmimT &dummy )
     947              : {
     948              :     static_cast<void>( dummy ); // be unused
     949              : 
     950            0 :     std::unique_lock<std::mutex> lock( m_darkMutex ); // Lock the mutex before messing with the dark.
     951              : 
     952            0 :     m_darkImage.resize( darkMonitorT::m_width, darkMonitorT::m_height );
     953            0 :     m_darkImage.setZero();
     954              : 
     955            0 :     dark_pixget = getPixPointer<realT>( darkMonitorT::m_dataType );
     956              : 
     957            0 :     if( dark_pixget == nullptr )
     958              :     {
     959            0 :         log<software_error>( { __FILE__, __LINE__, "bad data type" } );
     960            0 :         m_darkSet   = false;
     961            0 :         m_darkValid = false;
     962            0 :         return -1;
     963              :     }
     964              : 
     965            0 :     if( darkMonitorT::m_width == shmimMonitorT::m_width || darkMonitorT::m_height == shmimMonitorT::m_height )
     966              :     {
     967            0 :         m_darkValid = true;
     968              :     }
     969              :     else
     970              :     {
     971            0 :         m_darkValid = false;
     972              :     }
     973              : 
     974            0 :     return 0;
     975            0 : }
     976              : 
     977            0 : inline int shmimIntegrator::processImage( void *curr_src, const darkShmimT &dummy )
     978              : {
     979              :     static_cast<void>( dummy ); // be unused
     980              : 
     981            0 :     realT *data = m_darkImage.data();
     982              : 
     983            0 :     for( unsigned nn = 0; nn < darkMonitorT::m_width * darkMonitorT::m_height; ++nn )
     984              :     {
     985              :         // data[nn] = *( (int16_t * ) (curr_src + nn*shmimMonitorT::m_typeSize));
     986            0 :         data[nn] = dark_pixget( curr_src, nn );
     987              :     }
     988              : 
     989            0 :     m_darkSet = true; // There is a dark set and ready to use, but it may or may not be valid.
     990              : 
     991            0 :     return 0;
     992              : }
     993              : 
     994            0 : inline int shmimIntegrator::allocate( const dark2ShmimT &dummy )
     995              : {
     996              :     static_cast<void>( dummy ); // be unused
     997              : 
     998            0 :     std::unique_lock<std::mutex> lock( m_darkMutex ); // Lock the mutex before messing with the dark.
     999              : 
    1000            0 :     m_dark2Image.resize( dark2MonitorT::m_width, dark2MonitorT::m_height );
    1001            0 :     m_dark2Image.setZero();
    1002              : 
    1003            0 :     dark2_pixget = getPixPointer<realT>( dark2MonitorT::m_dataType );
    1004              : 
    1005            0 :     if( dark2_pixget == nullptr )
    1006              :     {
    1007            0 :         log<software_error>( { __FILE__, __LINE__, "bad data type" } );
    1008            0 :         m_dark2Set   = false;
    1009            0 :         m_dark2Valid = false;
    1010            0 :         return -1;
    1011              :     }
    1012              : 
    1013            0 :     if( dark2MonitorT::m_width == shmimMonitorT::m_width || dark2MonitorT::m_height == shmimMonitorT::m_height )
    1014              :     {
    1015            0 :         m_dark2Valid = true;
    1016              :     }
    1017              :     else
    1018              :     {
    1019            0 :         m_dark2Valid = false;
    1020              :     }
    1021              : 
    1022            0 :     return 0;
    1023            0 : }
    1024              : 
    1025            0 : inline int shmimIntegrator::processImage( void *curr_src, const dark2ShmimT &dummy )
    1026              : {
    1027              :     static_cast<void>( dummy ); // be unused
    1028              : 
    1029            0 :     realT *data = m_dark2Image.data();
    1030              : 
    1031            0 :     for( unsigned nn = 0; nn < dark2MonitorT::m_width * dark2MonitorT::m_height; ++nn )
    1032              :     {
    1033              :         // data[nn] = *( (int16_t * ) (curr_src + nn*shmimMonitorT::m_typeSize));
    1034            0 :         data[nn] = dark2_pixget( curr_src, nn );
    1035              :     }
    1036              : 
    1037            0 :     m_dark2Set = true; // There is a dark set and ready to use, but it may or may not be valid.
    1038              : 
    1039            0 :     return 0;
    1040              : }
    1041              : 
    1042            0 : inline int shmimIntegrator::findMatchingDark()
    1043              : {
    1044            0 :     std::vector<std::string> fnames;
    1045              : 
    1046            0 :     mx::error_t errc = mx::ioutils::getFileNames(fnames, m_fileSaveDir, m_configName, "", ".fits" );
    1047              : 
    1048            0 :     if(errc != mx::error_t::noerror)
    1049              :     {
    1050            0 :         return log<text_log,-1>(std::format("Could not get list of darks: {} "
    1051            0 :          "({})", mx::errorMessage(errc), mx::errorName(errc)), logPrio::LOG_ERROR);
    1052              :     }
    1053              : 
    1054              :     // getFileNames sorts, so these will be in oldest to newest order by lexical timestamp sort
    1055              :     // So we search in reverse to always pick newest
    1056            0 :     long N = fnames.size();
    1057            0 :     for( long n = N - 1; n >= 0; --n )
    1058              :     {
    1059            0 :         std::string fn = mx::ioutils::pathStem( fnames[n] );
    1060              : 
    1061            0 :         if( fn.size() < m_configName.size() + 1 )
    1062            0 :             continue;
    1063              : 
    1064            0 :         size_t st = m_configName.size() + 1;
    1065            0 :         size_t ed = fn.find( "__T" );
    1066            0 :         if( ed == std::string::npos || ed - st < 2 )
    1067            0 :             continue;
    1068            0 :         std::string stateStr = fn.substr( st, ed - st );
    1069              : 
    1070            0 :         if( stateStr == m_stateString )
    1071              :         {
    1072            0 :             mx::fits::fitsFile<float> ff;
    1073            0 :             ff.read( m_avgImage, fnames[n] );
    1074              : 
    1075            0 :             if( m_avgImage.rows() != shmimMonitorT::m_width || m_avgImage.cols() != shmimMonitorT::m_height )
    1076              :             {
    1077              :                 // Means the camera has changed but stream hasn't caught up
    1078              :                 //(This happens on startup before stream connection completes.)
    1079              : 
    1080              :                 // And possibly that we haven't turned the shmimMonitor on yet by switching to OPERATING
    1081            0 :                 if( shmimMonitorT::m_width == 0 && shmimMonitorT::m_height == 0 )
    1082              :                 {
    1083            0 :                     sleep( 1 ); // wait for everything else to get initialized
    1084            0 :                     shmimMonitorT::m_width  = m_avgImage.rows();
    1085            0 :                     shmimMonitorT::m_height = m_avgImage.cols();
    1086            0 :                     m_reconfig              = true;
    1087              :                 }
    1088              :                 else
    1089              :                 {
    1090            0 :                     if( m_running )
    1091            0 :                         return 0;
    1092            0 :                     m_imageValid         = false;
    1093            0 :                     m_stateStringChanged = true; // So we let appLogic try again next time around.
    1094            0 :                     return 0;
    1095              :                 }
    1096              :             }
    1097              : 
    1098            0 :             if( m_running )
    1099            0 :                 return 0;
    1100              : 
    1101            0 :             m_updated = true;
    1102              :             // Now tell the f.g. to get going
    1103            0 :             if( sem_post( &m_smSemaphore ) < 0 )
    1104              :             {
    1105            0 :                 log<software_critical>( { __FILE__, __LINE__, errno, 0, "Error posting to semaphore" } );
    1106            0 :                 return -1;
    1107              :             }
    1108            0 :             m_imageValid         = true;
    1109            0 :             m_stateStringChanged = false;
    1110              : 
    1111            0 :             log<text_log>( "loaded last matching dark from disk", logPrio::LOG_NOTICE );
    1112              : 
    1113            0 :             return 0;
    1114            0 :         }
    1115            0 :     }
    1116              : 
    1117            0 :     if( m_running )
    1118            0 :         return 0;
    1119              : 
    1120            0 :     m_imageValid         = false;
    1121            0 :     m_stateStringChanged = false; // stop trying b/c who else is going to add a dark?
    1122              : 
    1123            0 :     log<text_log>( "dark is not valid", logPrio::LOG_WARNING );
    1124              : 
    1125            0 :     return 0;
    1126            0 : }
    1127              : 
    1128            0 : inline int shmimIntegrator::configureAcquisition()
    1129              : {
    1130            0 :     std::unique_lock<std::mutex> lock( m_indiMutex );
    1131              : 
    1132              :     ///\todo potential but verrrrry unlikely bug: shmimMonitorT could change these before allocate sets the lock above.
    1133              :     ///Should use a local set of w/h instead.
    1134            0 :     if( shmimMonitorT::m_width == 0 || shmimMonitorT::m_height == 0 )
    1135              :     {
    1136              :         // This means we haven't connected to the stream to average. so wait.
    1137            0 :         lock.unlock(); // don't hold the lock for a whole second.
    1138            0 :         sleep( 1 );
    1139            0 :         return -1;
    1140              :     }
    1141              : 
    1142            0 :     frameGrabberT::m_width    = shmimMonitorT::m_width;
    1143            0 :     frameGrabberT::m_height   = shmimMonitorT::m_height;
    1144            0 :     frameGrabberT::m_dataType = _DATATYPE_FLOAT;
    1145              : 
    1146            0 :     return 0;
    1147            0 : }
    1148              : 
    1149            0 : inline int shmimIntegrator::startAcquisition()
    1150              : {
    1151            0 :     return 0;
    1152              : }
    1153              : 
    1154            0 : inline int shmimIntegrator::acquireAndCheckValid()
    1155              : {
    1156              :     timespec ts;
    1157              : 
    1158            0 :     if( clock_gettime( CLOCK_REALTIME, &ts ) < 0 )
    1159              :     {
    1160            0 :         log<software_critical>( { __FILE__, __LINE__, errno, 0, "clock_gettime" } );
    1161            0 :         return -1;
    1162              :     }
    1163              : 
    1164            0 :     ts.tv_sec += 1;
    1165              : 
    1166            0 :     if( sem_timedwait( &m_smSemaphore, &ts ) == 0 )
    1167              :     {
    1168            0 :         if( m_updated )
    1169              :         {
    1170            0 :             clock_gettime( CLOCK_REALTIME, &m_currImageTimestamp );
    1171            0 :             return 0;
    1172              :         }
    1173              :         else
    1174              :         {
    1175            0 :             return 1;
    1176              :         }
    1177              :     }
    1178              :     else
    1179              :     {
    1180            0 :         return 1;
    1181              :     }
    1182              : }
    1183              : 
    1184            0 : inline int shmimIntegrator::loadImageIntoStream( void *dest )
    1185              : {
    1186            0 :     memcpy( dest, m_avgImage.data(), shmimMonitorT::m_width * shmimMonitorT::m_height * frameGrabberT::m_typeSize );
    1187            0 :     m_updated = false;
    1188            0 :     return 0;
    1189              : }
    1190              : 
    1191            0 : inline int shmimIntegrator::reconfig()
    1192              : {
    1193            0 :     return 0;
    1194              : }
    1195              : 
    1196            0 : INDI_NEWCALLBACK_DEFN( shmimIntegrator, m_indiP_nAverage )( const pcf::IndiProperty &ipRecv )
    1197              : {
    1198            0 :     if( ipRecv.getName() != m_indiP_nAverage.getName() )
    1199              :     {
    1200            0 :         log<software_error>( { __FILE__, __LINE__, "invalid indi property received" } );
    1201            0 :         return -1;
    1202              :     }
    1203              : 
    1204              :     unsigned target;
    1205              : 
    1206            0 :     if( indiTargetUpdate( m_indiP_nAverage, target, ipRecv, true ) < 0 )
    1207              :     {
    1208            0 :         log<software_error>( { __FILE__, __LINE__ } );
    1209            0 :         return -1;
    1210              :     }
    1211              : 
    1212            0 :     m_nAverage = target;
    1213              : 
    1214            0 :     if( m_avgTime > 0 && m_fps > 0 )
    1215              :     {
    1216            0 :         m_avgTime = m_nAverage / m_fps;
    1217              :     }
    1218              : 
    1219            0 :     updateIfChanged( m_indiP_nAverage, "current", m_nAverage, INDI_IDLE );
    1220            0 :     updateIfChanged( m_indiP_nAverage, "target", m_nAverage, INDI_IDLE );
    1221              : 
    1222            0 :     updateIfChanged( m_indiP_avgTime, "current", m_avgTime, INDI_IDLE );
    1223            0 :     updateIfChanged( m_indiP_avgTime, "target", m_avgTime, INDI_IDLE );
    1224              : 
    1225            0 :     shmimMonitorT::m_restart = true;
    1226              : 
    1227            0 :     log<text_log>( "set nAverage to " + std::to_string( m_nAverage ), logPrio::LOG_NOTICE );
    1228              : 
    1229            0 :     return 0;
    1230              : }
    1231              : 
    1232            0 : INDI_NEWCALLBACK_DEFN( shmimIntegrator, m_indiP_avgTime )( const pcf::IndiProperty &ipRecv )
    1233              : {
    1234            0 :     if( ipRecv.getName() != m_indiP_avgTime.getName() )
    1235              :     {
    1236            0 :         log<software_error>( { __FILE__, __LINE__, "invalid indi property received" } );
    1237            0 :         return -1;
    1238              :     }
    1239              : 
    1240              :     float target;
    1241              : 
    1242            0 :     if( indiTargetUpdate( m_indiP_avgTime, target, ipRecv, true ) < 0 )
    1243              :     {
    1244            0 :         log<software_error>( { __FILE__, __LINE__ } );
    1245            0 :         return -1;
    1246              :     }
    1247              : 
    1248            0 :     m_avgTime = target;
    1249              : 
    1250            0 :     updateIfChanged( m_indiP_avgTime, "current", m_avgTime, INDI_IDLE );
    1251            0 :     updateIfChanged( m_indiP_avgTime, "target", m_avgTime, INDI_IDLE );
    1252              : 
    1253            0 :     shmimMonitorT::m_restart = true;
    1254              : 
    1255            0 :     log<text_log>( "set avgTime to " + std::to_string( m_avgTime ), logPrio::LOG_NOTICE );
    1256              : 
    1257            0 :     return 0;
    1258              : }
    1259              : 
    1260            0 : INDI_NEWCALLBACK_DEFN( shmimIntegrator, m_indiP_nUpdate )( const pcf::IndiProperty &ipRecv )
    1261              : {
    1262            0 :     if( ipRecv.getName() != m_indiP_nUpdate.getName() )
    1263              :     {
    1264            0 :         log<software_error>( { __FILE__, __LINE__, "invalid indi property received" } );
    1265            0 :         return -1;
    1266              :     }
    1267              : 
    1268              :     unsigned target;
    1269              : 
    1270            0 :     if( indiTargetUpdate( m_indiP_nUpdate, target, ipRecv, true ) < 0 )
    1271              :     {
    1272            0 :         log<software_error>( { __FILE__, __LINE__ } );
    1273            0 :         return -1;
    1274              :     }
    1275              : 
    1276            0 :     m_nUpdate = target;
    1277              : 
    1278            0 :     updateIfChanged( m_indiP_nUpdate, "current", m_nUpdate, INDI_IDLE );
    1279            0 :     updateIfChanged( m_indiP_nUpdate, "target", m_nUpdate, INDI_IDLE );
    1280              : 
    1281            0 :     shmimMonitorT::m_restart = true;
    1282              : 
    1283            0 :     log<text_log>( "set nUpdate to " + std::to_string( m_nUpdate ), logPrio::LOG_NOTICE );
    1284              : 
    1285            0 :     return 0;
    1286              : }
    1287              : 
    1288            0 : INDI_NEWCALLBACK_DEFN( shmimIntegrator, m_indiP_startAveraging )( const pcf::IndiProperty &ipRecv )
    1289              : {
    1290            0 :     if( ipRecv.getName() != m_indiP_startAveraging.getName() )
    1291              :     {
    1292            0 :         log<software_error>( { __FILE__, __LINE__, "invalid indi property received" } );
    1293            0 :         return -1;
    1294              :     }
    1295              : 
    1296            0 :     if( !ipRecv.find( "toggle" ) )
    1297            0 :         return 0;
    1298              : 
    1299            0 :     if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off )
    1300              :     {
    1301            0 :         std::unique_lock<std::mutex> lock( m_indiMutex );
    1302              : 
    1303            0 :         m_running = false;
    1304              : 
    1305            0 :         state( stateCodes::READY );
    1306              : 
    1307            0 :         updateSwitchIfChanged( m_indiP_startAveraging, "toggle", pcf::IndiElement::Off, INDI_IDLE );
    1308            0 :     }
    1309              : 
    1310            0 :     if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
    1311              :     {
    1312            0 :         std::unique_lock<std::mutex> lock( m_indiMutex );
    1313              : 
    1314            0 :         if( m_fileSaver && !m_continuous )
    1315            0 :             m_stateStringChanged = false; // We reset this here so we can detect a change at the end of the integration
    1316              : 
    1317            0 :         m_stateStringValidOnStart = m_stateStringValid;
    1318            0 :         m_running                 = true;
    1319              : 
    1320            0 :         state( stateCodes::OPERATING );
    1321              : 
    1322            0 :         updateSwitchIfChanged( m_indiP_startAveraging, "toggle", pcf::IndiElement::On, INDI_BUSY );
    1323            0 :     }
    1324            0 :     return 0;
    1325              : }
    1326              : 
    1327            0 : INDI_SETCALLBACK_DEFN( shmimIntegrator, m_indiP_fpsSource )( const pcf::IndiProperty &ipRecv )
    1328              : {
    1329            0 :     if( ipRecv.getName() != m_indiP_fpsSource.getName() )
    1330              :     {
    1331            0 :         log<software_error>( { __FILE__, __LINE__, "Invalid INDI property." } );
    1332            0 :         return -1;
    1333              :     }
    1334              : 
    1335            0 :     if( ipRecv.find( "current" ) != true ) // this isn't valie
    1336              :     {
    1337            0 :         return 0;
    1338              :     }
    1339              : 
    1340            0 :     std::lock_guard<std::mutex> guard( m_indiMutex );
    1341              : 
    1342            0 :     realT fps = ipRecv["current"].get<float>();
    1343              : 
    1344            0 :     if( fps != m_fps )
    1345              :     {
    1346            0 :         m_fps = fps;
    1347            0 :         std::cout << "Got fps: " << m_fps << "\n";
    1348            0 :         shmimMonitorT::m_restart  = true;
    1349            0 :         frameGrabberT::m_reconfig = true;
    1350              :     }
    1351              : 
    1352            0 :     return 0;
    1353            0 : }
    1354              : 
    1355            0 : INDI_SETCALLBACK_DEFN( shmimIntegrator, m_indiP_stateSource )( const pcf::IndiProperty &ipRecv )
    1356              : {
    1357            0 :     if( ipRecv.getName() != m_indiP_stateSource.getName() )
    1358              :     {
    1359            0 :         log<software_error>( { __FILE__, __LINE__, "Invalid INDI property." } );
    1360            0 :         return -1;
    1361              :     }
    1362              : 
    1363            0 :     if( ipRecv.find( "valid" ) == true )
    1364              :     {
    1365              :         bool stateStringValid;
    1366            0 :         if( ipRecv["valid"].get<std::string>() == "yes" )
    1367            0 :             stateStringValid = true;
    1368              :         else
    1369            0 :             stateStringValid = false;
    1370              : 
    1371            0 :         if( stateStringValid != m_stateStringValid )
    1372            0 :             m_stateStringChanged = true;
    1373              : 
    1374            0 :         m_stateStringValid = stateStringValid;
    1375              :     }
    1376              : 
    1377            0 :     if( ipRecv.find( "current" ) != true )
    1378              :     {
    1379            0 :         return 0;
    1380              :     }
    1381              : 
    1382            0 :     std::lock_guard<std::mutex> guard( m_indiMutex );
    1383              : 
    1384            0 :     std::string ss = ipRecv["current"].get<std::string>();
    1385              : 
    1386            0 :     if( ss != m_stateString )
    1387              :     {
    1388            0 :         m_stateString = ss;
    1389            0 :         m_imageValid  = false; // This will mark the current dark invalid
    1390            0 :         updateIfChanged( m_indiP_imageValid, "flag", "no" );
    1391            0 :         m_stateStringChanged = true; // We declare it changed.  This can have two effects:
    1392              :                                      //  1) if we are not currently integrating, it will start a lookup in appLogic
    1393              :                                      //  2) if we are integrating, after it finishes it will not be declared valid and
    1394              :                                      //  then we'll lookup in appLogic
    1395              :     }
    1396              : 
    1397            0 :     return 0;
    1398            0 : }
    1399              : 
    1400            0 : inline int shmimIntegrator::checkRecordTimes()
    1401              : {
    1402            0 :     return telemeterT::checkRecordTimes( telem_fgtimings() );
    1403              : }
    1404              : 
    1405            0 : inline int shmimIntegrator::recordTelem( const telem_fgtimings * )
    1406              : {
    1407            0 :     return recordFGTimings( true );
    1408              : }
    1409              : 
    1410              : } // namespace app
    1411              : } // namespace MagAOX
    1412              : 
    1413              : #endif // shmimIntegrator_hpp
        

Generated by: LCOV version 2.0-1