LCOV - code coverage report
Current view: top level - libMagAOX/app/dev - frameGrabber.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 0.0 % 322 0
Test Date: 2026-01-03 21:03:39 Functions: 0.0 % 11 0

            Line data    Source code
       1              : /** \file frameGrabber.hpp
       2              :  * \brief The MagAO-X generic frame grabber.
       3              :  *
       4              :  * \author Jared R. Males (jaredmales@gmail.com)
       5              :  *
       6              :  * \ingroup app_files
       7              :  */
       8              : 
       9              : #ifndef frameGrabber_hpp
      10              : #define frameGrabber_hpp
      11              : 
      12              : #include <sys/syscall.h>
      13              : 
      14              : #include <mx/sigproc/circularBuffer.hpp>
      15              : #include <mx/math/vectorUtils.hpp>
      16              : #include <mx/improc/imageUtils.hpp>
      17              : 
      18              : #include "../../ImageStreamIO/ImageStruct.hpp"
      19              : #include <ImageStreamIO/ImageStreamIO.h>
      20              : 
      21              : #include "../../common/paths.hpp"
      22              : 
      23              : namespace MagAOX
      24              : {
      25              : namespace app
      26              : {
      27              : namespace dev
      28              : {
      29              : 
      30              : /** MagAO-X generic frame grabber
      31              :  *
      32              :  *
      33              :  * The derived class `derivedT` has the following requirements:
      34              :  *
      35              :  * - Must be derived from MagAOXApp<true>
      36              :  *
      37              :  * - Must be derived from telemeter<derivedT> (see below for required interface)
      38              :  *
      39              :  * - Must contain the following friend declaration:
      40              :  *   \code
      41              :  *       friend class dev::frameGrabber<derivedT>; //replace derivedT
      42              :  *   \endcode
      43              :  *
      44              :  * - Must declare the following typedef:
      45              :  *   \code
      46              :  *       typedef dev::frameGrabber<derivedT> frameGrabberT; //replace derivedT
      47              :  *   \endcode
      48              :  *
      49              :  * - must expose the following interface
      50              :  *   \code
      51              :  *       //Configures the camera for acquistion, must also set m_width, m_height, and m_dataType
      52              :  *       //so that the shared memory can be allocated
      53              :  *       int derivedT::configureAcquisition();
      54              :  *
      55              :  *       //Gets the frames-per-second readout rate
      56              :  *       //used for the latency statistics
      57              :  *       float derivedT::fps();
      58              :  *
      59              :  *       //Start acquisition.
      60              :  *       int derivedT::startAcquisition();
      61              :  *
      62              :  *       //Acquires the data, and checks if it is valid.
      63              :  *       //This should set m_currImageTimestamp to the image timestamp.
      64              :  *       // returns 0 if valid, < 0 on error, > 0 on no data.
      65              :  *       int derivedT::acquireAndCheckValid()
      66              :  *
      67              :  *       //Loads the acquired image into the stream, copying it to the appropriate member of m_imageStream->array.
      68              :  *       //This could simply be a memcpy.
      69              :  *       int derivedT::loadImageIntoStream(void * dest);
      70              :  *
      71              :  *       //Take any actions needed to reconfigure the system.  Called if m_reconfig is set to true.
      72              :  *       int derivedT::reconfig()
      73              :  *   \endcode
      74              :  *   Each of the above functions should return 0 on success, and -1 on an error (except fps).
      75              :  *   For `acquireAndCheckValid` >0 will indicate no data but not an error.  In most cases,
      76              :  *   an appropriate state code, such as NOTCONNECTED, should be set as well.
      77              :  *
      78              :  * - A static configuration variable must be defined in derivedT as
      79              :  *   \code
      80              :  *       static constexpr bool c_frameGrabber_flippable =true; //or: false
      81              :  *   \endcode
      82              :  *   which determines whether or not the images can be flipped programmatically.
      83              :  *
      84              :  * - Calls to this class's `setupConfig`, `loadConfig`, `appStartup`, `appLogic`, `updateINDI`, and `appShutdown`
      85              :  *   functions must be placed in the derived class's functions of the same name. For convenience the
      86              :  *   following macros are defined to provide error checking:
      87              :  *   \code
      88              :  *       FRAMEGRABBER_SETUP_CONFIG( cfig )
      89              :  *       FRAMEGRABBER_LOAD_CONFIG( cfig )
      90              :  *       FRAMEGRABBER_APP_STARTUP
      91              :  *       FRAMEGRABBER_APP_LOGIC
      92              :  *       FRAMEGRABBER_UPDATE_INDI
      93              :  *       FRAMEGRABBER_APP_SHUTDOWN
      94              :  *   \endcode
      95              :  *
      96              :  * \ingroup appdev
      97              :  */
      98              : template <class derivedT>
      99              : class frameGrabber
     100              : {
     101              :   public:
     102              :     enum fgFlip
     103              :     {
     104              :         fgFlipNone,
     105              :         fgFlipUD,
     106              :         fgFlipLR,
     107              :         fgFlipUDLR
     108              :     };
     109              : 
     110              :     typedef int32_t cbIndexT;
     111              : 
     112              :   protected:
     113              :     /** \name Configurable Parameters
     114              :      * @{
     115              :      */
     116              :     std::string m_shmimName{ "" }; ///< The name of the shared memory image, is used in `/tmp/<shmimName>.im.shm`.
     117              :                                    ///< Derived classes should set a default.
     118              : 
     119              :     int         m_fgThreadPrio{ 2 }; ///< Priority of the framegrabber thread, should normally be > 00.
     120              :     std::string m_fgCpuset; ///< The cpuset to assign the framegrabber thread to.  Not used if empty, the default.
     121              : 
     122              :     uint32_t m_circBuffLength{ 1 }; ///< Length of the circular buffer, in frames
     123              : 
     124              :     cbIndexT m_latencyCircBuffMaxLength{ 100000 }; ///< Maximum length of the latency measurement circular buffers
     125              : 
     126              :     float m_latencyCircBuffMaxTime{ 5 }; ///< Maximum time of the latency meaurement circular buffers
     127              : 
     128              :     int m_defaultFlip{ fgFlipNone };
     129              : 
     130              :     ///@}
     131              : 
     132              :     uint32_t m_width{ 0 };  ///< The width of the image, once deinterlaced etc.
     133              :     uint32_t m_height{ 0 }; ///< The height of the image, once deinterlaced etc.
     134              : 
     135              :     uint8_t m_dataType{ 0 }; ///< The ImageStreamIO type code.
     136              :     size_t  m_typeSize{ 0 }; ///< The size of the type, in bytes.  Result of sizeof.
     137              : 
     138              :     int m_xbinning{ 0 }; ///< The x-binning according to the framegrabber
     139              :     int m_ybinning{ 0 }; ///< The y-binning according to the framegrabber
     140              : 
     141              :     timespec m_currImageTimestamp{ 0, 0 }; ///< The timestamp of the current image.
     142              : 
     143              :     bool m_reconfig{ false }; ///< Flag to set if a camera reconfiguration requires a framegrabber reset.
     144              : 
     145              :     IMAGE *m_imageStream{ nullptr }; ///< The ImageStreamIO shared memory buffer.
     146              : 
     147              :     bool m_ownShmim{ true }; ///< Flag controlling if the shmim is owned.  If true it will be destroyed as needed.
     148              : 
     149              :     ino_t m_inode{ 0 }; ///< The inode of the image stream file
     150              : 
     151              :     float m_cbFPS{ 0 }; ///< The FPS used to configure the circular buffers
     152              : 
     153              :     mx::sigproc::circularBufferIndex<timespec, cbIndexT> m_atimes;
     154              :     mx::sigproc::circularBufferIndex<timespec, cbIndexT> m_wtimes;
     155              : 
     156              :     std::vector<double> m_atimesD;
     157              :     std::vector<double> m_wtimesD;
     158              :     std::vector<double> m_watimesD;
     159              : 
     160              :     timespec m_dummy_ts{ 0, 0 };
     161              :     uint64_t m_dummy_cnt{ 0 };
     162              :     char     m_dummy_c{ 0 };
     163              : 
     164              :     double m_mna;
     165              :     double m_vara;
     166              :     double m_mina;
     167              :     double m_maxa;
     168              : 
     169              :     double m_mnw;
     170              :     double m_varw;
     171              :     double m_minw;
     172              :     double m_maxw;
     173              : 
     174              :     double m_mnwa;
     175              :     double m_varwa;
     176              : 
     177              :   public:
     178              :     /// Setup the configuration system
     179              :     /**
     180              :       * This should be called in `derivedT::setupConfig` as
     181              :       * \code
     182              :         framegrabber<derivedT>::setupConfig(config);
     183              :         \endcode
     184              :       * with appropriate error checking.
     185              :       */
     186              :     int setupConfig( mx::app::appConfigurator &config /**< [out] the derived classes configurator*/ );
     187              : 
     188              :     /// load the configuration system results
     189              :     /**
     190              :       * This should be called in `derivedT::loadConfig` as
     191              :       * \code
     192              :         framegrabber<derivedT>::loadConfig(config);
     193              :         \endcode
     194              :       * with appropriate error checking.
     195              :       */
     196              :     int loadConfig( mx::app::appConfigurator &config /**< [in] the derived classes configurator*/ );
     197              : 
     198              :     /// Startup function
     199              :     /** Starts the framegrabber thread
     200              :       * This should be called in `derivedT::appStartup` as
     201              :       * \code
     202              :         framegrabber<derivedT>::appStartup();
     203              :         \endcode
     204              :       * with appropriate error checking.
     205              :       *
     206              :       * \returns 0 on success
     207              :       * \returns -1 on error, which is logged.
     208              :       */
     209              :     int appStartup();
     210              : 
     211              :     /// Checks the framegrabber thread
     212              :     /** This should be called in `derivedT::appLogic` as
     213              :       * \code
     214              :         framegrabber<derivedT>::appLogic();
     215              :         \endcode
     216              :       * with appropriate error checking.
     217              :       *
     218              :       * \returns 0 on success
     219              :       * \returns -1 on error, which is logged.
     220              :       */
     221              :     int appLogic();
     222              : 
     223              :     /// On power off, sets m_reconfig to true.
     224              :     /** This should be called in `derivedT::onPowerOff` as
     225              :       * \code
     226              :         framegrabber<derivedT>::onPowerOff();
     227              :         \endcode
     228              :       * with appropriate error checking.
     229              :       *
     230              :       * \returns 0 on success
     231              :       * \returns -1 on error, which is logged.
     232              :       */
     233              :     int onPowerOff();
     234              : 
     235              :     /// Shuts down the framegrabber thread
     236              :     /** This should be called in `derivedT::appShutdown` as
     237              :       * \code
     238              :         framegrabber<derivedT>::appShutdown();
     239              :         \endcode
     240              :       * with appropriate error checking.
     241              :       *
     242              :       * \returns 0 on success
     243              :       * \returns -1 on error, which is logged.
     244              :       */
     245              :     int appShutdown();
     246              : 
     247              :     int configCircBuffs();
     248              : 
     249              :   protected:
     250              :     /** \name Framegrabber Thread
     251              :      * This thread actually manages the framegrabbing hardware
     252              :      * @{
     253              :      */
     254              : 
     255              :     bool m_fgThreadInit{ true }; ///< Synchronizer for thread startup, to allow priority setting to finish.
     256              : 
     257              :     pid_t m_fgThreadID{ 0 }; ///< The ID of the framegrabber thread.
     258              : 
     259              :     pcf::IndiProperty m_fgThreadProp; ///< The property to hold the f.g. thread details.
     260              : 
     261              :     std::thread m_fgThread; ///< A separate thread for the actual framegrabbings
     262              : 
     263              :     /// Thread starter, called by MagAOXApp::threadStart on thread construction.  Calls fgThreadExec.
     264              :     static void fgThreadStart( frameGrabber *o /**< [in] a pointer to a frameGrabber instance (normally this) */ );
     265              : 
     266              :     /// Execute framegrabbing.
     267              :     void fgThreadExec();
     268              : 
     269              :     ///@}
     270              : 
     271              :     void *loadImageIntoStreamCopy( void *dest, void *src, size_t width, size_t height, size_t szof );
     272              : 
     273              :     int openShmim();
     274              : 
     275              :     /** \name INDI
     276              :      *
     277              :      *@{
     278              :      */
     279              :   protected:
     280              :     // declare our properties
     281              : 
     282              :     pcf::IndiProperty m_indiP_shmimName; ///< Property used to report the shmim buffer name
     283              : 
     284              :     pcf::IndiProperty m_indiP_frameSize; ///< Property used to report the current frame size
     285              : 
     286              :     pcf::IndiProperty m_indiP_timing;
     287              : 
     288              :   public:
     289              :     /// Update the INDI properties for this device controller
     290              :     /** You should call this once per main loop.
     291              :      * It is not called automatically.
     292              :      *
     293              :      * \returns 0 on success.
     294              :      * \returns -1 on error.
     295              :      */
     296              :     int updateINDI();
     297              : 
     298              :     ///@}
     299              : 
     300              :     /** \name Telemeter Interface
     301              :      * @{
     302              :      */
     303              : 
     304              :     int recordFGTimings( bool force = false );
     305              : 
     306              :     /// @}
     307              : 
     308              :   private:
     309            0 :     derivedT &derived()
     310              :     {
     311            0 :         return *static_cast<derivedT *>( this );
     312              :     }
     313              : };
     314              : 
     315              : template <class derivedT>
     316            0 : int frameGrabber<derivedT>::setupConfig( mx::app::appConfigurator &config )
     317              : {
     318            0 :     config.add( "framegrabber.threadPrio",
     319              :                 "",
     320              :                 "framegrabber.threadPrio",
     321              :                 argType::Required,
     322              :                 "framegrabber",
     323              :                 "threadPrio",
     324              :                 false,
     325              :                 "int",
     326              :                 "The real-time priority of the framegrabber thread." );
     327              : 
     328            0 :     config.add( "framegrabber.cpuset",
     329              :                 "",
     330              :                 "framegrabber.cpuset",
     331              :                 argType::Required,
     332              :                 "framegrabber",
     333              :                 "cpuset",
     334              :                 false,
     335              :                 "string",
     336              :                 "The cpuset to assign the framegrabber thread to." );
     337              : 
     338            0 :     config.add( "framegrabber.shmimName",
     339              :                 "",
     340              :                 "framegrabber.shmimName",
     341              :                 argType::Required,
     342              :                 "framegrabber",
     343              :                 "shmimName",
     344              :                 false,
     345              :                 "string",
     346              :                 "The name of the ImageStreamIO shared memory image. Will be used as /milk/shm/<shmimName>.im.shm." );
     347              : 
     348            0 :     config.add( "framegrabber.circBuffLength",
     349              :                 "",
     350              :                 "framegrabber.circBuffLength",
     351              :                 argType::Required,
     352              :                 "framegrabber",
     353              :                 "circBuffLength",
     354              :                 false,
     355              :                 "size_t",
     356              :                 "The length of the circular buffer. Sets m_circBuffLength, default is 1." );
     357              : 
     358            0 :     config.add( "framegrabber.latencyTime",
     359              :                 "",
     360              :                 "framegrabber.latencyTime",
     361              :                 argType::Required,
     362              :                 "framegrabber",
     363              :                 "latencyTime",
     364              :                 false,
     365              :                 "float",
     366              :                 "The maximum length of time to measure latency timings. "
     367              :                 "Sets  m_latencyCircBuffMaxTime, default is 5." );
     368              : 
     369            0 :     config.add( "framegrabber.latencySize",
     370              :                 "",
     371              :                 "framegrabber.latencySize",
     372              :                 argType::Required,
     373              :                 "framegrabber",
     374              :                 "latencySize",
     375              :                 false,
     376              :                 "float",
     377              :                 "The maximum length of the buffer used to measure latency timings. Sets  m_latencyCircBuffMaxLength, "
     378              :                 "default is 3600." );
     379              : 
     380              :     if( derivedT::c_frameGrabber_flippable )
     381              :     {
     382              :         config.add( "framegrabber.defaultFlip",
     383              :                     "",
     384              :                     "framegrabber.defaultFlip",
     385              :                     argType::Required,
     386              :                     "framegrabber",
     387              :                     "defaultFlip",
     388              :                     false,
     389              :                     "string",
     390              :                     "The default flip of the image.  Options are flipNone, flipUD, flipLR, flipUDLR.  The default is "
     391              :                     "flipNone." );
     392              :     }
     393              : 
     394            0 :     return 0;
     395              : }
     396              : 
     397              : template <class derivedT>
     398            0 : int frameGrabber<derivedT>::loadConfig( mx::app::appConfigurator &config )
     399              : {
     400            0 :     config( m_fgThreadPrio, "framegrabber.threadPrio" );
     401            0 :     config( m_fgCpuset, "framegrabber.cpuset" );
     402            0 :     if( m_shmimName == "" )
     403              :     {
     404            0 :         m_shmimName = derived().configName();
     405              :     }
     406              : 
     407            0 :     config( m_shmimName, "framegrabber.shmimName" );
     408              : 
     409            0 :     if( m_shmimName == "" )
     410              :     {
     411            0 :         return derivedT::template log<software_critical, -1>( { "framegrabber.shmimName can't be empty" } );
     412              :     }
     413              : 
     414            0 :     config( m_circBuffLength, "framegrabber.circBuffLength" );
     415              : 
     416            0 :     if( m_circBuffLength < 1 )
     417              :     {
     418            0 :         m_circBuffLength = 1;
     419            0 :         derivedT::template log<text_log>( "circBuffLength set to 1" );
     420              :     }
     421              : 
     422            0 :     config( m_latencyCircBuffMaxTime, "framegrabber.latencyTime" );
     423            0 :     if( m_latencyCircBuffMaxTime < 0 )
     424              :     {
     425            0 :         m_latencyCircBuffMaxTime = 0;
     426            0 :         derivedT::template log<text_log>( "latencyTime set to 0 (off)" );
     427              :     }
     428              : 
     429            0 :     config( m_latencyCircBuffMaxLength, "framegrabber.latencySize" );
     430              : 
     431              :     if( derivedT::c_frameGrabber_flippable )
     432              :     {
     433              :         std::string flip = "flipNone";
     434              : 
     435              :         config( flip, "framegrabber.defaultFlip" );
     436              : 
     437              :         if( flip == "flipNone" )
     438              :         {
     439              :             m_defaultFlip = fgFlipNone;
     440              :         }
     441              :         else if( flip == "flipUD" )
     442              :         {
     443              :             m_defaultFlip = fgFlipUD;
     444              :         }
     445              :         else if( flip == "flipLR" )
     446              :         {
     447              :             m_defaultFlip = fgFlipLR;
     448              :         }
     449              :         else if( flip == "flipUDLR" )
     450              :         {
     451              :             m_defaultFlip = fgFlipUDLR;
     452              :         }
     453              :         else
     454              :         {
     455              :             derivedT::template log<text_log>( { std::string( "invalid framegrabber flip"
     456              :                                                              "specification (" ) +
     457              :                                                 flip + "), setting flipNone" },
     458              :                                               logPrio::LOG_ERROR );
     459              : 
     460              :             m_defaultFlip = fgFlipNone;
     461              :         }
     462              :     }
     463              : 
     464            0 :     return 0;
     465              : }
     466              : 
     467              : template <class derivedT>
     468            0 : int frameGrabber<derivedT>::appStartup()
     469              : {
     470              :     // Register the shmimName INDI property
     471            0 :     m_indiP_shmimName = pcf::IndiProperty( pcf::IndiProperty::Text );
     472            0 :     m_indiP_shmimName.setDevice( derived().configName() );
     473            0 :     m_indiP_shmimName.setName( "fg_shmimName" );
     474            0 :     m_indiP_shmimName.setPerm( pcf::IndiProperty::ReadOnly );
     475            0 :     m_indiP_shmimName.setState( pcf::IndiProperty::Idle );
     476            0 :     m_indiP_shmimName.add( pcf::IndiElement( "name" ) );
     477            0 :     m_indiP_shmimName["name"] = m_shmimName;
     478              : 
     479            0 :     if( derived().registerIndiPropertyNew( m_indiP_shmimName, nullptr ) < 0 )
     480              :     {
     481              : #ifndef FRAMEGRABBER_TEST_NOLOG
     482            0 :         derivedT::template log<software_error>( { std::source_location::current() } );
     483              : #endif
     484              : 
     485            0 :         return -1;
     486              :     }
     487              : 
     488              :     // Register the frameSize INDI property
     489            0 :     m_indiP_frameSize = pcf::IndiProperty( pcf::IndiProperty::Number );
     490            0 :     m_indiP_frameSize.setDevice( derived().configName() );
     491            0 :     m_indiP_frameSize.setName( "fg_frameSize" );
     492            0 :     m_indiP_frameSize.setPerm( pcf::IndiProperty::ReadOnly );
     493            0 :     m_indiP_frameSize.setState( pcf::IndiProperty::Idle );
     494            0 :     m_indiP_frameSize.add( pcf::IndiElement( "width" ) );
     495            0 :     m_indiP_frameSize["width"] = 0;
     496            0 :     m_indiP_frameSize.add( pcf::IndiElement( "height" ) );
     497            0 :     m_indiP_frameSize["height"] = 0;
     498            0 :     m_indiP_frameSize.add( pcf::IndiElement( "depth" ) );
     499            0 :     m_indiP_frameSize["depth"] = 0;
     500              : 
     501            0 :     if( derived().registerIndiPropertyNew( m_indiP_frameSize, nullptr ) < 0 )
     502              :     {
     503              : #ifndef FRAMEGRABBER_TEST_NOLOG
     504            0 :         derivedT::template log<software_error>( { std::source_location::current() } );
     505              : #endif
     506              : 
     507            0 :         return -1;
     508              :     }
     509              : 
     510              :     // Register the timing INDI property
     511            0 :     derived().createROIndiNumber( m_indiP_timing, "fg_timing" );
     512            0 :     m_indiP_timing.add( pcf::IndiElement( "acq_fps" ) );
     513            0 :     m_indiP_timing.add( pcf::IndiElement( "acq_min" ) );
     514            0 :     m_indiP_timing.add( pcf::IndiElement( "acq_max" ) );
     515            0 :     m_indiP_timing.add( pcf::IndiElement( "acq_jitter" ) );
     516            0 :     m_indiP_timing.add( pcf::IndiElement( "write_fps" ) );
     517            0 :     m_indiP_timing.add( pcf::IndiElement( "write_min" ) );
     518            0 :     m_indiP_timing.add( pcf::IndiElement( "write_max" ) );
     519            0 :     m_indiP_timing.add( pcf::IndiElement( "write_jitter" ) );
     520            0 :     m_indiP_timing.add( pcf::IndiElement( "delta_aw" ) );
     521            0 :     m_indiP_timing.add( pcf::IndiElement( "delta_aw_jitter" ) );
     522              : 
     523            0 :     if( derived().registerIndiPropertyReadOnly( m_indiP_timing ) < 0 )
     524              :     {
     525              : #ifndef FRAMEGRABBER_TEST_NOLOG
     526            0 :         derivedT::template log<software_error>( { std::source_location::current() } );
     527              : #endif
     528              : 
     529            0 :         return -1;
     530              :     }
     531              : 
     532              :     // Start the f.g. thread
     533            0 :     if( derived().threadStart( m_fgThread,
     534            0 :                                m_fgThreadInit,
     535            0 :                                m_fgThreadID,
     536            0 :                                m_fgThreadProp,
     537              :                                m_fgThreadPrio,
     538            0 :                                m_fgCpuset,
     539              :                                "framegrabber",
     540              :                                this,
     541            0 :                                fgThreadStart ) < 0 )
     542              :     {
     543            0 :         derivedT::template log<software_error, -1>( { std::source_location::current() } );
     544            0 :         return -1;
     545              :     }
     546              : 
     547            0 :     return 0;
     548              : }
     549              : 
     550              : template <class derivedT>
     551            0 : int frameGrabber<derivedT>::appLogic()
     552              : {
     553              :     // do a join check to see if other threads have exited.
     554            0 :     if( pthread_tryjoin_np( m_fgThread.native_handle(), 0 ) == 0 )
     555              :     {
     556            0 :         derivedT::template log<software_error>( { "framegrabber thread has exited" } );
     557              : 
     558            0 :         return -1;
     559              :     }
     560              : 
     561              :     try
     562              :     {
     563            0 :         if( derived().state() == stateCodes::OPERATING && m_atimes.size() > 0 && derived().fps() > 0 )
     564              :         {
     565            0 :             if( m_atimes.size() >= m_atimes.maxEntries() )
     566              :             {
     567            0 :                 cbIndexT latTime = m_latencyCircBuffMaxTime * m_cbFPS;
     568            0 :                 if( latTime >= m_atimes.maxEntries() )
     569              :                 {
     570            0 :                     latTime = m_atimes.maxEntries() - 1;
     571              :                 }
     572              : 
     573            0 :                 m_atimesD.resize( latTime - 1 );
     574            0 :                 m_wtimesD.resize( latTime - 1 );
     575            0 :                 m_watimesD.resize( latTime - 1 );
     576              : 
     577            0 :                 cbIndexT refEntry = m_atimes.latest();
     578              : 
     579            0 :                 if( refEntry >= latTime )
     580              :                 {
     581            0 :                     refEntry -= latTime;
     582              :                 }
     583              :                 else
     584              :                 {
     585            0 :                     refEntry = m_atimes.maxEntries() + refEntry - latTime;
     586              :                 }
     587              : 
     588            0 :                 timespec ts = m_atimes.at( refEntry, 0 );
     589            0 :                 double   a0 = ts.tv_sec + ( (double)ts.tv_nsec ) / 1e9;
     590              : 
     591            0 :                 ts        = m_wtimes.at( refEntry, 0 );
     592            0 :                 double w0 = ts.tv_sec + ( (double)ts.tv_nsec ) / 1e9;
     593              : 
     594            0 :                 double mina = 1e9;
     595            0 :                 double maxa = -1e9;
     596            0 :                 double minw = 1e9;
     597            0 :                 double maxw = -1e9;
     598              : 
     599            0 :                 for( size_t n = 1; n <= m_atimesD.size(); ++n )
     600              :                 {
     601            0 :                     ts = m_atimes.at( refEntry, n );
     602              : 
     603            0 :                     double a = ts.tv_sec + ( (double)ts.tv_nsec ) / 1e9;
     604              : 
     605            0 :                     ts = m_wtimes.at( refEntry, n );
     606              : 
     607            0 :                     double w = ts.tv_sec + ( (double)ts.tv_nsec ) / 1e9;
     608              : 
     609            0 :                     m_atimesD[n - 1]  = a - a0;
     610            0 :                     m_wtimesD[n - 1]  = w - w0;
     611            0 :                     m_watimesD[n - 1] = w - a;
     612            0 :                     a0                = a;
     613            0 :                     w0                = w;
     614              : 
     615            0 :                     if( m_atimesD[n - 1] < mina )
     616              :                     {
     617            0 :                         mina = m_atimesD[n - 1];
     618              :                     }
     619              : 
     620            0 :                     if( m_atimesD[n - 1] > maxa )
     621              :                     {
     622            0 :                         maxa = m_atimesD[n - 1];
     623              :                     }
     624              : 
     625            0 :                     if( m_wtimesD[n - 1] < minw )
     626              :                     {
     627            0 :                         minw = m_wtimesD[n - 1];
     628              :                     }
     629              : 
     630            0 :                     if( m_wtimesD[n - 1] > maxw )
     631              :                     {
     632            0 :                         maxw = m_wtimesD[n - 1];
     633              :                     }
     634              : 
     635            0 :                     if( m_wtimesD[n - 1] < 0 )
     636              :                     {
     637            0 :                         std::cerr << "negative wtime: " << m_wtimesD[n - 1] << ' ' << n << ' ' << m_atimesD.size()
     638            0 :                                   << ' ' << refEntry << ' ' << m_atimes.maxEntries() << ' ' << latTime << '\n';
     639              : 
     640            0 :                         return derivedT::template log<software_error, 0>( { "negative write time. latency "
     641            0 :                                                                             "circ buff is not long enought" } );
     642              :                     }
     643              :                 }
     644              : 
     645            0 :                 m_mna  = mx::math::vectorMean( m_atimesD );
     646            0 :                 m_vara = mx::math::vectorVariance( m_atimesD, m_mna );
     647            0 :                 m_mina = mina;
     648            0 :                 m_maxa = maxa;
     649              : 
     650            0 :                 m_mnw  = mx::math::vectorMean( m_wtimesD );
     651            0 :                 m_varw = mx::math::vectorVariance( m_wtimesD, m_mnw );
     652            0 :                 m_minw = minw;
     653            0 :                 m_maxw = maxw;
     654              : 
     655            0 :                 m_mnwa  = mx::math::vectorMean( m_watimesD );
     656            0 :                 m_varwa = mx::math::vectorVariance( m_watimesD, m_mnwa );
     657              : 
     658            0 :                 recordFGTimings();
     659              :             }
     660              :             else
     661              :             {
     662            0 :                 m_mna   = 0;
     663            0 :                 m_vara  = 0;
     664            0 :                 m_mina  = 0;
     665            0 :                 m_maxa  = 0;
     666            0 :                 m_mnw   = 0;
     667            0 :                 m_varw  = 0;
     668            0 :                 m_minw  = 0;
     669            0 :                 m_maxw  = 0;
     670            0 :                 m_mnwa  = 0;
     671            0 :                 m_varwa = 0;
     672              :             }
     673              :         }
     674              :         else
     675              :         {
     676            0 :             m_mna   = 0;
     677            0 :             m_vara  = 0;
     678            0 :             m_mina  = 0;
     679            0 :             m_maxa  = 0;
     680            0 :             m_mnw   = 0;
     681            0 :             m_varw  = 0;
     682            0 :             m_minw  = 0;
     683            0 :             m_maxw  = 0;
     684            0 :             m_mnwa  = 0;
     685            0 :             m_varwa = 0;
     686              :         }
     687              :     }
     688            0 :     catch( const std::exception &e )
     689              :     {
     690            0 :         std::cerr << e.what() << '\n';
     691              :     }
     692              : 
     693            0 :     return 0;
     694              : }
     695              : 
     696              : template <class derivedT>
     697              : int frameGrabber<derivedT>::onPowerOff()
     698              : {
     699              :     m_mna   = 0;
     700              :     m_vara  = 0;
     701              :     m_mina  = 0;
     702              :     m_maxa  = 0;
     703              :     m_mnw   = 0;
     704              :     m_varw  = 0;
     705              :     m_minw  = 0;
     706              :     m_maxw  = 0;
     707              :     m_mnwa  = 0;
     708              :     m_varwa = 0;
     709              : 
     710              :     m_width  = 0;
     711              :     m_height = 0;
     712              :     m_circBuffLength = 1;
     713              : 
     714              :     updateINDI();
     715              : 
     716              :     m_reconfig = true;
     717              : 
     718              :     return 0;
     719              : }
     720              : 
     721              : template <class derivedT>
     722            0 : int frameGrabber<derivedT>::appShutdown()
     723              : {
     724            0 :     if( m_fgThread.joinable() )
     725              :     {
     726              :         try
     727              :         {
     728            0 :             m_fgThread.join(); // this will throw if it was already joined
     729              :         }
     730            0 :         catch( ... )
     731              :         {
     732              :         }
     733              :     }
     734              : 
     735            0 :     return 0;
     736              : }
     737              : 
     738              : template <class derivedT>
     739            0 : int frameGrabber<derivedT>::configCircBuffs()
     740              : {
     741            0 :     m_cbFPS = derived().fps();
     742              : 
     743            0 :     if( m_latencyCircBuffMaxLength == 0 || m_latencyCircBuffMaxTime == 0 || m_cbFPS <= 0 )
     744              :     {
     745            0 :         m_atimes.maxEntries( 0 );
     746            0 :         m_wtimes.maxEntries( 0 );
     747              : 
     748            0 :         if( m_cbFPS < 0 )
     749              :         {
     750            0 :             return -1;
     751              :         }
     752              :     }
     753              :     else
     754              :     {
     755              :         // Set up the latency circ. buffs
     756            0 :         cbIndexT cbSz = 2 * m_latencyCircBuffMaxTime * m_cbFPS;
     757            0 :         if( cbSz > m_latencyCircBuffMaxLength )
     758              :         {
     759            0 :             cbSz = m_latencyCircBuffMaxLength;
     760              :         }
     761            0 :         if( cbSz < 3 )
     762              :         {
     763            0 :             cbSz = 3; // Make variance meaningful
     764              :         }
     765              : 
     766            0 :         m_atimes.maxEntries( cbSz );
     767            0 :         m_wtimes.maxEntries( cbSz );
     768              :     }
     769              : 
     770            0 :     return 0;
     771              : }
     772              : 
     773              : template <class derivedT>
     774            0 : void frameGrabber<derivedT>::fgThreadStart( frameGrabber *o )
     775              : {
     776            0 :     o->fgThreadExec();
     777            0 : }
     778              : 
     779              : template <class derivedT>
     780            0 : void frameGrabber<derivedT>::fgThreadExec()
     781              : {
     782              :     // Get the thread PID immediately so the caller can return.
     783            0 :     m_fgThreadID = syscall( SYS_gettid );
     784              : 
     785              :     // timespec writestart;
     786              : 
     787              :     // Wait fpr the thread starter to finish initializing this thread.
     788            0 :     while( m_fgThreadInit == true && derived().shutdown() == 0 )
     789              :     {
     790            0 :         sleep( 1 );
     791              :     }
     792              : 
     793            0 :     uint32_t    imsize[3] = { 0, 0, 0 };
     794            0 :     bool        cbuff     = false;
     795            0 :     std::string shmimName;
     796              : 
     797              :     static bool logged_wrong_size = false;
     798              : 
     799            0 :     while( derived().shutdown() == 0 )
     800              :     {
     801              :         ///\todo this ought to wait until OPERATING, using READY as a sign of "not integrating"
     802            0 :         while( !derived().shutdown() &&
     803            0 :                ( !( derived().state() == stateCodes::READY || derived().state() == stateCodes::OPERATING ) ||
     804            0 :                  derived().powerState() <= 0 ) )
     805              :         {
     806            0 :             sleep( 1 );
     807              :         }
     808              : 
     809            0 :         if( derived().shutdown() )
     810              :         {
     811            0 :             break;
     812              :         }
     813              : 
     814              :         // At the end of this, must have m_width, m_height, m_dataType set, and derived()->fps must be valid.
     815            0 :         if( derived().configureAcquisition() != 0 )
     816              :         {
     817            0 :             sleep( 1 );
     818            0 :             continue;
     819              :         }
     820              : 
     821            0 :         m_typeSize = ImageStreamIO_typesize( m_dataType );
     822              : 
     823            0 :         if( configCircBuffs() < 0 )
     824              :         {
     825            0 :             derivedT::template log<software_error>( { "error configuring latency circ. buffs" } );
     826              :         }
     827              : 
     828              :         /* Initialize ImageStreamIO
     829              :          */
     830              : 
     831              :         // Check if we are already connected by configureAcquisition
     832            0 :         if( m_imageStream != nullptr )
     833              :         {
     834            0 :             imsize[0] = m_imageStream->md[0].size[0];
     835              : 
     836            0 :             if( m_imageStream->md[0].naxis > 1 )
     837              :             {
     838            0 :                 imsize[1] = m_imageStream->md[0].size[1];
     839              :             }
     840              : 
     841            0 :             if( m_imageStream->md[0].naxis > 2 )
     842              :             {
     843            0 :                 imsize[2] = m_imageStream->md[0].size[2];
     844            0 :                 cbuff     = true;
     845              :             }
     846              :             else
     847              :             {
     848            0 :                 imsize[2] = 1;
     849            0 :                 cbuff     = false;
     850              :             }
     851              :         }
     852              : 
     853            0 :         if( m_width != imsize[0] || m_height != imsize[1] || m_circBuffLength != imsize[2] || m_imageStream == nullptr )
     854              :         {
     855            0 :             if( m_imageStream != nullptr && m_ownShmim )
     856              :             {
     857            0 :                 ImageStreamIO_destroyIm( m_imageStream );
     858            0 :                 free( m_imageStream );
     859              :             }
     860            0 :             else if( m_imageStream != nullptr && !m_ownShmim )
     861              :             {
     862            0 :                 if( !logged_wrong_size )
     863              :                 {
     864            0 :                     derivedT::template log<text_log>(
     865            0 :                         std::format( "image stream {} is not expected size", m_shmimName ), logPrio::LOG_WARNING );
     866            0 :                     logged_wrong_size = true;
     867              :                 }
     868              : 
     869            0 :                 sleep( 1 );
     870            0 :                 continue; // we go around and try again, waiting for the shmim to get resized to our expectations
     871              :             }
     872              : 
     873            0 :             logged_wrong_size = false;
     874              : 
     875            0 :             m_imageStream = reinterpret_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
     876              : 
     877            0 :             imsize[0] = m_width;
     878            0 :             imsize[1] = m_height;
     879            0 :             imsize[2] = m_circBuffLength;
     880              : 
     881            0 :             std::cerr << "Creating: " << m_shmimName << " " << m_width << " " << m_height << " " << m_circBuffLength
     882            0 :                       << "\n";
     883              : 
     884            0 :             ImageStreamIO_createIm_gpu( m_imageStream,
     885              :                                         m_shmimName.c_str(),
     886              :                                         3,
     887              :                                         imsize,
     888            0 :                                         m_dataType,
     889              :                                         -1,
     890              :                                         1,
     891              :                                         IMAGE_NB_SEMAPHORE,
     892              :                                         0,
     893              :                                         CIRCULAR_BUFFER | ZAXIS_TEMPORAL,
     894              :                                         0 );
     895              : 
     896            0 :             m_imageStream->md->cnt1 = m_circBuffLength - 1;
     897              : 
     898            0 :             cbuff = true; // because we created it!
     899              :         }
     900              : 
     901              :         // This completes the reconfiguration.
     902            0 :         m_reconfig = false;
     903              : 
     904            0 :         if( derived().startAcquisition() < 0 )
     905              :         {
     906            0 :             continue;
     907              :         }
     908              : 
     909            0 :         uint64_t next_cnt1 = 0;
     910            0 :         char    *next_dest = reinterpret_cast<char *>( m_imageStream->array.raw );
     911              : 
     912            0 :         timespec *next_wtimearr = nullptr;
     913            0 :         timespec *next_atimearr = nullptr;
     914            0 :         uint64_t *next_cntarr   = nullptr;
     915              : 
     916            0 :         if( cbuff )
     917              :         {
     918            0 :             next_wtimearr = &m_imageStream->writetimearray[0];
     919            0 :             next_atimearr = &m_imageStream->atimearray[0];
     920            0 :             next_cntarr   = &m_imageStream->cntarray[0];
     921              :         }
     922              : 
     923              :         // This is the main image grabbing loop.
     924            0 :         while( !derived().shutdown() && !m_reconfig && derived().powerState() > 0 )
     925              :         {
     926              :             //==================
     927              :             // Get next image, process validity.
     928              :             //====================
     929            0 :             int isValid = derived().acquireAndCheckValid();
     930            0 :             if( isValid != 0 )
     931              :             {
     932            0 :                 if( isValid < 0 )
     933              :                 {
     934            0 :                     break;
     935              :                 }
     936              :                 else
     937              :                 {
     938            0 :                     continue;
     939              :                 }
     940              :             }
     941              : 
     942              :             // Ok, no timeout, so we process the image and publish it.
     943            0 :             m_imageStream->md->write = 1;
     944              : 
     945            0 :             if( derived().loadImageIntoStream( next_dest ) < 0 )
     946              :             {
     947            0 :                 break;
     948              :             }
     949              : 
     950              :             // Set the time of last write
     951              :             // clock_gettime(CLOCK_REALTIME, &m_imageStream->md->writetime);
     952            0 :             if( clock_gettime( CLOCK_REALTIME, &m_imageStream->md->writetime ) < 0 )
     953              :             {
     954            0 :                 derivedT::template log<software_critical>( { errno, "clock_gettime" } );
     955              :             }
     956              : 
     957              :             // Set the image acquisition timestamp
     958            0 :             m_imageStream->md->atime = m_currImageTimestamp;
     959              : 
     960              :             // Update cnt1
     961            0 :             m_imageStream->md->cnt1 = next_cnt1;
     962              : 
     963              :             // Update cnt0
     964            0 :             m_imageStream->md->cnt0++;
     965              : 
     966            0 :             if( cbuff )
     967              :             {
     968            0 :                 *next_wtimearr = m_imageStream->md->writetime;
     969            0 :                 *next_atimearr = m_currImageTimestamp;
     970            0 :                 *next_cntarr   = m_imageStream->md->cnt0;
     971              :             }
     972              : 
     973              :             // And post
     974            0 :             m_imageStream->md->write = 0;
     975            0 :             ImageStreamIO_sempost( m_imageStream, -1 );
     976              : 
     977              :             // Update the latency circ. buffs
     978            0 :             if( m_atimes.maxEntries() > 0 )
     979              :             {
     980            0 :                 m_atimes.nextEntry( m_imageStream->md->atime );
     981            0 :                 m_wtimes.nextEntry( m_imageStream->md->writetime );
     982              :             }
     983              : 
     984              :             // Now we increment pointers outside the time-critical part of the loop.
     985            0 :             next_cnt1 = m_imageStream->md->cnt1 + 1;
     986            0 :             if( next_cnt1 >= m_circBuffLength )
     987              :             {
     988            0 :                 next_cnt1 = 0;
     989              :             }
     990              : 
     991            0 :             next_dest =
     992            0 :                 reinterpret_cast<char *>( m_imageStream->array.raw ) + next_cnt1 * m_width * m_height * m_typeSize;
     993              : 
     994            0 :             if( cbuff )
     995              :             {
     996            0 :                 next_wtimearr = &m_imageStream->writetimearray[next_cnt1];
     997            0 :                 next_atimearr = &m_imageStream->atimearray[next_cnt1];
     998            0 :                 next_cntarr   = &m_imageStream->cntarray[next_cnt1];
     999              :             }
    1000              : 
    1001              :             // Touch them to make sure we move
    1002            0 :             m_dummy_c = next_dest[0];
    1003              : 
    1004            0 :             if( cbuff )
    1005              :             {
    1006            0 :                 m_dummy_ts.tv_sec = next_wtimearr[0].tv_sec + next_atimearr[0].tv_sec;
    1007            0 :                 m_dummy_cnt       = next_cntarr[0];
    1008              :             }
    1009              : 
    1010            0 :             if( m_cbFPS != derived().fps() )
    1011              :             {
    1012            0 :                 std::cerr << "configuring due to mismatch m_cbFPS\n";
    1013            0 :                 if( configCircBuffs() < 0 )
    1014              :                 {
    1015            0 :                     derivedT::template log<software_error>( { "error configuring latency circ. buffs" } );
    1016              :                 }
    1017              :             }
    1018              :         }
    1019              : 
    1020            0 :         if( m_reconfig && !derived().shutdown() )
    1021              :         {
    1022            0 :             derived().reconfig();
    1023              :         }
    1024              : 
    1025              :     } // outer loop, will exit if m_shutdown==true
    1026              : 
    1027            0 :     if( m_imageStream != nullptr )
    1028              :     {
    1029            0 :         if( m_ownShmim )
    1030              :         {
    1031            0 :             ImageStreamIO_destroyIm( m_imageStream );
    1032              :         }
    1033              :         else
    1034              :         {
    1035            0 :             ImageStreamIO_closeIm( m_imageStream );
    1036              :         }
    1037              : 
    1038            0 :         free( m_imageStream );
    1039            0 :         m_imageStream = nullptr;
    1040              :     }
    1041            0 : }
    1042              : 
    1043              : template <class derivedT>
    1044              : void *frameGrabber<derivedT>::loadImageIntoStreamCopy( void *dest, void *src, size_t width, size_t height, size_t szof )
    1045              : {
    1046              :     if( !derivedT::c_frameGrabber_flippable )
    1047              :     {
    1048              :         return memcpy( dest, src, width * height * szof );
    1049              :     }
    1050              :     else
    1051              :     {
    1052              :         switch( m_defaultFlip )
    1053              :         {
    1054              :         case fgFlipNone:
    1055              :             return mx::improc::imcpy( dest, src, width, height, szof );
    1056              :         case fgFlipUD:
    1057              :             return mx::improc::imcpy_flipUD( dest, src, width, height, szof );
    1058              :         case fgFlipLR:
    1059              :             return mx::improc::imcpy_flipLR( dest, src, width, height, szof );
    1060              :         case fgFlipUDLR:
    1061              :             return mx::improc::imcpy_flipUDLR( dest, src, width, height, szof );
    1062              :         default:
    1063              :             return nullptr;
    1064              :         }
    1065              :     }
    1066              : }
    1067              : 
    1068              : template <class derivedT>
    1069              : int frameGrabber<derivedT>::openShmim()
    1070              : {
    1071              :     static bool logged = false;
    1072              : 
    1073              :     if( m_imageStream != nullptr )
    1074              :     {
    1075              :         ImageStreamIO_closeIm( m_imageStream );
    1076              :         free( m_imageStream );
    1077              :         m_imageStream = nullptr;
    1078              :     }
    1079              : 
    1080              :     // b/c ImageStreamIO prints every single time, and latest version don't support stopping it yet, and that
    1081              :     // isn't thread-safe-able anyway we do our own checks.  This is the same code in ImageStreamIO_openIm...
    1082              :     int  SM_fd;
    1083              :     char SM_fname[1024];
    1084              :     ImageStreamIO_filename( SM_fname, sizeof( SM_fname ), m_shmimName.c_str() );
    1085              :     SM_fd = open( SM_fname, O_RDWR );
    1086              : 
    1087              :     if( SM_fd == -1 )
    1088              :     {
    1089              :         if( !logged )
    1090              :         {
    1091              :             derivedT::template log<text_log>( "ImageStream " + m_shmimName + " not found (yet).  Retrying . . .",
    1092              :                                               logPrio::LOG_NOTICE );
    1093              :             logged = true;
    1094              :         }
    1095              : 
    1096              :         return 1;
    1097              :     }
    1098              : 
    1099              :     // Found and opened,  close it and then use ImageStreamIO
    1100              :     logged = false;
    1101              :     close( SM_fd );
    1102              : 
    1103              :     m_imageStream = reinterpret_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
    1104              : 
    1105              :     if( ImageStreamIO_openIm( m_imageStream, m_shmimName.c_str() ) == 0 )
    1106              :     {
    1107              :         if( m_imageStream->md[0].sem < SEMAPHORE_MAXVAL )
    1108              :         {
    1109              :             ImageStreamIO_closeIm( m_imageStream );
    1110              :             free( m_imageStream );
    1111              :             m_imageStream = nullptr;
    1112              : 
    1113              :             return 1; // We just need to wait for the server process to finish startup.
    1114              :         }
    1115              :         else
    1116              :         {
    1117              :             char SM_fname[1024];
    1118              :             ImageStreamIO_filename( SM_fname, sizeof( SM_fname ), m_shmimName.c_str() );
    1119              : 
    1120              :             struct stat buffer;
    1121              : 
    1122              :             int rv = stat( SM_fname, &buffer );
    1123              : 
    1124              :             if( rv != 0 )
    1125              :             {
    1126              :                 derivedT::template log<software_critical>( { errno,
    1127              :                                                              "Could not get inode for " + m_shmimName +
    1128              :                                                                  ". Source process will need to be restarted." } );
    1129              : 
    1130              :                 ImageStreamIO_closeIm( m_imageStream );
    1131              : 
    1132              :                 free( m_imageStream );
    1133              : 
    1134              :                 m_imageStream = nullptr;
    1135              : 
    1136              :                 derived().m_shutdown = true;
    1137              : 
    1138              :                 return -1;
    1139              :             }
    1140              : 
    1141              :             m_inode = buffer.st_ino;
    1142              : 
    1143              :             m_width = m_imageStream->md->size[0];
    1144              : 
    1145              :             if( m_imageStream->md->naxis == 2  )
    1146              :             {
    1147              :                 m_height = m_imageStream->md->size[1];
    1148              :                 m_circBuffLength  = 1;
    1149              :             }
    1150              :             else if( m_imageStream->md->naxis == 3 )
    1151              :             {
    1152              :                 m_height = m_imageStream->md->size[1];
    1153              :                 m_circBuffLength  = m_imageStream->md->size[2];
    1154              :             }
    1155              :             else
    1156              :             {
    1157              :                 m_height = 1;
    1158              :                 m_circBuffLength  = 1;
    1159              :             }
    1160              : 
    1161              :             m_dataType = m_imageStream->md->datatype;
    1162              :             m_typeSize = ImageStreamIO_typesize( m_dataType );
    1163              : 
    1164              :             return 0;
    1165              :         }
    1166              :     }
    1167              :     else
    1168              :     {
    1169              :         free( m_imageStream );
    1170              :         m_imageStream = nullptr;
    1171              : 
    1172              :         return 1; // be patient
    1173              :     }
    1174              : }
    1175              : 
    1176              : template <class derivedT>
    1177            0 : int frameGrabber<derivedT>::updateINDI()
    1178              : {
    1179            0 :     if( !derived().m_indiDriver )
    1180            0 :         return 0;
    1181              : 
    1182            0 :     indi::updateIfChanged( m_indiP_shmimName, "name", m_shmimName, derived().m_indiDriver );
    1183            0 :     indi::updateIfChanged( m_indiP_frameSize, "width", m_width, derived().m_indiDriver );
    1184            0 :     indi::updateIfChanged( m_indiP_frameSize, "height", m_height, derived().m_indiDriver );
    1185            0 :     indi::updateIfChanged( m_indiP_frameSize, "depth", m_circBuffLength, derived().m_indiDriver );
    1186              : 
    1187            0 :     double fpsa = 0;
    1188            0 :     double fpsw = 0;
    1189            0 :     if( m_mna != 0 )
    1190            0 :         fpsa = 1.0 / m_mna;
    1191            0 :     if( m_mnw != 0 )
    1192            0 :         fpsw = 1.0 / m_mnw;
    1193              : 
    1194            0 :     indi::updateIfChanged<double>(
    1195            0 :         m_indiP_timing,
    1196              :         { "acq_fps",
    1197              :           "acq_min",
    1198              :           "acq_max",
    1199              :           "acq_jitter",
    1200              :           "write_fps",
    1201              :           "write_min",
    1202              :           "write_max",
    1203              :           "write_jitter",
    1204              :           "delta_aw",
    1205              :           "delta_aw_jitter" },
    1206            0 :         { fpsa, m_mina, m_maxa, sqrt( m_vara ), fpsw, m_minw, m_maxw, sqrt( m_varw ), m_mnwa, sqrt( m_varwa ) },
    1207            0 :         derived().m_indiDriver );
    1208              : 
    1209            0 :     return 0;
    1210              : }
    1211              : 
    1212              : template <class derivedT>
    1213            0 : int frameGrabber<derivedT>::recordFGTimings( bool force )
    1214              : {
    1215              :     static double last_mna  = 0;
    1216              :     static double last_vara = 0;
    1217              : 
    1218              :     static double last_mnw  = 0;
    1219              :     static double last_varw = 0;
    1220              : 
    1221              :     static double last_mnwa  = 0;
    1222              :     static double last_varwa = 0;
    1223              : 
    1224            0 :     if( force || m_mna != last_mna || m_vara != last_vara || m_mnw != last_mnw || m_varw != last_varw ||
    1225            0 :         m_mnwa != last_mnwa || m_varwa != last_varwa )
    1226              :     {
    1227            0 :         derived().template telem<telem_fgtimings>(
    1228            0 :             { m_mna, sqrt( m_vara ), m_mnw, sqrt( m_varw ), m_mnwa, sqrt( m_varwa ) } );
    1229              : 
    1230            0 :         last_mna   = m_mna;
    1231            0 :         last_vara  = m_vara;
    1232            0 :         last_mnw   = m_mnw;
    1233            0 :         last_varw  = m_varw;
    1234            0 :         last_mnwa  = m_mnwa;
    1235            0 :         last_varwa = m_varwa;
    1236              :     }
    1237              : 
    1238            0 :     return 0;
    1239              : }
    1240              : 
    1241              : /// Call frameGrabberT::setupConfig with error checking for frameGrabber
    1242              : /**
    1243              :  * \param cfig the application configurator
    1244              :  */
    1245              : #define FRAMEGRABBER_SETUP_CONFIG( cfig )                                                                              \
    1246              :     if( frameGrabberT::setupConfig( cfig ) < 0 )                                                                       \
    1247              :     {                                                                                                                  \
    1248              :         log<software_error>( { "Error from frameGrabberT::setupConfig" } );                                            \
    1249              :         m_shutdown = true;                                                                                             \
    1250              :         return;                                                                                                        \
    1251              :     }
    1252              : 
    1253              : /// Call frameGrabberT::loadConfig with error checking for frameGrabber
    1254              : /** This must be inside a function that returns int, e.g. the standard loadConfigImpl.
    1255              :  * \param cfig the application configurator
    1256              :  */
    1257              : #define FRAMEGRABBER_LOAD_CONFIG( cfig )                                                                               \
    1258              :     if( frameGrabberT::loadConfig( cfig ) < 0 )                                                                        \
    1259              :     {                                                                                                                  \
    1260              :         return log<software_error, -1>( { "Error from frameGrabberT::loadConfig" } );                                  \
    1261              :     }
    1262              : 
    1263              : /// Call frameGrabberT::appStartup with error checking for frameGrabber
    1264              : #define FRAMEGRABBER_APP_STARTUP                                                                                       \
    1265              :     if( frameGrabberT::appStartup() < 0 )                                                                              \
    1266              :     {                                                                                                                  \
    1267              :         return log<software_error, -1>( { "Error from frameGrabberT::appStartup" } );                                  \
    1268              :     }
    1269              : 
    1270              : /// Call frameGrabberT::appLogic with error checking for frameGrabber
    1271              : #define FRAMEGRABBER_APP_LOGIC                                                                                         \
    1272              :     if( frameGrabberT::appLogic() < 0 )                                                                                \
    1273              :     {                                                                                                                  \
    1274              :         return log<software_error, -1>( { "Error from frameGrabberT::appLogic" } );                                    \
    1275              :     }
    1276              : 
    1277              : /// Call frameGrabberT::updateINDI with error checking for frameGrabber
    1278              : #define FRAMEGRABBER_UPDATE_INDI                                                                                       \
    1279              :     if( frameGrabberT::updateINDI() < 0 )                                                                              \
    1280              :     {                                                                                                                  \
    1281              :         return log<software_error, -1>( { "Error from frameGrabberT::updateINDI" } );                                  \
    1282              :     }
    1283              : 
    1284              : /// Call frameGrabberT::appShutdown with error checking for frameGrabber
    1285              : #define FRAMEGRABBER_APP_SHUTDOWN                                                                                      \
    1286              :     if( frameGrabberT::appShutdown() < 0 )                                                                             \
    1287              :     {                                                                                                                  \
    1288              :         return log<software_error, -1>( { "Error from frameGrabberT::appShutdown" } );                                 \
    1289              :     }
    1290              : 
    1291              : } // namespace dev
    1292              : } // namespace app
    1293              : } // namespace MagAOX
    1294              : #endif
        

Generated by: LCOV version 2.0-1