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

            Line data    Source code
       1              : /** \file dmPokeWFS.hpp
       2              :   * \brief The MagAO-X DM Poke Centering header file
       3              :   *
       4              :   * \ingroup dmPokeWFS_files
       5              :   */
       6              : 
       7              : #ifndef dmPokeWFS_hpp
       8              : #define dmPokeWFS_hpp
       9              : 
      10              : #include <mx/improc/eigenImage.hpp>
      11              : #include <mx/improc/milkImage.hpp>
      12              : #include <mx/improc/eigenCube.hpp>
      13              : using namespace mx::improc;
      14              : 
      15              : #include "semUtilsDerived.hpp"
      16              : 
      17              : #include "../../ImageStreamIO/pixaccess.hpp"
      18              : 
      19              : /** \defgroup dmPokeWFS
      20              :   * \brief The MagAO-X device to coordinate poking a deformable mirror's actuators and synchronize reads of a camera image.
      21              :   *
      22              :   * <a href="../handbook/operating/software/apps/dmPokeWFS.html">Application Documentation</a>
      23              :   *
      24              :   * \ingroup apps
      25              :   *
      26              :   */
      27              : 
      28              : 
      29              : /** \defgroup dmPokeWFS_files
      30              :   * \ingroup dmPokeWFS
      31              :   */
      32              : 
      33              : namespace MagAOX
      34              : {
      35              : namespace app
      36              : {
      37              : namespace dev
      38              : {
      39              : 
      40              : 
      41              : 
      42              : /// A base class to coordinate poking a deformable mirror's actuators and synchronizedreads of a camera image.
      43              : /** CRTP class `derivedT` has the following requirements:
      44              :   *
      45              :   * - Must be derived from MagAOXApp<true>
      46              :   *
      47              :   * - Must be derived from `dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::wfsShmimT>` (replace DERIVEDNAME with derivedT class name)
      48              :   *
      49              :   * - Must be derived from  `dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::darkShmimT>` (replace DERIVEDNAME with derivedT class name)
      50              :   *
      51              :   * - Must contain the following friend declarations (replace DERIVEDNAME with derivedT class name):
      52              :   *   \code
      53              :   *      friend class dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::wfsShmimT>;
      54              :   *      friend class dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::darkShmimT>;
      55              :   *      friend class dev::dmPokeWFS<DERIVEDNAME>
      56              :   *   \endcode
      57              :   *
      58              :   * - Must contain the following typedefs (replace DERIVEDNAME with derivedT class name):
      59              :   *   \code
      60              :   *       typedef dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::wfsShmimT> shmimMonitorT;
      61              :   *       typedef dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::darkShmimT> darkShmimMonitorT;
      62              :   *       typedef dev::dmPokeWFS<DERIVEDNAME> dmPokeWFST;
      63              :   *
      64              :   *   \endcode
      65              :   * - Must provide the following interfaces:
      66              :   *   \code
      67              :   *       shmimMonitorT & shmimMonitor()
      68              :   *       {
      69              :   *           return *static_cast<shmimMonitorT *>(this);
      70              :   *       }
      71              :   *
      72              :   *       darkShmimMonitorT & darkShmimMonitor()
      73              :   *       {
      74              :   *           return *static_cast<darkShmimMonitorT *>(this);
      75              :   *       }
      76              :   *   \endcode
      77              :   *
      78              :   * - If derivedT has additional shmimMonitor parents, you will need to include these lines in the class
      79              :   *   declaration:
      80              :   *   \code
      81              :   *       using dmPokeWFST::allocate;
      82              :   *       using dmPokeWFST::processImage;
      83              :   *   \endcode
      84              :   *
      85              :   * - Must provide the following interface:
      86              :   *   \code
      87              :   *       // Run the sensor steps
      88              :   *       // Coordinates the actions of poking and collecting images.
      89              :   *       // Upon completion this calls runSensor.  If \p firstRun == true, one time
      90              :   *       // actions such as taking a dark can be executed.
      91              :   *       //
      92              :   *       // returns 0 on success
      93              :   *       // returns \< 0 on an error
      94              :   *       int runSensor(bool firstRun ///< [in] flag indicating this is the first call.  triggers taking a dark if true.
      95              :   *                     );
      96              :   *   \endcode
      97              :   *
      98              :   * - Must provide the following interface:
      99              :   *   \code
     100              :   *       // Analyze the poke image
     101              :   *       // This analyzes the resulting poke images.
     102              :   *       //
     103              :   *       // returns 0 on success
     104              :   *       // returns \< 0 on an error
     105              :   *       int analyzeSensor();
     106              :   *   \endcode
     107              :   *   At the conclusion of analyzeSensor the measured signal (e.g. deltaX and deltaY) should be updated and set in m_indiP_measurement.
     108              :   *   The function \ref updateMeasurement() can be used for this.  However, the updating of the loop counter and the subsequent INDI
     109              :   *   property update is handled automatically after that.
     110              :   *
     111              :   * - Must be a telemeter with the following interface:
     112              :   *
     113              :   *     - Must be derived from `dev::telemeter<DERIVEDNAME>` (replace DERIVEDNAME with derivedT class name) and meet the requirements
     114              :   *       of `dev::telemeter`
     115              :   *
     116              :   *     - In the function `derivedT::checkRecordTimes()` required by `dev::telemeter`,  the `telem_pokeloop` type must be checked.
     117              :   *       The minimum `derivedT::checkRecordTimes()` is:
     118              :   *       \code
     119              :   *         int checkRecordTimes()
     120              :   *         {
     121              :   *             return telemeterT::checkRecordTimes(telem_pokeloop());
     122              :   *         }
     123              :   *       \endcode
     124              :   *
     125              :   * - Must call this base class's setupConfig(), loadConfig(), appStartup(), appStartup(), and appShutdown() in the
     126              :   *    appropriate functions.  For convenience the following macros are defined to provide error checking:
     127              :   *    \code
     128              :   *       DMPOKEWFS_SETUP_CONFIG( cfig )
     129              :   *       DMPOKEWFS_LOAD_CONFIG( cfig )
     130              :   *       DMPOKEWFS_APP_STARTUP
     131              :   *       DMPOKEWFS_APP_LOGIC
     132              :   *       DMPOKEWFS_APP_SHUTDOWN
     133              :   *    \endcode
     134              :   *
     135              :   * \ingroup appdev
     136              :   */
     137              : template<class derivedT>
     138              : class dmPokeWFS
     139              : {
     140              : 
     141              : public:
     142              : 
     143              :     struct wfsShmimT
     144              :     {
     145            0 :         static std::string configSection()
     146              :         {
     147            0 :             return "wfscam";
     148              :         };
     149              : 
     150            0 :         static std::string indiPrefix()
     151              :         {
     152            0 :             return "wfscam";
     153              :         };
     154              :     };
     155              : 
     156              :     struct darkShmimT
     157              :     {
     158            0 :         static std::string configSection()
     159              :         {
     160            0 :             return "wfsdark";
     161              :         };
     162              : 
     163            0 :         static std::string indiPrefix()
     164              :         {
     165            0 :             return "wfsdark";
     166              :         };
     167              :     };
     168              : 
     169              : protected:
     170              : 
     171              :     /** \name Configurable Parameters
     172              :       *@{
     173              :       */
     174              : 
     175              :     std::string m_wfsCamDevName; ///<INDI device name of the WFS camera.  Default is wfscam.shmimName.
     176              : 
     177              :     double m_wfsSemWait {1.5}; ///< The time in sec to wait on the WFS semaphore.  Default 0.5 sec.
     178              : 
     179              :     double m_imageSemWait {0.5}; ///< The time in sec to wait on the image semaphore.  Default 0.5 sec.
     180              : 
     181              :     unsigned m_nPokeImages {5}; ///< The number of images to average for the poke images.  Default is 5.
     182              : 
     183              :     unsigned m_nPokeAverage {10}; ///< The number of poke sequences to average.  Default is 10.
     184              : 
     185              :     std::string m_dmChan;
     186              : 
     187              :     std::vector<int> m_poke_x;
     188              :     std::vector<int> m_poke_y;
     189              : 
     190              :     float m_poke_amp {0.0};
     191              : 
     192              :     float m_dmSleep {10000}; ///<The time to sleep for the DM command to be applied, in microseconds. Default is 10000.
     193              : 
     194              :     ///@}
     195              : 
     196              :     std::mutex m_wfsImageMutex;
     197              : 
     198              :     mx::improc::milkImage<float> m_rawImage;
     199              : 
     200              :     mx::improc::milkImage<float> m_pokeImage;
     201              :     mx::improc::eigenImage<float> m_pokeLocal;
     202              : 
     203              :     float (*wfsPixget)(void *, size_t) {nullptr}; ///< Pointer to a function to extract the image data as float
     204              : 
     205              :     float m_wfsFps {-1}; ///< The WFS camera FPS
     206              : 
     207              :     mx::improc::eigenImage<float> m_darkImage; ///< The dark image
     208              : 
     209              :     bool m_darkValid {false}; ///< Flag indicating if dark is valid based on its size.
     210              : 
     211              :     float (*darkPixget)(void *, size_t) {nullptr}; ///< Pointer to a function to extract the dark image data as float
     212              : 
     213              :     mx::improc::milkImage<float> m_dmStream;
     214              : 
     215              :     mx::improc::eigenImage<float> m_dmImage;
     216              : 
     217              :     float m_deltaX {0};
     218              :     float m_deltaY {0};
     219              :     uint64_t m_counter {0};
     220              : 
     221              : public:
     222              : 
     223              :     /**\name MagAOXApp Interface
     224              :       *
     225              :       * @{
     226              :       */
     227              : 
     228              :     /// Setup the configuration system
     229              :     /**
     230              :      * This should be called in `derivedT::setupConfig` as
     231              :      * \code
     232              :        dmPokeWFS<derivedT,realT>::setupConfig(config);
     233              :        \endcode
     234              :      * with appropriate error checking.
     235              :      */
     236              :     int setupConfig( mx::app::appConfigurator & config /**< [in] an application configuration to load values to*/);
     237              : 
     238              :     /// load the configuration system results
     239              :     /**
     240              :       * This should be called in `derivedT::loadConfig` as
     241              :       * \code
     242              :         dmPokeWFS<derivedT,realT>::loadConfig(config);
     243              :         \endcode
     244              :       * with appropriate error checking.
     245              :       */
     246              :     int loadConfig( mx::app::appConfigurator & config /**< [in] an application configuration from which to load values */);
     247              : 
     248              :    /// Startup function
     249              :    /**
     250              :      * This should be called in `derivedT::appStartup` as
     251              :      * \code
     252              :        dmPokeWFS<derivedT,realT>::appStartup();
     253              :        \endcode
     254              :      * with appropriate error checking.
     255              :      *
     256              :      * \returns 0 on success
     257              :      * \returns -1 on error, which is logged.
     258              :      */
     259              :     int appStartup();
     260              : 
     261              :    /// dmPokeWFS application logic
     262              :    /** This should be called in `derivedT::appLogic` as
     263              :      * \code
     264              :        dmPokeWFS<derivedT,realT>::appLogic();
     265              :        \endcode
     266              :      * with appropriate error checking.
     267              :      *
     268              :      * \returns 0 on success
     269              :      * \returns -1 on error, which is logged.
     270              :      */
     271              :     int appLogic();
     272              : 
     273              :    /// dmPokeWFS shutdown
     274              :    /** This should be called in `derivedT::appShutdown` as
     275              :      * \code
     276              :        dmPokeWFS<derivedT,realT>::appShutdown();
     277              :        \endcode
     278              :      * with appropriate error checking.
     279              :      *
     280              :      * \returns 0 on success
     281              :      * \returns -1 on error, which is logged.
     282              :      */
     283              :     int appShutdown();
     284              : 
     285              :     ///@}
     286              : 
     287              :     /** \name shmimMonitor Interface
     288              :       * @{
     289              :       */
     290              : 
     291              :     int allocate( const wfsShmimT & /**< [in] tag to differentiate shmimMonitor parents.*/);
     292              : 
     293              :     int processImage( void * curr_src,   ///< [in] pointer to the start of the current frame
     294              :                       const wfsShmimT &  ///< [in] tag to differentiate shmimMonitor parents.
     295              :                     );
     296              :     ///@}
     297              : 
     298              : 
     299              :     /** \name darkShmimMonitor Interface
     300              :       * @{
     301              :       */
     302              : 
     303              :     int allocate( const darkShmimT & /**< [in] tag to differentiate shmimMonitor parents.*/);
     304              : 
     305              :     int processImage( void * curr_src,   ///< [in] pointer to the start of the current frame
     306              :                       const darkShmimT &  ///< [in] tag to differentiate shmimMonitor parents.
     307              :                     );
     308              :     ///@}
     309              : 
     310              :     /** \name WFS Thread
     311              :       * This thread coordinates the WFS process
     312              :       *
     313              :       * @{
     314              :       */
     315              : protected:
     316              : 
     317              :     int m_wfsThreadPrio {1}; ///< Priority of the WFS thread, should normally be > 00.
     318              : 
     319              :     std::string m_wfsCpuset; ///< The cpuset for the framegrabber thread.  Ignored if empty (the default).
     320              : 
     321              :     std::thread m_wfsThread; ///< A separate thread for the actual WFSing
     322              : 
     323              :     bool m_wfsThreadInit {true}; ///< Synchronizer to ensure wfs thread initializes before doing dangerous things.
     324              : 
     325              :     pid_t m_wfsThreadID {0}; ///< WFS thread PID.
     326              : 
     327              :     pcf::IndiProperty m_wfsThreadProp; ///< The property to hold the WFS thread details.
     328              : 
     329              :     ///Thread starter, called by wfsThreadStart on thread construction.  Calls wfsThreadExec.
     330              :     static void wfsThreadStart( dmPokeWFS * s /**< [in] a pointer to an streamWriter instance (normally this) */);
     331              : 
     332              :     /// Execute the frame grabber main loop.
     333              :     void wfsThreadExec();
     334              : 
     335              :     sem_t m_wfsSemaphore; ///< Semaphore used to signal the WFS thread to start WFSing.
     336              : 
     337              :     unsigned m_wfsSemWait_sec {1}; ///< The timeout for the WFS semaphore, seconds component.
     338              : 
     339              :     unsigned m_wfsSemWait_nsec {0}; ///< The timeoutfor the WFS semaphore, nanoseconds component.
     340              : 
     341              :     int m_measuring {0}; ///< Status of measuring: 0 no, 1 single in progress, 2 continuous in progress.
     342              : 
     343              :     bool m_single {false}; ///< True a single measurement is in progress.
     344              : 
     345              :     bool m_continuous {false}; ///< True if continuous measurements are in progress.
     346              : 
     347              :     bool m_stopMeasurement {false}; ///< Used to request that the measurement in progress stop.
     348              : 
     349              :     ///@}
     350              : 
     351              :     sem_t m_imageSemaphore; ///< Semaphore used to signal that an image is ready
     352              : 
     353              :     unsigned m_imageSemWait_sec {1}; ///< The timeout for the image semaphore, seconds component.
     354              : 
     355              :     unsigned m_imageSemWait_nsec {0}; ///< The timeout for the image semaphore, nanoseconds component.
     356              : 
     357              : 
     358              :     /// Apply a single DM poke pattern and record the results
     359              :     /** This accumulates m_nPokeImages*m_nPokeAverage images in m_pokeLocal, so m_pokeLocal
     360              :       * should be zeroed before the first call to this (e.g. for a +1 poke),
     361              :       * but not zeroed before the second call (e.g. for the -1 poke). You also need
     362              :       * to 0 the DM after finishing a poke pair.
     363              :       * See basicRunSensor() for how to use.
     364              :       *
     365              :       * \returns +1 if exit is due to shutdown or stop request
     366              :       * \returns 0 if no error
     367              :       * \returns -1 if an error occurs
     368              :       */
     369              :     int basicTimedPoke(float pokeSign /**< [in] the sign, and possibly a scaling, to apply to m_pokeAmplitude*/);
     370              : 
     371              :     /// Run the basic +/- poke sensor steps
     372              :     /** Coordinates the actions of poking and collecting images.
     373              :       *
     374              :       * This can be called from the derived class runSensor.
     375              :       *
     376              :       * \returns +1 if exit is due to shutdown or stop request
     377              :       * \returns 0 if no error
     378              :       * \returns -1 if an error occurs
     379              :       */
     380              :     int basicRunSensor();
     381              : 
     382              :     int updateMeasurement( float deltaX,
     383              :                            float deltaY
     384              :                          );
     385              : 
     386              :     /** \name INDI Interface
     387              :       * @{
     388              :       */
     389              : protected:
     390              : 
     391              :     pcf::IndiProperty m_indiP_poke_amp;
     392            0 :     INDI_NEWCALLBACK_DECL(derivedT, m_indiP_poke_amp);
     393              : 
     394              :     pcf::IndiProperty m_indiP_nPokeImages;
     395            0 :     INDI_NEWCALLBACK_DECL(derivedT, m_indiP_nPokeImages);
     396              : 
     397              :     pcf::IndiProperty m_indiP_nPokeAverage;
     398            0 :     INDI_NEWCALLBACK_DECL(derivedT, m_indiP_nPokeAverage);
     399              : 
     400              :     pcf::IndiProperty m_indiP_wfsFps; ///< Property to get the FPS from the WFS camera
     401            0 :     INDI_SETCALLBACK_DECL(derivedT, m_indiP_wfsFps);
     402              : 
     403              :     pcf::IndiProperty m_indiP_single; ///< Switch to start a single measurement
     404            0 :     INDI_NEWCALLBACK_DECL(derivedT, m_indiP_single);
     405              : 
     406              :     pcf::IndiProperty m_indiP_continuous; ///< Switch to start continuous measurement
     407            0 :     INDI_NEWCALLBACK_DECL(derivedT, m_indiP_continuous);
     408              : 
     409              :     pcf::IndiProperty m_indiP_stop; ///< Switch to request that measurement stop
     410            0 :     INDI_NEWCALLBACK_DECL(derivedT, m_indiP_stop);
     411              : 
     412              :     pcf::IndiProperty m_indiP_measurement; ///< Property to report the delta measurement, including the loop counter.
     413              : 
     414              : 
     415              :     ///@}
     416              : 
     417              :     /** \name Telemeter Interface
     418              :       * @{
     419              :       */
     420              : 
     421              :     int recordTelem(const telem_pokeloop *);
     422              : 
     423              :     int recordPokeLoop(bool force = false);
     424              : 
     425              :     ///@}
     426              : 
     427              : private:
     428            0 :     derivedT & derived()
     429              :     {
     430            0 :         return *static_cast<derivedT *>(this);
     431              :     }
     432              : 
     433              : };
     434              : 
     435              : template<class derivedT>
     436            0 : int dmPokeWFS<derivedT>::setupConfig(mx::app::appConfigurator & config)
     437              : {
     438            0 :     if(derived().shmimMonitor().setupConfig(config) < 0)
     439              :     {
     440            0 :         derivedT::template log<software_error>({__FILE__, __LINE__, "shmimMonitorT::setupConfig"});
     441            0 :         return -1;
     442              :     }
     443              : 
     444            0 :     config.add("wfscam.camDevName", "", "wfscam.camDevName", argType::Required, "wfscam", "camDevName", false, "string", "INDI device name of the WFS camera.  Default is wfscam.shmimName.");
     445            0 :     config.add("wfscam.loopSemWait", "", "wfscam.loopSemWait", argType::Required, "wfscam", "loopSemWait", false, "float", "The semaphore wait time for the wfs loop start signal");
     446            0 :     config.add("wfscam.imageSemWait", "", "wfscam.imageSemWait", argType::Required, "wfscam", "imageSemWait", false, "float", "The semaphore wait time for the image availability signal");
     447              : 
     448            0 :     if(derived().darkShmimMonitor().setupConfig(config) < 0)
     449              :     {
     450            0 :         derivedT::template log<software_error>({__FILE__, __LINE__, "darkShmimMonitorT::setupConfig"});
     451            0 :         return -1;
     452              :     }
     453              : 
     454            0 :     config.add("pokecen.dmChannel", "", "pokecen.dmChannel", argType::Required, "pokecen", "dmChannel", false, "string", "The dm channel to use for pokes, e.g. dm01disp06.");
     455            0 :     config.add("pokecen.pokeX", "", "pokecen.pokeX", argType::Required, "pokecen", "pokeX", false, "vector<int>", "The x-coordinates of the actuators to poke. ");
     456            0 :     config.add("pokecen.pokeY", "", "pokecen.pokeY", argType::Required, "pokecen", "pokeY", false, "vector<int>", "The y-coordinates of the actuators to poke. ");
     457            0 :     config.add("pokecen.pokeAmp", "", "pokecen.pokeAmp", argType::Required, "pokecen", "pokeAmp", false, "float", "The poke amplitude, in DM command units. Default is 0.");
     458            0 :     config.add("pokecen.dmSleep", "", "pokecen.dmSleep", argType::Required, "pokecen", "dmSleep", false, "float", "The time to sleep for the DM command to be applied, in microseconds. Default is 10000.");
     459            0 :     config.add("pokecen.nPokeImages", "", "pokecen.nPokeImages", argType::Required, "pokecen", "nPokeImages", false, "int", "The number of poke images to average.  Default 5.");
     460            0 :     config.add("pokecen.nPokeAverage", "", "pokecen.nPokeAverage", argType::Required, "pokecen", "nPokeAverage", false, "int", "The number of poke sequences to average.  Default 10.");
     461              : 
     462              : 
     463            0 :     return 0;
     464              : }
     465              : 
     466              : 
     467              : template<class derivedT>
     468            0 : int dmPokeWFS<derivedT>::loadConfig( mx::app::appConfigurator & config)
     469              : {
     470            0 :     if(derived().shmimMonitor().loadConfig(config) < 0)
     471              :     {
     472            0 :         return derivedT::template log<software_error, -1>({__FILE__, __LINE__, "shmimMonitorT::loadConfig"});
     473              :     }
     474              : 
     475            0 :     m_wfsCamDevName = derived().shmimMonitor().shmimName();
     476            0 :     config(m_wfsCamDevName, "wfscam.camDevName");
     477              : 
     478              :     //configure the semaphore waits
     479            0 :     config(m_wfsSemWait, "wfscam.loopSemWait");
     480              : 
     481            0 :     m_wfsSemWait_sec = floor(m_wfsSemWait);
     482            0 :     m_wfsSemWait_nsec = (m_wfsSemWait - m_wfsSemWait_sec) * 1e9;
     483              : 
     484            0 :     config(m_imageSemWait, "wfscam.imageSemWait");
     485              : 
     486            0 :     m_imageSemWait_sec = floor(m_imageSemWait);
     487            0 :     m_imageSemWait_nsec = (m_imageSemWait - m_imageSemWait_sec) * 1e9;
     488              : 
     489            0 :     if(derived().darkShmimMonitor().loadConfig(config) < 0)
     490              :     {
     491            0 :         return derivedT::template log<software_error, -1>({__FILE__, __LINE__, "darkShmimMonitorT::loadConfig"});
     492              :     }
     493              : 
     494            0 :     config(m_dmChan, "pokecen.dmChannel");
     495              : 
     496            0 :     config(m_poke_x, "pokecen.pokeX");
     497              : 
     498            0 :     config(m_poke_y, "pokecen.pokeY");
     499              : 
     500            0 :     if(m_poke_x.size() == 0 || (m_poke_x.size() != m_poke_y.size()))
     501              :     {
     502            0 :         return derivedT::template log<software_error,-1>({__FILE__, __LINE__, "invalid poke specification"});
     503              :     }
     504              : 
     505            0 :     config(m_poke_amp, "pokecen.pokeAmp");
     506              : 
     507            0 :     config(m_dmSleep, "pokecen.dmSleep");
     508              : 
     509            0 :     config(m_nPokeImages, "pokecen.nPokeImages");
     510              : 
     511            0 :     config(m_nPokeAverage, "pokecen.nPokeAverage");
     512              : 
     513            0 :     return 0;
     514              : }
     515              : 
     516              : template<class derivedT>
     517            0 : int dmPokeWFS<derivedT>::appStartup()
     518              : {
     519            0 :     if( derived().shmimMonitor().appStartup() < 0)
     520              :     {
     521            0 :         return derivedT::template log<software_error, -1>({__FILE__,__LINE__});
     522              :     }
     523              : 
     524            0 :     if( derived().darkShmimMonitor().appStartup() < 0)
     525              :     {
     526            0 :         return derivedT::template log<software_error, -1>({__FILE__,__LINE__});
     527              :     }
     528              : 
     529            0 :     CREATE_REG_INDI_NEW_NUMBERF_DERIVED(m_indiP_poke_amp, "poke_amp", -1, 1, 1e-1, "%0.01f", "", "");
     530            0 :     m_indiP_poke_amp["current"].setValue(m_poke_amp);
     531            0 :     m_indiP_poke_amp["target"].setValue(m_poke_amp);
     532              : 
     533            0 :     CREATE_REG_INDI_NEW_NUMBERI_DERIVED(m_indiP_nPokeImages, "nPokeImages", 1, 1000, 1, "%d", "", "");
     534            0 :     m_indiP_nPokeImages["current"].setValue(m_nPokeImages);
     535            0 :     m_indiP_nPokeImages["target"].setValue(m_nPokeImages);
     536              : 
     537            0 :     CREATE_REG_INDI_NEW_NUMBERI_DERIVED(m_indiP_nPokeAverage, "nPokeAverage", 1, 1000, 1, "%d", "", "");
     538            0 :     m_indiP_nPokeAverage["current"].setValue(m_nPokeAverage);
     539            0 :     m_indiP_nPokeAverage["target"].setValue(m_nPokeAverage);
     540              : 
     541            0 :     REG_INDI_SETPROP_DERIVED(m_indiP_wfsFps, m_wfsCamDevName, std::string("fps"));
     542              : 
     543            0 :     CREATE_REG_INDI_NEW_TOGGLESWITCH_DERIVED( m_indiP_single, "single");
     544              : 
     545            0 :     CREATE_REG_INDI_NEW_TOGGLESWITCH_DERIVED( m_indiP_continuous, "continuous");
     546              : 
     547            0 :     CREATE_REG_INDI_NEW_REQUESTSWITCH_DERIVED( m_indiP_stop, "stop");
     548              : 
     549            0 :     derived().template registerIndiPropertyReadOnly( m_indiP_measurement, "measurement", pcf::IndiProperty::Number, pcf::IndiProperty::ReadOnly, pcf::IndiProperty::Idle);
     550            0 :     m_indiP_measurement.add({"delta_x", 0.0});
     551            0 :     m_indiP_measurement.add({"delta_y", 0.0});
     552            0 :     m_indiP_measurement.add({"counter", 0});
     553              : 
     554            0 :     if(sem_init(&m_wfsSemaphore, 0,0) < 0)
     555              :     {
     556            0 :         return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno,0, "Initializing wfs semaphore"});
     557              :     }
     558              : 
     559            0 :     if(sem_init(&m_imageSemaphore, 0,0) < 0)
     560              :     {
     561            0 :         return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno,0, "Initializing image semaphore"});
     562              :     }
     563              : 
     564            0 :     if(derived().template threadStart( m_wfsThread, m_wfsThreadInit, m_wfsThreadID, m_wfsThreadProp, m_wfsThreadPrio, m_wfsCpuset, "wfs", this, wfsThreadStart)  < 0)
     565              :     {
     566            0 :         return derivedT::template log<software_critical,-1>({__FILE__, __LINE__});
     567              :     }
     568              : 
     569            0 :     return 0;
     570              : }
     571              : 
     572              : template<class derivedT>
     573            0 : int dmPokeWFS<derivedT>::appLogic()
     574              : {
     575              : 
     576            0 :     if( derived().shmimMonitor().appLogic() < 0)
     577              :     {
     578            0 :         return derivedT::template log<software_error, -1>({__FILE__,__LINE__});
     579              :     }
     580              : 
     581            0 :     if( derived().darkShmimMonitor().appLogic() < 0)
     582              :     {
     583            0 :         return derivedT::template log<software_error, -1>({__FILE__,__LINE__});
     584              :     }
     585              : 
     586              :     //first do a join check to see if other threads have exited.
     587              :     //these will throw if the threads are really gone
     588              :     try
     589              :     {
     590            0 :         if(pthread_tryjoin_np(m_wfsThread.native_handle(),0) == 0)
     591              :         {
     592            0 :             derivedT::template log<software_error>({__FILE__, __LINE__, "WFS thread has exited"});
     593            0 :             return -1;
     594              :         }
     595              :     }
     596            0 :     catch(...)
     597              :     {
     598            0 :         derivedT::template log<software_error>({__FILE__, __LINE__, "WFS thread has exited"});
     599            0 :         return -1;
     600              :     }
     601              : 
     602            0 :     if(m_measuring > 0)
     603              :     {
     604            0 :         if(m_continuous)
     605              :         {
     606            0 :             derived().template updateSwitchIfChanged(m_indiP_continuous, "toggle", pcf::IndiElement::SwitchStateType::On, INDI_OK);
     607              :         }
     608              :         else
     609              :         {
     610            0 :             derived().template updateSwitchIfChanged(m_indiP_continuous, "toggle", pcf::IndiElement::SwitchStateType::Off, INDI_IDLE);
     611              :         }
     612              : 
     613            0 :         if(m_single)
     614              :         {
     615            0 :             derived().template updateSwitchIfChanged(m_indiP_single, "toggle", pcf::IndiElement::SwitchStateType::On, INDI_OK);
     616              :         }
     617              :         else
     618              :         {
     619            0 :             derived().template updateSwitchIfChanged(m_indiP_single, "toggle", pcf::IndiElement::SwitchStateType::Off, INDI_IDLE);
     620              :         }
     621              :     }
     622              :     else
     623              :     {
     624            0 :         derived().template updateSwitchIfChanged(m_indiP_continuous, "toggle", pcf::IndiElement::SwitchStateType::Off, INDI_IDLE);
     625            0 :         derived().template updateSwitchIfChanged(m_indiP_single, "toggle", pcf::IndiElement::SwitchStateType::Off, INDI_IDLE);
     626              :     }
     627              : 
     628            0 :     derived().template updateIfChanged( m_indiP_nPokeImages, "current", m_nPokeImages);
     629            0 :     derived().template updateIfChanged( m_indiP_nPokeAverage, "current", m_nPokeAverage);
     630            0 :     derived().template updateIfChanged( m_indiP_poke_amp, "current", m_poke_amp);
     631              : 
     632            0 :     return 0;
     633              : }
     634              : 
     635              : template<class derivedT>
     636            0 : int dmPokeWFS<derivedT>::appShutdown()
     637              : {
     638            0 :     if(derived().shmimMonitor().appShutdown() < 0)
     639              :     {
     640            0 :         derivedT::template log<software_error>({__FILE__, __LINE__, "error from shmimMonitorT::appShutdown"});
     641              :     }
     642              : 
     643            0 :     if(derived().darkShmimMonitor().appShutdown() < 0)
     644              :     {
     645            0 :         derivedT::template log<software_error>({__FILE__, __LINE__, "error from darkShmimMonitorT::appShutdown"});
     646              :     }
     647              : 
     648            0 :     if (m_wfsThread.joinable())
     649              :     {
     650            0 :         pthread_kill(m_wfsThread.native_handle(), SIGUSR1);
     651              :         try
     652              :         {
     653            0 :             m_wfsThread.join(); // this will throw if it was already joined
     654              :         }
     655            0 :         catch (...)
     656              :         {
     657              :         }
     658              :     }
     659              : 
     660            0 :     return 0;
     661              : }
     662              : 
     663              : template<class derivedT>
     664            0 : int dmPokeWFS<derivedT>::allocate( const wfsShmimT & dummy)
     665              : {
     666              :     static_cast<void>(dummy); //be unused
     667              : 
     668            0 :     std::unique_lock<std::mutex> lock(m_wfsImageMutex);
     669              : 
     670            0 :     m_rawImage.create( derived().m_configName + "_raw", derived().shmimMonitor().width(), derived().shmimMonitor().height());
     671              : 
     672            0 :     wfsPixget = getPixPointer<float>(derived().shmimMonitor().dataType());
     673              : 
     674              :     try
     675              :     {
     676            0 :         m_dmStream.open(m_dmChan);
     677              :     }
     678            0 :     catch(const std::exception& e)
     679              :     {
     680            0 :         return derivedT::template log<software_error,-1>({__FILE__, __LINE__, std::string("exception opening DM: ") + e.what()});
     681              :     }
     682              : 
     683            0 :     m_dmStream.passive(true);
     684              : 
     685            0 :     m_dmImage.resize(m_dmStream.rows(), m_dmStream.cols());
     686              : 
     687            0 :     if(derived().darkShmimMonitor().width() == derived().shmimMonitor().width() &&
     688            0 :          derived().darkShmimMonitor().height() == derived().shmimMonitor().height() )
     689              :     {
     690            0 :         m_darkValid = true;
     691              :     }
     692              :     else
     693              :     {
     694            0 :         m_darkValid = false;
     695              :     }
     696              : 
     697            0 :     if(m_pokeImage.rows() != derived().shmimMonitor().width() || m_pokeImage.cols() != derived().shmimMonitor().height())
     698              :     {
     699            0 :         m_pokeImage.create(derived().m_configName + "_poke", derived().shmimMonitor().width(), derived().shmimMonitor().height());
     700              :     }
     701              : 
     702            0 :     m_pokeLocal.resize(derived().shmimMonitor().width(), derived().shmimMonitor().height());
     703              : 
     704            0 :     return 0;
     705            0 : }
     706              : 
     707              : template<class derivedT>
     708            0 : int dmPokeWFS<derivedT>::processImage( void * curr_src,
     709              :                                        const wfsShmimT &  dummy
     710              :                                      )
     711              : {
     712              :     static_cast<void>(dummy); //be unused
     713              : 
     714            0 :     std::unique_lock<std::mutex> lock(m_wfsImageMutex);
     715              : 
     716            0 :     float * data = m_rawImage().data();
     717            0 :     float * darkData = m_darkImage.data();
     718              : 
     719              :     //Copy the data out as float no matter what type it is
     720            0 :     uint64_t Npix = derived().shmimMonitor().width()*derived().shmimMonitor().height();
     721              : 
     722            0 :     if(m_darkValid)
     723              :     {
     724            0 :         for(unsigned nn=0; nn < Npix; ++nn)
     725              :         {
     726            0 :             data[nn] = wfsPixget(curr_src, nn) - darkData[nn];
     727              :         }
     728              :     }
     729              :     else
     730              :     {
     731            0 :         for(unsigned nn=0; nn < Npix; ++nn)
     732              :         {
     733            0 :             data[nn] = wfsPixget(curr_src, nn);
     734              :         }
     735              :     }
     736              : 
     737            0 :     if(sem_post(&m_imageSemaphore) < 0)
     738              :     {
     739            0 :         return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno, 0, "Error posting to semaphore"});
     740              :     }
     741              : 
     742            0 :     return 0;
     743            0 : }
     744              : 
     745              : //--dark shmim
     746              : 
     747              : template<class derivedT>
     748            0 : int dmPokeWFS<derivedT>::allocate( const darkShmimT & dummy)
     749              : {
     750              :     static_cast<void>(dummy); //be unused
     751              : 
     752            0 :     std::unique_lock<std::mutex> lock(m_wfsImageMutex);
     753              : 
     754            0 :     m_darkImage.resize(derived().darkShmimMonitor().width(), derived().darkShmimMonitor().height());
     755              : 
     756            0 :     darkPixget = getPixPointer<float>(derived().darkShmimMonitor().dataType());
     757              : 
     758            0 :     if(derived().darkShmimMonitor().width() == derived().shmimMonitor().width() &&
     759            0 :          derived().darkShmimMonitor().height() == derived().shmimMonitor().height() )
     760              :     {
     761            0 :         std::cerr << "dark is valid " << derived().darkShmimMonitor().width() << " " << derived().shmimMonitor().width() << " ";
     762            0 :         std::cerr << derived().darkShmimMonitor().height() << " " << derived().shmimMonitor().height() << "\n";
     763            0 :         m_darkValid = true;
     764              :     }
     765              :     else
     766              :     {
     767            0 :         m_darkValid = false;
     768              :     }
     769              : 
     770            0 :     return 0;
     771            0 : }
     772              : 
     773              : template<class derivedT>
     774            0 : int dmPokeWFS<derivedT>::processImage( void * curr_src,
     775              :                                        const darkShmimT &  dummy
     776              :                                      )
     777              : {
     778              :     static_cast<void>(dummy); //be unused
     779              : 
     780            0 :     std::unique_lock<std::mutex> lock(m_wfsImageMutex);
     781              : 
     782            0 :     float * darkData = m_darkImage.data();
     783              : 
     784              :     //Copy the data out as float no matter what type it is
     785            0 :     uint64_t nPix = derived().darkShmimMonitor().width()*derived().darkShmimMonitor().height();
     786            0 :     for(unsigned nn=0; nn < nPix; ++nn)
     787              :     {
     788            0 :         darkData[nn] = darkPixget(curr_src, nn);
     789              :     }
     790              : 
     791            0 :     return 0;
     792            0 : }
     793              : 
     794              : template<class derivedT>
     795            0 : void dmPokeWFS<derivedT>::wfsThreadStart( dmPokeWFS * d)
     796              : {
     797            0 :    d->wfsThreadExec();
     798            0 : }
     799              : 
     800              : template<class derivedT>
     801            0 : void dmPokeWFS<derivedT>::wfsThreadExec()
     802              : {
     803            0 :     m_wfsThreadID = syscall(SYS_gettid);
     804              : 
     805              :     //Wait fpr the thread starter to finish initializing this thread.
     806            0 :     while(m_wfsThreadInit == true && derived().m_shutdown == 0)
     807              :     {
     808            0 :         sleep(1);
     809              :     }
     810              : 
     811            0 :     while(derived().m_shutdown == 0)
     812              :     {
     813              :         timespec ts;
     814            0 :         XWC_SEM_WAIT_TS_RETVOID_DERIVED(ts, m_wfsSemWait_sec, m_wfsSemWait_nsec);
     815              : 
     816            0 :         XWC_SEM_TIMEDWAIT_LOOP_DERIVED( m_wfsSemaphore, ts )
     817              : 
     818              :         //Lock a mutex here
     819            0 :         if(m_single)
     820              :         {
     821            0 :             m_measuring = 1;
     822              :         }
     823            0 :         else if(m_continuous)
     824              :         {
     825            0 :             m_measuring = 2;
     826              :         }
     827              :         else
     828              :         {
     829            0 :             m_measuring = 0;
     830            0 :             return;
     831              :         }
     832              : 
     833            0 :         derived().template state(stateCodes::OPERATING);
     834              : 
     835            0 :         while(!m_pokeImage.valid())
     836              :         {
     837            0 :             mx::sys::milliSleep(10);
     838              :         }
     839              : 
     840            0 :         m_stopMeasurement = false;
     841              : 
     842            0 :         bool firstRun = true;
     843              : 
     844            0 :         while(!m_stopMeasurement && !derived().m_shutdown)
     845              :         {
     846            0 :             if( derived().runSensor(firstRun) < 0)
     847              :             {
     848            0 :                 derivedT::template log<software_error>({__FILE__, __LINE__, "runSensor returned error"});
     849            0 :                 break;
     850              :             }
     851              : 
     852            0 :             if(m_stopMeasurement || derived().m_shutdown)
     853              :             {
     854            0 :                 break;
     855              :             }
     856              : 
     857            0 :             if( derived().analyzeSensor() < 0)
     858              :             {
     859            0 :                 derivedT::template log<software_error>({__FILE__, __LINE__, "runSensor returned error"});
     860            0 :                 break;
     861              :             }
     862              : 
     863            0 :             ++m_counter;
     864            0 :             derived().updateIfChanged(m_indiP_measurement, "counter", m_counter);
     865            0 :             derived().recordPokeLoop();
     866              : 
     867            0 :             firstRun = false;
     868              : 
     869            0 :             if(m_measuring == 1)
     870              :             {
     871            0 :                 break;
     872              :             }
     873              :         }
     874              : 
     875            0 :         m_measuring = 0;
     876            0 :         m_single = 0;
     877            0 :         m_continuous = 0;
     878              : 
     879            0 :         derived().template state(stateCodes::READY);
     880              : 
     881              : 
     882              :     } //outer loop, will exit if derived().m_shutdown==true
     883              : 
     884            0 :     return;
     885              : 
     886              : }
     887              : 
     888              : template<class derivedT>
     889            0 : int dmPokeWFS<derivedT>::basicTimedPoke(float pokeSign)
     890              : {
     891              :     timespec ts;
     892              : 
     893            0 :     int sign = 1;
     894            0 :     if(pokeSign < 0) sign = -1;
     895              : 
     896              :     //Prepare the DM image with the pokes
     897            0 :     m_dmImage.setZero();
     898              : 
     899            0 :     for(size_t nn = 0; nn < m_poke_x.size(); ++nn)
     900              :     {
     901            0 :         m_dmImage( m_poke_x[nn], m_poke_y[nn]) = pokeSign*m_poke_amp;
     902              :     }
     903              : 
     904              :     //This is where the pokes are applied to the DM
     905            0 :     m_dmStream = m_dmImage;
     906              : 
     907            0 :     mx::sys::microSleep(m_dmSleep);
     908              : 
     909              :     //flush semaphore so we take the _next_ good image
     910            0 :     XWC_SEM_FLUSH_DERIVED(m_imageSemaphore);
     911              : 
     912              :     //** And wait one image to be sure we are on a whole poke**//
     913            0 :     XWC_SEM_WAIT_TS_DERIVED(ts, m_imageSemWait_sec, m_imageSemWait_nsec);
     914            0 :     bool ready = false;
     915            0 :     while(!ready && !(m_stopMeasurement || derived().m_shutdown))
     916              :     {
     917            0 :         XWC_SEM_TIMEDWAIT_LOOP_DERIVED( m_imageSemaphore, ts )
     918              :         else
     919              :         {
     920            0 :             ready = true;
     921              :         }
     922              :     }
     923              : 
     924            0 :     uint32_t n = 0;
     925            0 :     while(n < m_nPokeImages && !(m_stopMeasurement || derived().m_shutdown))
     926              :     {
     927              :         //** Now we record the poke image **//
     928            0 :         XWC_SEM_WAIT_TS_DERIVED(ts, m_imageSemWait_sec, m_imageSemWait_nsec);
     929            0 :         XWC_SEM_TIMEDWAIT_LOOP_DERIVED( m_imageSemaphore, ts )
     930              : 
     931              :         //If here, we got an image.  m_rawImage will have been updated
     932            0 :         m_pokeLocal +=  sign*m_rawImage();
     933              : 
     934            0 :         ++n;
     935              :     }
     936              : 
     937            0 :     if(m_stopMeasurement || derived().m_shutdown)
     938              :     {
     939            0 :         m_dmImage.setZero();
     940            0 :         m_dmStream = m_dmImage;
     941            0 :         return 1;
     942              :     }
     943              : 
     944            0 :     return 0;
     945              : }
     946              : 
     947              : template<class derivedT>
     948            0 : int dmPokeWFS<derivedT>::basicRunSensor()
     949              : {
     950              :     int rv;
     951              : 
     952            0 :     if(!m_pokeImage.valid())
     953              :     {
     954            0 :         return derivedT::template log<software_error,-1>({__FILE__, __LINE__, "poke image is not allocated"});
     955              :     }
     956              : 
     957            0 :     m_pokeLocal.setZero();
     958              : 
     959            0 :     for(unsigned nseq = 0; nseq < m_nPokeAverage; ++nseq)
     960              :     {
     961              : 
     962              :         //************** positive POKE **********************/
     963              : 
     964            0 :         rv = basicTimedPoke(+1);
     965              : 
     966            0 :         if(rv < 0)
     967              :         {
     968            0 :             derivedT::template log<software_error>({__FILE__, __LINE__});
     969            0 :             return rv;
     970              :         }
     971            0 :         else if (rv > 0) // shutdown
     972              :         {
     973            0 :             return rv;
     974              :         }
     975              : 
     976            0 :         if(m_stopMeasurement || derived().m_shutdown)
     977              :         {
     978            0 :             break;
     979              :         }
     980              : 
     981              :         //************** NEGATIVE POKE **********************/
     982              : 
     983            0 :         rv = basicTimedPoke(-1);
     984              : 
     985            0 :         if(rv < 0)
     986              :         {
     987            0 :             derivedT::template log<software_error>({__FILE__, __LINE__});
     988            0 :             return rv;
     989              :         }
     990            0 :         else if (rv > 0) // shutdown
     991              :         {
     992            0 :             return rv;
     993              :         }
     994              : 
     995            0 :         if(m_stopMeasurement || derived().m_shutdown)
     996              :         {
     997            0 :             break;
     998              :         }
     999              :     }
    1000              : 
    1001              :     try
    1002              :     {
    1003            0 :         m_pokeImage = m_pokeLocal/(2.0*m_nPokeImages*m_nPokeAverage);
    1004              :     }
    1005            0 :     catch(const std::exception& e)
    1006              :     {
    1007            0 :         return derivedT::template log<software_error,-1>({__FILE__, __LINE__, e.what()});
    1008              :     }
    1009              : 
    1010              : 
    1011              : 
    1012            0 :     m_dmImage.setZero();
    1013            0 :     m_dmStream = m_dmImage;
    1014              : 
    1015            0 :     return 0;
    1016              : }
    1017              : 
    1018              : template<class derivedT>
    1019            0 : int dmPokeWFS<derivedT>::updateMeasurement( float deltaX,
    1020              :                                             float deltaY
    1021              :                                           )
    1022              : {
    1023            0 :     m_deltaX = deltaX;
    1024            0 :     m_deltaY = deltaY;
    1025            0 :     m_indiP_measurement["delta_x"] = deltaX;
    1026            0 :     m_indiP_measurement["delta_y"] = deltaY;
    1027              : 
    1028            0 :     return 0;
    1029              : }
    1030              : 
    1031              : template<class derivedT>
    1032            0 : INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_nPokeImages )(const pcf::IndiProperty &ipRecv)
    1033              : {
    1034            0 :     INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_nPokeImages, ipRecv)
    1035              : 
    1036              :     float target;
    1037              : 
    1038            0 :     if( derived().template indiTargetUpdate(m_indiP_nPokeImages, target, ipRecv, false) < 0)
    1039              :     {
    1040            0 :         return derivedT::template log<software_error,-1>({__FILE__, __LINE__});
    1041              :     }
    1042              : 
    1043            0 :     m_nPokeImages = target;
    1044              : 
    1045            0 :     return 0;
    1046              : }
    1047              : 
    1048              : template<class derivedT>
    1049            0 : INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_nPokeAverage )(const pcf::IndiProperty &ipRecv)
    1050              : {
    1051            0 :     INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_nPokeAverage, ipRecv)
    1052              : 
    1053              :     float target;
    1054              : 
    1055            0 :     if( derived().template indiTargetUpdate(m_indiP_nPokeAverage, target, ipRecv, false) < 0)
    1056              :     {
    1057            0 :         return derivedT::template log<software_error,-1>({__FILE__, __LINE__});
    1058              :     }
    1059              : 
    1060            0 :     m_nPokeAverage = target;
    1061              : 
    1062            0 :     return 0;
    1063              : }
    1064              : 
    1065              : template<class derivedT>
    1066            0 : INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_poke_amp )(const pcf::IndiProperty &ipRecv)
    1067              : {
    1068            0 :     INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_poke_amp, ipRecv)
    1069              : 
    1070              :     float target;
    1071              : 
    1072            0 :     if( derived().template indiTargetUpdate(m_indiP_poke_amp, target, ipRecv, false) < 0)
    1073              :     {
    1074            0 :         return derivedT::template log<software_error,-1>({__FILE__, __LINE__});
    1075              :     }
    1076              : 
    1077            0 :     m_poke_amp = target;
    1078              : 
    1079            0 :     return 0;
    1080              : }
    1081              : 
    1082              : template<class derivedT>
    1083            0 : INDI_SETCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_wfsFps )(const pcf::IndiProperty &ipRecv)
    1084              : {
    1085            0 :     INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_wfsFps, ipRecv)
    1086              : 
    1087            0 :     if( ipRecv.find("current") != true ) //this isn't valid
    1088              :     {
    1089            0 :         return 0;
    1090              :     }
    1091              : 
    1092            0 :     m_wfsFps = ipRecv["current"].get<float>();
    1093              : 
    1094            0 :     return 0;
    1095              : }
    1096              : 
    1097              : template<class derivedT>
    1098            0 : INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_single )(const pcf::IndiProperty &ipRecv)
    1099              : {
    1100            0 :     INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_single, ipRecv)
    1101              : 
    1102            0 :     if( ipRecv.find("toggle") != true ) //this isn't valid
    1103              :     {
    1104            0 :         return -1;
    1105              :     }
    1106              : 
    1107            0 :     if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
    1108              :     {
    1109            0 :         if(m_measuring == 0)
    1110              :         {
    1111            0 :             m_continuous = 0;
    1112            0 :             m_single = 1;
    1113            0 :             if(sem_post(&m_wfsSemaphore) < 0)
    1114              :             {
    1115            0 :                 return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno, 0, "Error posting to semaphore"});
    1116              :             }
    1117              :         }
    1118              :     }
    1119              : 
    1120            0 :     return 0;
    1121              : }
    1122              : 
    1123              : template<class derivedT>
    1124            0 : INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_continuous )(const pcf::IndiProperty &ipRecv)
    1125              : {
    1126            0 :     INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_continuous, ipRecv)
    1127              : 
    1128            0 :     if( ipRecv.find("toggle") != true ) //this isn't valid
    1129              :     {
    1130            0 :         return -1;
    1131              :     }
    1132              : 
    1133            0 :     if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
    1134              :     {
    1135            0 :         if(m_measuring == 0)
    1136              :         {
    1137            0 :             m_continuous = 1;
    1138            0 :             m_single = 0;
    1139            0 :             if(sem_post(&m_wfsSemaphore) < 0)
    1140              :             {
    1141            0 :                 return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno, 0, "Error posting to semaphore"});
    1142              :             }
    1143              :         }
    1144              :     }
    1145            0 :     else if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off )
    1146              :     {
    1147            0 :         if(m_measuring != 0)
    1148              :         {
    1149            0 :             m_stopMeasurement = true;
    1150              :         }
    1151              :     }
    1152              : 
    1153            0 :     return 0;
    1154              : }
    1155              : 
    1156              : template<class derivedT>
    1157            0 : INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_stop )(const pcf::IndiProperty &ipRecv)
    1158              : {
    1159            0 :     INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_stop, ipRecv)
    1160              : 
    1161            0 :     if( ipRecv.find("request") != true ) //this isn't valid
    1162              :     {
    1163            0 :         return -1;
    1164              :     }
    1165              : 
    1166            0 :     if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
    1167              :     {
    1168            0 :         if(m_measuring != 0)
    1169              :         {
    1170            0 :             m_stopMeasurement = true;
    1171              :         }
    1172              :     }
    1173              : 
    1174            0 :     return 0;
    1175              : }
    1176              : 
    1177              : template<class derivedT>
    1178            0 : int dmPokeWFS<derivedT>::recordTelem(const telem_pokeloop *)
    1179              : {
    1180            0 :     return recordPokeLoop(true);
    1181              : }
    1182              : 
    1183              : template<class derivedT>
    1184            0 : int dmPokeWFS<derivedT>::recordPokeLoop(bool force)
    1185              : {
    1186              :     static int measuring = -1;
    1187              :     static float deltaX = std::numeric_limits<float>::max();
    1188              :     static float deltaY = std::numeric_limits<float>::max();
    1189              :     static uint64_t counter = std::numeric_limits<uint64_t>::max();
    1190              : 
    1191            0 :     if(force || (m_counter != counter) || (m_deltaX != deltaX) || (m_deltaY != deltaY) || (m_measuring != measuring))
    1192              :     {
    1193            0 :         uint8_t meas = m_measuring;
    1194            0 :         derived().template telem<telem_pokeloop>({meas, m_deltaX, m_deltaY, m_counter});
    1195              : 
    1196            0 :         measuring = m_measuring;
    1197            0 :         deltaX = m_deltaX;
    1198            0 :         deltaY = m_deltaY;
    1199            0 :         counter = m_counter;
    1200              :     }
    1201              : 
    1202            0 :     return 0;
    1203              : }
    1204              : 
    1205              : /// Call dmPokeWFS::setupConfig with error checking
    1206              : /**
    1207              :   * \param cfig the application configurator
    1208              :   */
    1209              : #define DMPOKEWFS_SETUP_CONFIG( cfig )                                                   \
    1210              :     if(dmPokeWFST::setupConfig(cfig) < 0)                                                \
    1211              :     {                                                                                    \
    1212              :         log<software_error>({__FILE__, __LINE__, "Error from dmPokeWFST::setupConfig"}); \
    1213              :         m_shutdown = true;                                                               \
    1214              :         return;                                                                          \
    1215              :     }
    1216              : 
    1217              : /// Call dmPokeWFS::loadConfig with error checking
    1218              : /** This must be inside a function that returns int, e.g. the standard loadConfigImpl.
    1219              :   * \param cfig the application configurator
    1220              :   */
    1221              : #define DMPOKEWFS_LOAD_CONFIG( cfig )                                                             \
    1222              :     if(dmPokeWFST::loadConfig(cfig) < 0)                                                          \
    1223              :     {                                                                                             \
    1224              :         return log<software_error,-1>({__FILE__, __LINE__, "Error from dmPokeWFST::loadConfig"}); \
    1225              :     }
    1226              : 
    1227              : /// Call dmPokeWFS::appStartup with error checking
    1228              : #define DMPOKEWFS_APP_STARTUP                                \
    1229              :     if( dmPokeWFST::appStartup() < 0)                        \
    1230              :     {                                                        \
    1231              :         return log<software_error, -1>({__FILE__,__LINE__}); \
    1232              :     }
    1233              : 
    1234              : /// Call dmPokeWFS::appLogic with error checking
    1235              : #define DMPOKEWFS_APP_LOGIC                                  \
    1236              :     if( dmPokeWFST::appLogic() < 0)                          \
    1237              :     {                                                        \
    1238              :         return log<software_error, -1>({__FILE__,__LINE__}); \
    1239              :     }
    1240              : 
    1241              : /// Call dmPokeWFS::appShutdown with error checking
    1242              : #define DMPOKEWFS_APP_SHUTDOWN                                                           \
    1243              :     if(dmPokeWFST::appShutdown() < 0)                                                    \
    1244              :     {                                                                                    \
    1245              :         log<software_error>({__FILE__, __LINE__, "error from dmPokeWFST::appShutdown"}); \
    1246              :     }
    1247              : 
    1248              : } //namespace dev
    1249              : } //namespace app
    1250              : } //namespace MagAOX
    1251              : 
    1252              : #endif //dmPokeWFS_hpp
        

Generated by: LCOV version 2.0-1