LCOV - code coverage report
Current view: top level - libMagAOX/app/dev - frameGrabber.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 37.0 % 373 138
Test Date: 2026-04-15 19:34:29 Functions: 80.0 % 15 12

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

Generated by: LCOV version 2.0-1