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

            Line data    Source code
       1              : /** \file dmSpeckle.hpp
       2              :  * \brief The MagAO-X DM speckle maker header file
       3              :  *
       4              :  * \ingroup dmSpeckle_files
       5              :  */
       6              : 
       7              : #ifndef dmSpeckle_hpp
       8              : #define dmSpeckle_hpp
       9              : 
      10              : #include <mx/improc/eigenCube.hpp>
      11              : #include <mx/ioutils/fits/fitsFile.hpp>
      12              : #include <mx/improc/eigenImage.hpp>
      13              : #include <mx/ioutils/stringUtils.hpp>
      14              : #include <mx/sys/timeUtils.hpp>
      15              : #include <mx/sigproc/fourierModes.hpp>
      16              : 
      17              : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
      18              : #include "../../magaox_git_version.h"
      19              : 
      20              : /** \defgroup dmSpeckle
      21              :  * \brief The DM speckle maker app
      22              :  *
      23              :  * Creates a set of fourier modes to generate speckles, then applies them to a DM channel
      24              :  * at a specified rate.
      25              :  *
      26              :  *
      27              :  * <a href="../handbook/operating/software/apps/dmSpeckle.html">Application Documentation</a>
      28              :  *
      29              :  * \ingroup apps
      30              :  *
      31              :  */
      32              : 
      33              : /** \defgroup dmSpeckle_files
      34              :  * \ingroup dmSpeckle
      35              :  */
      36              : 
      37              : namespace MagAOX
      38              : {
      39              : namespace app
      40              : {
      41              : 
      42              : #define SPARKLE 0
      43              : #define ARBCUBE 1
      44              : 
      45              : /// The MagAO-X DM mode commander
      46              : /**
      47              :  * \ingroup dmSpeckle
      48              :  */
      49              : class dmSpeckle : public MagAOXApp<true>, public dev::telemeter<dmSpeckle>
      50              : {
      51              : 
      52              :     typedef float realT;
      53              : 
      54              :     friend class dev::telemeter<dmSpeckle>;
      55              :     friend class dmSpeckle_test;
      56              : 
      57              :   protected:
      58              :     /** \name Configurable Parameters
      59              :      *@{
      60              :      */
      61              : 
      62              :     std::string m_dmName; ///< The descriptive name of this dm. Default is the channel name.
      63              : 
      64              :     std::string m_dmChannelName; ///< The name of the DM channel to write to.
      65              : 
      66              :     std::string m_dmTriggerChannel; ///< The DM channel to monitor as a trigger
      67              : 
      68              :     float m_triggerDelay{ 0 }; // 0.000375- 0.5/2000.;
      69              : 
      70              :     int m_triggerSemaphore{ 9 }; ///< The semaphore to use (default 9)
      71              : 
      72              :     bool m_trigger{ true }; ///< Run in trigger mode if true (default)
      73              : 
      74              :     realT m_separation{ 15.0 }; ///< The radial separation of the speckles (default 15.0)
      75              : 
      76              :     realT m_angle{ 0.0 }; ///< The angle of the speckle pattern c.c.w. from up on camsci1/2 (default 0.0)
      77              : 
      78              :     realT m_angleOffset{ 28.0 }; ///< The calibration offset of angle so that up on camsci1/2 is 0
      79              : 
      80              :     realT m_amp{ 0.01 }; ///< The speckle amplitude on the DM
      81              : 
      82              :     bool m_cross{ true }; ///< If true, also apply the cross speckles rotated by 90 degrees
      83              : 
      84              :     realT m_frequency{ 2000 }; ///< The frequency to modulate at if not triggering (default 2000 Hz)
      85              : 
      86              :     unsigned m_dwell{ 1 }; ///< The dwell time for each speckle, or for how many frames it is held.
      87              : 
      88              :     int m_single{ -1 }; ///< if >= 0 a single frame is non-zero.
      89              : 
      90              :     int m_opMode{ SPARKLE };
      91              : 
      92              :     std::string m_fileName;
      93              : 
      94              :     ///@}
      95              : 
      96              :     mx::improc::eigenCube<realT> m_shapes;
      97              : 
      98              :     IMAGE    m_imageStream;
      99              :     uint32_t m_width{ 0 };  ///< The width of the image
     100              :     uint32_t m_height{ 0 }; ///< The height of the image.
     101              : 
     102              :     IMAGE m_triggerStream;
     103              : 
     104              :     uint8_t m_dataType{ 0 }; ///< The ImageStreamIO type code.
     105              :     size_t  m_typeSize{ 0 }; ///< The size of the type, in bytes.
     106              : 
     107              :     bool m_opened{ true };
     108              :     bool m_restart{ false };
     109              : 
     110              :     bool m_modulating{ false };
     111              : 
     112              :     bool m_restartSp{ false };
     113              : 
     114              :   public:
     115              :     /// Default c'tor.
     116              :     dmSpeckle();
     117              : 
     118              :     /// D'tor, declared and defined for noexcept.
     119            0 :     ~dmSpeckle() noexcept
     120            0 :     {
     121            0 :     }
     122              : 
     123              :     virtual void setupConfig();
     124              : 
     125              :     /// Implementation of loadConfig logic, separated for testing.
     126              :     /** This is called by loadConfig().
     127              :      */
     128              :     int loadConfigImpl(
     129              :         mx::app::appConfigurator &_config /**< [in] an application configuration from which to load values*/ );
     130              : 
     131              :     virtual void loadConfig();
     132              : 
     133              :     /// Startup function
     134              :     /**
     135              :      *
     136              :      */
     137              :     virtual int appStartup();
     138              : 
     139              :     /// Implementation of the FSM for dmSpeckle.
     140              :     /**
     141              :      * \returns 0 on no critical error
     142              :      * \returns -1 on an error requiring shutdown
     143              :      */
     144              :     virtual int appLogic();
     145              : 
     146              :     /// Shutdown the app.
     147              :     /**
     148              :      *
     149              :      */
     150              :     virtual int appShutdown();
     151              : 
     152              :   protected:
     153              :     int generateSpeckles();
     154              : 
     155              :     /** \name Modulator Thread
     156              :      * This thread sends the signal to the dm at the prescribed frequency
     157              :      *
     158              :      * @{
     159              :      */
     160              :     int m_modThreadPrio{ 60 }; ///< Priority of the modulator thread, should normally be > 00.
     161              : 
     162              :     std::string m_modThreadCpuset; ///< The cpuset for the modulator thread.
     163              : 
     164              :     std::thread m_modThread; ///< A separate thread for the modulation
     165              : 
     166              :     bool m_modThreadInit{ true }; ///< Synchronizer to ensure f.g. thread initializes before doing dangerous things.
     167              : 
     168              :     pid_t m_modThreadID{ 0 }; ///< Modulate thread PID.
     169              : 
     170              :     pcf::IndiProperty m_modThreadProp; ///< The property to hold the modulator thread details.
     171              : 
     172              :     /// Thread starter, called by modThreadStart on thread construction.  Calls modThreadExec.
     173              :     static void modThreadStart( dmSpeckle *d /**< [in] a pointer to a dmSpeckle instance (normally this) */ );
     174              : 
     175              :     /// Execute the frame grabber main loop.
     176              :     void modThreadExec();
     177              : 
     178              :     ///@}
     179              : 
     180              :     // INDI:
     181              :   protected:
     182              :     // declare our properties
     183              :     pcf::IndiProperty m_indiP_dm;
     184              :     pcf::IndiProperty m_indiP_trigger;
     185              :     pcf::IndiProperty m_indiP_delay;
     186              :     pcf::IndiProperty m_indiP_separation;
     187              :     pcf::IndiProperty m_indiP_angle;
     188              :     pcf::IndiProperty m_indiP_amp;
     189              :     pcf::IndiProperty m_indiP_cross;
     190              :     pcf::IndiProperty m_indiP_frequency;
     191              :     pcf::IndiProperty m_indiP_dwell;
     192              :     pcf::IndiProperty m_indiP_single;
     193              :     pcf::IndiProperty m_indiP_modulating;
     194              :     pcf::IndiProperty m_indiP_zero;
     195              : 
     196              :   public:
     197            0 :     INDI_NEWCALLBACK_DECL( dmSpeckle, m_indiP_trigger );
     198            0 :     INDI_NEWCALLBACK_DECL( dmSpeckle, m_indiP_delay );
     199            0 :     INDI_NEWCALLBACK_DECL( dmSpeckle, m_indiP_separation );
     200            0 :     INDI_NEWCALLBACK_DECL( dmSpeckle, m_indiP_angle );
     201            0 :     INDI_NEWCALLBACK_DECL( dmSpeckle, m_indiP_cross );
     202            0 :     INDI_NEWCALLBACK_DECL( dmSpeckle, m_indiP_amp );
     203            0 :     INDI_NEWCALLBACK_DECL( dmSpeckle, m_indiP_frequency );
     204            0 :     INDI_NEWCALLBACK_DECL( dmSpeckle, m_indiP_dwell );
     205            0 :     INDI_NEWCALLBACK_DECL( dmSpeckle, m_indiP_single );
     206            0 :     INDI_NEWCALLBACK_DECL( dmSpeckle, m_indiP_modulating );
     207            0 :     INDI_NEWCALLBACK_DECL( dmSpeckle, m_indiP_zero );
     208              : 
     209              :     /** \name Telemeter Interface
     210              :      *
     211              :      * @{
     212              :      */
     213              :     int checkRecordTimes();
     214              : 
     215              :     int recordTelem( const telem_dmspeck * );
     216              : 
     217              :     int recordDmSpeck( bool force = false );
     218              : 
     219              :     ///@}
     220              : };
     221              : 
     222            0 : dmSpeckle::dmSpeckle() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
     223              : {
     224            0 :     return;
     225            0 : }
     226              : 
     227            0 : void dmSpeckle::setupConfig()
     228              : {
     229            0 :     config.add( "dm.channelName",
     230              :                 "",
     231              :                 "dm.channelName",
     232              :                 argType::Required,
     233              :                 "dm",
     234              :                 "channelName",
     235              :                 false,
     236              :                 "string",
     237              :                 "The name of the DM channel to write to." );
     238            0 :     config.add( "dm.triggerChannel",
     239              :                 "",
     240              :                 "dm.triggerChannel",
     241              :                 argType::Required,
     242              :                 "dm",
     243              :                 "triggerChannel",
     244              :                 false,
     245              :                 "string",
     246              :                 "The name of the DM channel to trigger on." );
     247            0 :     config.add( "dm.triggerSemaphore",
     248              :                 "",
     249              :                 "dm.triggerSemaphore",
     250              :                 argType::Required,
     251              :                 "dm",
     252              :                 "triggerSemaphore",
     253              :                 false,
     254              :                 "int",
     255              :                 "The semaphore to use (default 9)." );
     256            0 :     config.add( "dm.trigger",
     257              :                 "",
     258              :                 "dm.trigger",
     259              :                 argType::True,
     260              :                 "dm",
     261              :                 "trigger",
     262              :                 false,
     263              :                 "bool",
     264              :                 "Run in trigger mode if true (default)." );
     265            0 :     config.add( "dm.triggerDelay",
     266              :                 "",
     267              :                 "dm.triggerDelay",
     268              :                 argType::Required,
     269              :                 "dm",
     270              :                 "triggerDelay",
     271              :                 false,
     272              :                 "float",
     273              :                 "Delay to apply to the trigger." );
     274              : 
     275            0 :     config.add( "dm.separation",
     276              :                 "",
     277              :                 "dm.separation",
     278              :                 argType::Required,
     279              :                 "dm",
     280              :                 "separation",
     281              :                 false,
     282              :                 "float",
     283              :                 "The radial separation of the speckles (default 15.0)." );
     284            0 :     config.add( "dm.angle",
     285              :                 "",
     286              :                 "dm.angle",
     287              :                 argType::Required,
     288              :                 "dm",
     289              :                 "angle",
     290              :                 false,
     291              :                 "float",
     292              :                 "The angle of the speckle pattern c.c.w. from up on camsci1/2 (default 0.0)." );
     293            0 :     config.add( "dm.angleOffset",
     294              :                 "",
     295              :                 "dm.angleOffset",
     296              :                 argType::Required,
     297              :                 "dm",
     298              :                 "angleOffset",
     299              :                 false,
     300              :                 "float",
     301              :                 "The calibration offset of angle so that up on camsci1/2 is 0." );
     302            0 :     config.add( "dm.amp",
     303              :                 "",
     304              :                 "dm.amp",
     305              :                 argType::Required,
     306              :                 "dm",
     307              :                 "amp",
     308              :                 false,
     309              :                 "float",
     310              :                 "The speckle amplitude on the DM (default 0.01)." );
     311            0 :     config.add( "dm.cross",
     312              :                 "",
     313              :                 "dm.cross",
     314              :                 argType::True,
     315              :                 "dm",
     316              :                 "cross",
     317              :                 false,
     318              :                 "bool",
     319              :                 "If true, also apply the cross speckles rotated by 90 degrees." );
     320              : 
     321            0 :     config.add( "dm.frequency",
     322              :                 "",
     323              :                 "dm.frequency",
     324              :                 argType::Required,
     325              :                 "dm",
     326              :                 "frequency",
     327              :                 false,
     328              :                 "float",
     329              :                 "The frequency to modulate at if not triggering (default 2000 Hz)." );
     330              : 
     331            0 :     config.add( "dm.dwell",
     332              :                 "",
     333              :                 "dm.dwell",
     334              :                 argType::True,
     335              :                 "dm",
     336              :                 "dwell",
     337              :                 false,
     338              :                 "int",
     339              :                 "The dwell time for each speckle, or for how many frames it is held. Default=1." );
     340              : 
     341            0 :     config.add( "modulator.threadPrio",
     342              :                 "",
     343              :                 "modulator.threadPrio",
     344              :                 argType::Required,
     345              :                 "modulator",
     346              :                 "threadPrio",
     347              :                 false,
     348              :                 "int",
     349              :                 "The real-time priority of the modulator thread." );
     350              : 
     351            0 :     config.add( "modulator.cpuset",
     352              :                 "",
     353              :                 "modulator.cpuset",
     354              :                 argType::Required,
     355              :                 "modulator",
     356              :                 "cpuset",
     357              :                 false,
     358              :                 "string",
     359              :                 "The cpuset to assign the modulator thread to." );
     360            0 :     config.add( "modulator.opMode",
     361              :                 "",
     362              :                 "modulator.opMode",
     363              :                 argType::Required,
     364              :                 "modulator",
     365              :                 "opMode",
     366              :                 false,
     367              :                 "string",
     368              :                 "operation mode, either sparkle (default) or arbcube.  If arbcub modulator.fileName must be set." );
     369            0 :     config.add( "modulator.fileName",
     370              :                 "",
     371              :                 "modulator.fileName",
     372              :                 argType::Required,
     373              :                 "modulator",
     374              :                 "fileName",
     375              :                 false,
     376              :                 "string",
     377              :                 "File name containing the FITS cube to modulate the DM with.  Only valid if opMode==arbcube." );
     378            0 : }
     379              : 
     380            0 : int dmSpeckle::loadConfigImpl( mx::app::appConfigurator &_config )
     381              : {
     382            0 :     _config( m_dmChannelName, "dm.channelName" );
     383              : 
     384            0 :     m_dmName = m_dmChannelName;
     385            0 :     _config( m_dmName, "dm.name" );
     386              : 
     387            0 :     _config( m_dmTriggerChannel, "dm.triggerChannel" );
     388            0 :     _config( m_triggerSemaphore, "dm.triggerSemaphore" );
     389              : 
     390            0 :     if( _config.isSet( "dm.trigger" ) )
     391              :     {
     392            0 :         _config( m_trigger, "dm.trigger" );
     393              :     }
     394              : 
     395            0 :     _config( m_triggerDelay, "dm.triggerDelay" );
     396              : 
     397            0 :     std::string opMode = "sparkle";
     398            0 :     _config( opMode, "modulator.opMode" );
     399              : 
     400            0 :     if( opMode == "arbcube" )
     401              :     {
     402            0 :         m_opMode = ARBCUBE;
     403              :     }
     404              :     else
     405              :     {
     406            0 :         m_opMode = SPARKLE;
     407              :     }
     408            0 :         _config(m_fileName, "modulator.fileName");
     409            0 :         _config( m_separation, "dm.separation" );
     410            0 :         _config( m_angle, "dm.angle" );
     411            0 :         _config( m_angleOffset, "dm.angleOffset" );
     412              : 
     413              : 
     414            0 :     _config( m_amp, "dm.amp" );
     415              : 
     416            0 :     if( _config.isSet( "dm.cross" ) )
     417              :     {
     418            0 :         _config( m_cross, "dm.cross" );
     419              :     }
     420              : 
     421            0 :     _config( m_frequency, "dm.frequency" );
     422            0 :     _config( m_dwell, "dm.dwell" );
     423              : 
     424            0 :     _config( m_modThreadPrio, "modulator.threadPrio" );
     425            0 :     _config( m_modThreadCpuset, "modulator.cpuset" );
     426              : 
     427            0 :     dev::telemeter<dmSpeckle>::loadConfig( _config );
     428            0 :     return 0;
     429            0 : }
     430              : 
     431            0 : void dmSpeckle::loadConfig()
     432              : {
     433            0 :     loadConfigImpl( config );
     434            0 : }
     435              : 
     436            0 : int dmSpeckle::appStartup()
     437              : {
     438              : 
     439            0 :     REG_INDI_NEWPROP_NOCB( m_indiP_dm, "dm", pcf::IndiProperty::Text );
     440            0 :     m_indiP_dm.add( pcf::IndiElement( "name" ) );
     441            0 :     m_indiP_dm["name"] = m_dmName;
     442            0 :     m_indiP_dm.add( pcf::IndiElement( "channel" ) );
     443            0 :     m_indiP_dm["channel"] = m_dmChannelName;
     444              : 
     445            0 :     createStandardIndiNumber<float>( m_indiP_delay, "delay", 0, 0, 1, "%f" );
     446            0 :     m_indiP_delay["current"] = m_triggerDelay;
     447            0 :     m_indiP_delay["target"]  = m_triggerDelay;
     448            0 :     registerIndiPropertyNew( m_indiP_delay, INDI_NEWCALLBACK( m_indiP_delay ) );
     449              : 
     450            0 :     if( m_opMode == SPARKLE )
     451              :     {
     452            0 :         createStandardIndiNumber<float>( m_indiP_separation, "separation", 0, 0, 100, "%f" );
     453            0 :         m_indiP_separation["current"] = m_separation;
     454            0 :         m_indiP_separation["target"]  = m_separation;
     455            0 :         registerIndiPropertyNew( m_indiP_separation, INDI_NEWCALLBACK( m_indiP_separation ) );
     456              : 
     457            0 :         createStandardIndiNumber<float>( m_indiP_angle, "angle", 0, 0, 100, "%f" );
     458            0 :         m_indiP_angle["current"] = m_angle;
     459            0 :         m_indiP_angle["target"]  = m_angle;
     460            0 :         registerIndiPropertyNew( m_indiP_angle, INDI_NEWCALLBACK( m_indiP_angle ) );
     461              : 
     462            0 :         createStandardIndiToggleSw( m_indiP_cross, "cross" );
     463            0 :         if( registerIndiPropertyNew( m_indiP_cross, INDI_NEWCALLBACK( m_indiP_cross ) ) < 0 )
     464              :         {
     465            0 :             log<software_error>( { __FILE__, __LINE__ } );
     466            0 :             return -1;
     467              :         }
     468            0 :         if( m_cross )
     469              :         {
     470            0 :             m_indiP_cross["toggle"] = pcf::IndiElement::On;
     471              :         }
     472              :         else
     473              :         {
     474            0 :             m_indiP_cross["toggle"] = pcf::IndiElement::Off;
     475              :         }
     476              :     }
     477              : 
     478            0 :     createStandardIndiNumber<float>( m_indiP_amp, "amp", -1, 0, 1, "%f" );
     479            0 :     m_indiP_amp["current"] = m_amp;
     480            0 :     m_indiP_amp["target"]  = m_amp;
     481            0 :     registerIndiPropertyNew( m_indiP_amp, INDI_NEWCALLBACK( m_indiP_amp ) );
     482              : 
     483            0 :     createStandardIndiNumber<float>( m_indiP_frequency, "frequency", 0, 0, 10000, "%f" );
     484            0 :     m_indiP_frequency["current"] = m_frequency;
     485            0 :     m_indiP_frequency["target"]  = m_frequency;
     486            0 :     registerIndiPropertyNew( m_indiP_frequency, INDI_NEWCALLBACK( m_indiP_frequency ) );
     487              : 
     488            0 :     createStandardIndiToggleSw( m_indiP_trigger, "trigger" );
     489            0 :     if( registerIndiPropertyNew( m_indiP_trigger, INDI_NEWCALLBACK( m_indiP_trigger ) ) < 0 )
     490              :     {
     491            0 :         log<software_error>( { __FILE__, __LINE__ } );
     492            0 :         return -1;
     493              :     }
     494            0 :     if( m_trigger )
     495              :     {
     496            0 :         m_indiP_trigger["toggle"] = pcf::IndiElement::On;
     497              :     }
     498              :     else
     499              :     {
     500            0 :         m_indiP_trigger["toggle"] = pcf::IndiElement::Off;
     501              :     }
     502              : 
     503            0 :     createStandardIndiNumber<int>( m_indiP_dwell, "dwell", 1, 100, 1, "%d" );
     504            0 :     m_indiP_dwell["current"] = m_dwell;
     505            0 :     m_indiP_dwell["target"]  = m_dwell;
     506            0 :     registerIndiPropertyNew( m_indiP_dwell, INDI_NEWCALLBACK( m_indiP_dwell ) );
     507              : 
     508            0 :     createStandardIndiNumber<int>( m_indiP_single, "single", -1, 3, 1, "%d" );
     509            0 :     m_indiP_single["current"] = m_single;
     510            0 :     m_indiP_single["target"]  = m_single;
     511            0 :     registerIndiPropertyNew( m_indiP_single, INDI_NEWCALLBACK( m_indiP_single ) );
     512              : 
     513            0 :     createStandardIndiToggleSw( m_indiP_modulating, "modulating" );
     514            0 :     if( registerIndiPropertyNew( m_indiP_modulating, INDI_NEWCALLBACK( m_indiP_modulating ) ) < 0 )
     515              :     {
     516            0 :         log<software_error>( { __FILE__, __LINE__ } );
     517            0 :         return -1;
     518              :     }
     519              : 
     520            0 :     createStandardIndiRequestSw( m_indiP_zero, "zero" );
     521            0 :     if( registerIndiPropertyNew( m_indiP_zero, INDI_NEWCALLBACK( m_indiP_zero ) ) < 0 )
     522              :     {
     523            0 :         log<software_error>( { __FILE__, __LINE__ } );
     524            0 :         return -1;
     525              :     }
     526              : 
     527            0 :     if( threadStart( m_modThread,
     528            0 :                      m_modThreadInit,
     529            0 :                      m_modThreadID,
     530            0 :                      m_modThreadProp,
     531              :                      m_modThreadPrio,
     532            0 :                      m_modThreadCpuset,
     533              :                      "modulator",
     534              :                      this,
     535            0 :                      modThreadStart ) < 0 )
     536              :     {
     537            0 :         log<software_critical>( { __FILE__, __LINE__ } );
     538            0 :         return -1;
     539              :     }
     540              : 
     541            0 :     if( dev::telemeter<dmSpeckle>::appStartup() < 0 )
     542              :     {
     543            0 :         return log<software_error, -1>( { __FILE__, __LINE__ } );
     544              :     }
     545              : 
     546            0 :     state( stateCodes::NOTCONNECTED );
     547              : 
     548            0 :     return 0;
     549              : }
     550              : 
     551            0 : int dmSpeckle::appLogic()
     552              : {
     553            0 :     if( state() == stateCodes::NOTCONNECTED )
     554              :     {
     555            0 :         m_opened  = false;
     556            0 :         m_restart = false; // Set this up front, since we're about to restart.
     557              : 
     558            0 :         if( ImageStreamIO_openIm( &m_imageStream, m_dmChannelName.c_str() ) == 0 )
     559              :         {
     560            0 :             if( m_imageStream.md[0].sem < 10 ) ///<\todo this is hardcoded in ImageStreamIO.c -- should be a define
     561              :             {
     562            0 :                 ImageStreamIO_closeIm( &m_imageStream );
     563              :             }
     564              :             else
     565              :             {
     566            0 :                 m_opened = true;
     567              :             }
     568              :         }
     569              : 
     570              :         // Only bother to try if previous worked and we have a spec
     571            0 :         if( m_opened == true && m_dmTriggerChannel != "" )
     572              :         {
     573            0 :             if( ImageStreamIO_openIm( &m_triggerStream, m_dmTriggerChannel.c_str() ) == 0 )
     574              :             {
     575            0 :                 if( m_triggerStream.md[0].sem <
     576              :                     10 ) ///<\todo this is hardcoded in ImageStreamIO.c -- should be a define
     577              :                 {
     578            0 :                     ImageStreamIO_closeIm( &m_triggerStream );
     579            0 :                     m_opened = false;
     580              :                 }
     581              :             }
     582              :         }
     583              : 
     584            0 :         if( m_opened )
     585              :         {
     586            0 :             state( stateCodes::CONNECTED );
     587              :         }
     588              :     }
     589              : 
     590            0 :     if( state() == stateCodes::CONNECTED )
     591              :     {
     592            0 :         m_dataType = m_imageStream.md[0].datatype;
     593            0 :         m_typeSize = ImageStreamIO_typesize( m_dataType );
     594            0 :         m_width    = m_imageStream.md[0].size[0];
     595            0 :         m_height   = m_imageStream.md[0].size[1];
     596              : 
     597            0 :         if( m_dataType != _DATATYPE_FLOAT )
     598              :         {
     599            0 :             return log<text_log, -1>( "Data type of DM channel is not float.", logPrio::LOG_CRITICAL );
     600              :         }
     601              : 
     602            0 :         if( m_typeSize != sizeof( realT ) )
     603              :         {
     604            0 :             return log<text_log, -1>( "Type-size mismatch, realT is not float.", logPrio::LOG_CRITICAL );
     605              :         }
     606              : 
     607            0 :         state( stateCodes::READY );
     608              :     }
     609              : 
     610            0 :     if( telemeter<dmSpeckle>::appLogic() < 0 )
     611              :     {
     612            0 :         log<software_error>( { __FILE__, __LINE__ } );
     613            0 :         return 0;
     614              :     }
     615              : 
     616            0 :     return 0;
     617              : }
     618              : 
     619            0 : int dmSpeckle::appShutdown()
     620              : {
     621            0 :     if( m_modThread.joinable() )
     622              :     {
     623              :         try
     624              :         {
     625            0 :             m_modThread.join(); // this will throw if it was already joined
     626              :         }
     627            0 :         catch( ... )
     628              :         {
     629            0 :         }
     630              :     }
     631              : 
     632            0 :     dev::telemeter<dmSpeckle>::appShutdown();
     633              : 
     634            0 :     return 0;
     635              : }
     636              : 
     637            0 : int dmSpeckle::generateSpeckles()
     638              : {
     639            0 :     if( m_opMode == ARBCUBE )
     640              :     {
     641            0 :         mx::fits::fitsFile<float> ff;
     642            0 :         mx::error_t errc = ff.read( m_shapes, m_fileName ) ;
     643            0 :         if( errc != mx::error_t::noerror )
     644              :         {
     645            0 :             return log<software_critical, -1>( { __FILE__, __LINE__, "no file with that name" } );
     646              :         }
     647              : 
     648            0 :         if( m_shapes.rows() != m_width || m_shapes.cols() != m_height )
     649              :         {
     650            0 :             return log<software_critical, -1>( { __FILE__, __LINE__, "shape cube is not the right size" } );
     651              :         }
     652              : 
     653            0 :         for(int p =0; p < m_shapes.planes(); ++p)
     654              :         {
     655            0 :             m_shapes.image(p) *= (float) m_amp;
     656              :         }
     657            0 :     }
     658              :     else
     659              :     {
     660            0 :         mx::improc::eigenImage<realT> onesp, onespC;
     661            0 :         onesp.resize( m_width, m_height );
     662            0 :         onespC.resize( m_width, m_height );
     663              : 
     664            0 :         m_shapes.resize( m_width, m_height, 4 );
     665              : 
     666            0 :         realT m = m_separation * cos( mx::math::dtor<realT>( -1 * m_angle + m_angleOffset ) );
     667            0 :         realT n = m_separation * sin( mx::math::dtor<realT>( -1 * m_angle + m_angleOffset ) );
     668              : 
     669            0 :         mx::sigproc::makeFourierMode( m_shapes.image( 0 ), m, n, 1 );
     670              : 
     671            0 :         if( m_cross )
     672              :         {
     673            0 :             onesp = m_shapes.image( 0 );
     674            0 :             mx::sigproc::makeFourierMode( m_shapes.image( 0 ), -n, m, 1 );
     675            0 :             m_shapes.image( 0 ) += onesp;
     676              :         }
     677              : 
     678            0 :         m_shapes.image( 0 ) *= m_amp;
     679            0 :         m_shapes.image( 1 ) = -1 * m_shapes.image( 0 );
     680              : 
     681            0 :         mx::sigproc::makeFourierMode( m_shapes.image( 2 ), m, n, -1 );
     682              : 
     683            0 :         if( m_cross )
     684              :         {
     685            0 :             onesp = m_shapes.image( 2 );
     686            0 :             mx::sigproc::makeFourierMode( m_shapes.image( 2 ), -n, m, -1 );
     687            0 :             m_shapes.image( 2 ) += onesp;
     688              :         }
     689              : 
     690            0 :         m_shapes.image( 2 ) *= m_amp;
     691            0 :         m_shapes.image( 3 ) = -m_shapes.image( 2 );
     692              : 
     693            0 :         mx::fits::fitsFile<realT> ff;
     694            0 :         ff.write( "/tmp/specks.fits", m_shapes );
     695              : 
     696            0 :         if( m_single >= 0 )
     697              :         {
     698            0 :             for( int pp = 0; pp < m_shapes.planes(); ++pp )
     699              :             {
     700            0 :                 if( pp != m_single )
     701              :                 {
     702            0 :                     m_shapes.image( pp ) *= 0;
     703              :                 }
     704              :             }
     705              :         }
     706            0 :         updateIfChanged( m_indiP_separation, "current", m_separation );
     707            0 :         updateIfChanged( m_indiP_angle, "current", m_angle );
     708            0 :     }
     709            0 :     updateIfChanged( m_indiP_delay, "current", m_triggerDelay );
     710            0 :     updateIfChanged( m_indiP_amp, "current", m_amp );
     711            0 :     updateIfChanged( m_indiP_frequency, "current", m_frequency );
     712            0 :     updateIfChanged( m_indiP_dwell, "current", m_dwell );
     713            0 :     updateIfChanged( m_indiP_single, "current", m_single );
     714              : 
     715            0 :     return 0;
     716              : }
     717              : 
     718            0 : inline void dmSpeckle::modThreadStart( dmSpeckle *d )
     719              : {
     720            0 :     d->modThreadExec();
     721            0 : }
     722              : 
     723            0 : inline void dmSpeckle::modThreadExec()
     724              : {
     725            0 :     m_modThreadID = syscall( SYS_gettid );
     726              : 
     727              :     // Wait fpr the thread starter to finish initializing this thread.
     728            0 :     while( ( m_modThreadInit == true || state() != stateCodes::READY ) && m_shutdown == 0 )
     729              :     {
     730            0 :         sleep( 1 );
     731              :     }
     732              : 
     733            0 :     while( m_shutdown == 0 )
     734              :     {
     735            0 :         if( !m_modulating && !m_shutdown ) // If we aren't modulating we sleep for 1/2 a second
     736              :         {
     737            0 :             mx::sys::milliSleep( 500 );
     738              :         }
     739              : 
     740            0 :         if( m_modulating && !m_shutdown )
     741              :         {
     742            0 :             m_restartSp = false;
     743            0 :             if(generateSpeckles() < 0)
     744              :             {
     745            0 :                 m_modulating = false;
     746            0 :                 continue;
     747              :             }
     748              : 
     749            0 :             int64_t freqNsec = ( 1.0 / m_frequency ) * 1e9;
     750              :             int64_t dnsec;
     751              : 
     752            0 :             int idx = 0;
     753              : 
     754              :             timespec modstart;
     755              :             timespec currtime;
     756              : 
     757            0 :             bool   triggered = false;
     758            0 :             sem_t *sem       = nullptr;
     759            0 :             if( m_dmTriggerChannel == "" )
     760              :             {
     761            0 :                 m_trigger = false;
     762            0 :                 indi::updateSwitchIfChanged(
     763            0 :                     m_indiP_trigger, "toggle", pcf::IndiElement::Off, m_indiDriver, INDI_IDLE );
     764              :             }
     765            0 :             else if( m_trigger == true )
     766              :             {
     767            0 :                 ImageStreamIO_semflush( &m_triggerStream, m_triggerSemaphore );
     768              : 
     769            0 :                 sem = m_triggerStream.semptr[m_triggerSemaphore]; ///< The semaphore to monitor for new image data
     770              :             }
     771              : 
     772            0 :             log<text_log>( "started modulating", logPrio::LOG_NOTICE );
     773              :             // To send a message
     774            0 :             log<telem_dmspeck>( { m_modulating,
     775            0 :                                   m_trigger,
     776            0 :                                   m_frequency,
     777            0 :                                   std::vector<float>( { m_separation } ),
     778            0 :                                   std::vector<float>( { m_angle } ),
     779            0 :                                   std::vector<float>( { m_amp } ),
     780            0 :                                   std::vector<bool>( { m_cross } ) },
     781              :                                 logPrio::LOG_INFO );
     782              :             // The official record:
     783            0 :             recordDmSpeck( true );
     784              : 
     785            0 :             dnsec = 0;
     786            0 :             clock_gettime( CLOCK_REALTIME, &modstart );
     787              : 
     788            0 :             unsigned dwelled = 0;
     789            0 :             if( m_dwell == 0 )
     790            0 :                 m_dwell = 1;
     791              : 
     792            0 :             float triggerDelay = m_triggerDelay / 1e6;
     793              : 
     794              :             double t0, t1;
     795              : 
     796            0 :             while( m_modulating && !m_restartSp && !m_shutdown )
     797              :             {
     798            0 :                 if( m_trigger )
     799              :                 {
     800              :                     timespec ts;
     801              : 
     802            0 :                     if( clock_gettime( CLOCK_REALTIME, &ts ) < 0 )
     803              :                     {
     804            0 :                         log<software_critical>( { __FILE__, __LINE__, errno, 0, "clock_gettime" } );
     805            0 :                         return;
     806              :                     }
     807              : 
     808            0 :                     ts.tv_sec += 1;
     809              : 
     810            0 :                     if( sem_timedwait( sem, &ts ) == 0 )
     811              :                     {
     812            0 :                         t0 = mx::sys::get_curr_time();
     813            0 :                         t1 = t0;
     814              : 
     815            0 :                         while( t1 - t0 < triggerDelay )
     816              :                         {
     817            0 :                             double dt = ( 1e8 ) * ( triggerDelay -
     818              :                                                     ( t1 - t0 ) ); // This is 0.1 times remaining time, but in nanosecs
     819            0 :                             if( dt <= 0 )
     820            0 :                                 break;
     821            0 :                             mx::sys::nanoSleep( dt );
     822            0 :                             t1 = mx::sys::get_curr_time();
     823              :                         }
     824              : 
     825            0 :                         triggered = true;
     826              :                     }
     827              :                     else
     828              :                     {
     829            0 :                         triggered = false;
     830              : 
     831              :                         // Check for why we timed out
     832            0 :                         if( errno == EINTR )
     833            0 :                             break; // This indicates signal interrupted us, time to restart or shutdown, loop will exit
     834              :                                    // normally if flags set.
     835              : 
     836              :                         // ETIMEDOUT just means we should wait more.
     837              :                         // Otherwise, report an error.
     838            0 :                         if( errno != ETIMEDOUT )
     839              :                         {
     840            0 :                             log<software_error>( { __FILE__, __LINE__, errno, "sem_timedwait" } );
     841            0 :                             break;
     842              :                         }
     843              :                     }
     844              :                 }
     845              :                 else
     846              :                 {
     847            0 :                     mx::sys::nanoSleep( 0.5 * dnsec );
     848            0 :                     clock_gettime( CLOCK_REALTIME, &currtime );
     849              : 
     850            0 :                     dnsec =
     851            0 :                         ( currtime.tv_sec - modstart.tv_sec ) * 1000000000 + ( currtime.tv_nsec - modstart.tv_nsec );
     852            0 :                     triggered = false;
     853              :                 }
     854              : 
     855            0 :                 if( dwelled < m_dwell - 1 )
     856              :                 {
     857            0 :                     ++dwelled;
     858              :                 }
     859            0 :                 else if( dnsec >= freqNsec || triggered )
     860              :                 {
     861              :                     // Do the write
     862            0 :                     dwelled = 0;
     863              : 
     864            0 :                     m_imageStream.md->write = 1;
     865              : 
     866            0 :                     memcpy( m_imageStream.array.raw, m_shapes.image( idx ).data(), m_width * m_height * m_typeSize );
     867              : 
     868            0 :                     m_imageStream.md->atime     = currtime;
     869            0 :                     m_imageStream.md->writetime = currtime;
     870              : 
     871            0 :                     if( !m_trigger || triggerDelay > 0 )
     872              :                     {
     873            0 :                         m_imageStream.md->cnt0++;
     874              :                     }
     875              : 
     876            0 :                     m_imageStream.md->write = 0;
     877            0 :                     ImageStreamIO_sempost( &m_imageStream, -1 );
     878              : 
     879            0 :                     ++idx;
     880            0 :                     if( idx >= m_shapes.planes() )
     881            0 :                         idx = 0;
     882              : 
     883            0 :                     if( !m_trigger )
     884              :                     {
     885            0 :                         modstart.tv_nsec += freqNsec;
     886            0 :                         if( modstart.tv_nsec >= 1000000000 )
     887              :                         {
     888            0 :                             modstart.tv_nsec -= 1000000000;
     889            0 :                             modstart.tv_sec += 1;
     890              :                         }
     891            0 :                         dnsec = freqNsec;
     892              :                     }
     893              :                 }
     894              :             }
     895            0 :             if( m_restartSp )
     896            0 :                 continue;
     897              : 
     898            0 :             recordDmSpeck( true );
     899            0 :             log<text_log>( "stopped modulating", logPrio::LOG_NOTICE );
     900              :             // Always zero when done
     901            0 :             clock_gettime( CLOCK_REALTIME, &currtime );
     902            0 :             m_imageStream.md->write = 1;
     903              : 
     904            0 :             memset( m_imageStream.array.raw, 0.0, m_width * m_height * m_typeSize );
     905              : 
     906            0 :             m_imageStream.md->atime     = currtime;
     907            0 :             m_imageStream.md->writetime = currtime;
     908              : 
     909            0 :             if( !m_trigger )
     910            0 :                 m_imageStream.md->cnt0++;
     911              : 
     912            0 :             m_imageStream.md->write = 0;
     913            0 :             ImageStreamIO_sempost( &m_imageStream, -1 );
     914            0 :             log<text_log>( "zeroed" );
     915              :         }
     916              :     }
     917              : }
     918              : 
     919            0 : INDI_NEWCALLBACK_DEFN( dmSpeckle, m_indiP_trigger )
     920              : ( const pcf::IndiProperty &ipRecv )
     921              : {
     922            0 :     if( ipRecv.getName() != m_indiP_trigger.getName() )
     923              :     {
     924            0 :         log<software_error>( { __FILE__, __LINE__, "invalid indi property received" } );
     925            0 :         return -1;
     926              :     }
     927              : 
     928            0 :     if( !ipRecv.find( "toggle" ) )
     929            0 :         return 0;
     930              : 
     931            0 :     std::unique_lock<std::mutex> lock( m_indiMutex );
     932              : 
     933            0 :     if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off )
     934              :     {
     935            0 :         m_trigger = false;
     936            0 :         indi::updateSwitchIfChanged( m_indiP_trigger, "toggle", pcf::IndiElement::Off, m_indiDriver, INDI_IDLE );
     937              :     }
     938              : 
     939            0 :     if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
     940              :     {
     941            0 :         m_trigger = true;
     942            0 :         indi::updateSwitchIfChanged( m_indiP_trigger, "toggle", pcf::IndiElement::On, m_indiDriver, INDI_OK );
     943              :     }
     944              : 
     945            0 :     m_restartSp = true;
     946              : 
     947            0 :     return 0;
     948            0 : }
     949              : 
     950            0 : INDI_NEWCALLBACK_DEFN( dmSpeckle, m_indiP_delay )( const pcf::IndiProperty &ipRecv )
     951              : {
     952            0 :     if( ipRecv.getName() != m_indiP_delay.getName() )
     953              :     {
     954            0 :         log<software_error>( { __FILE__, __LINE__, "wrong INDI property received." } );
     955            0 :         return -1;
     956              :     }
     957              : 
     958            0 :     float del = -1000000000;
     959              : 
     960            0 :     if( ipRecv.find( "current" ) )
     961              :     {
     962            0 :         del = ipRecv["current"].get<float>();
     963              :     }
     964              : 
     965            0 :     if( ipRecv.find( "target" ) )
     966              :     {
     967            0 :         del = ipRecv["target"].get<float>();
     968              :     }
     969              : 
     970            0 :     if( del == -1000000000 )
     971              :     {
     972            0 :         log<software_error>( { __FILE__, __LINE__, "No requested delay" } );
     973            0 :         return 0;
     974              :     }
     975              : 
     976            0 :     std::unique_lock<std::mutex> lock( m_indiMutex );
     977            0 :     m_triggerDelay = del;
     978            0 :     updateIfChanged( m_indiP_delay, "target", m_triggerDelay );
     979              : 
     980            0 :     m_restartSp = true;
     981            0 :     return 0;
     982            0 : }
     983              : 
     984            0 : INDI_NEWCALLBACK_DEFN( dmSpeckle, m_indiP_separation )( const pcf::IndiProperty &ipRecv )
     985              : {
     986            0 :     if( ipRecv.getName() != m_indiP_separation.getName() )
     987              :     {
     988            0 :         log<software_error>( { __FILE__, __LINE__, "wrong INDI property received." } );
     989            0 :         return -1;
     990              :     }
     991              : 
     992            0 :     float sep = -1000000000;
     993              : 
     994            0 :     if( ipRecv.find( "current" ) )
     995              :     {
     996            0 :         sep = ipRecv["current"].get<float>();
     997              :     }
     998              : 
     999            0 :     if( ipRecv.find( "target" ) )
    1000              :     {
    1001            0 :         sep = ipRecv["target"].get<float>();
    1002              :     }
    1003              : 
    1004            0 :     if( sep == -1000000000 )
    1005              :     {
    1006            0 :         log<software_error>( { __FILE__, __LINE__, "No requested separation" } );
    1007            0 :         return 0;
    1008              :     }
    1009              : 
    1010            0 :     std::unique_lock<std::mutex> lock( m_indiMutex );
    1011            0 :     m_separation = sep;
    1012            0 :     updateIfChanged( m_indiP_separation, "target", m_separation );
    1013              : 
    1014            0 :     m_restartSp = true;
    1015              : 
    1016            0 :     return 0;
    1017            0 : }
    1018              : 
    1019            0 : INDI_NEWCALLBACK_DEFN( dmSpeckle, m_indiP_angle )
    1020              : ( const pcf::IndiProperty &ipRecv )
    1021              : {
    1022            0 :     if( ipRecv.getName() != m_indiP_angle.getName() )
    1023              :     {
    1024            0 :         log<software_error>( { __FILE__, __LINE__, "wrong INDI property received." } );
    1025            0 :         return -1;
    1026              :     }
    1027              : 
    1028            0 :     float ang = -1000000000;
    1029              : 
    1030            0 :     if( ipRecv.find( "current" ) )
    1031              :     {
    1032            0 :         ang = ipRecv["current"].get<float>();
    1033              :     }
    1034              : 
    1035            0 :     if( ipRecv.find( "target" ) )
    1036              :     {
    1037            0 :         ang = ipRecv["target"].get<float>();
    1038              :     }
    1039              : 
    1040            0 :     if( ang == -1000000000 )
    1041              :     {
    1042            0 :         log<software_error>( { __FILE__, __LINE__, "No angle received" } );
    1043            0 :         return 0;
    1044              :     }
    1045              : 
    1046            0 :     std::unique_lock<std::mutex> lock( m_indiMutex );
    1047            0 :     m_angle = ang;
    1048            0 :     updateIfChanged( m_indiP_angle, "target", m_angle );
    1049              : 
    1050            0 :     m_restartSp = true;
    1051            0 :     return 0;
    1052            0 : }
    1053              : 
    1054            0 : INDI_NEWCALLBACK_DEFN( dmSpeckle, m_indiP_amp )
    1055              : ( const pcf::IndiProperty &ipRecv )
    1056              : {
    1057            0 :     if( ipRecv.getName() != m_indiP_amp.getName() )
    1058              :     {
    1059            0 :         log<software_error>( { __FILE__, __LINE__, "wrong INDI property received." } );
    1060            0 :         return -1;
    1061              :     }
    1062              : 
    1063            0 :     float amp = -1000000000;
    1064              : 
    1065            0 :     if( ipRecv.find( "current" ) )
    1066              :     {
    1067            0 :         amp = ipRecv["current"].get<float>();
    1068              :     }
    1069              : 
    1070            0 :     if( ipRecv.find( "target" ) )
    1071              :     {
    1072            0 :         amp = ipRecv["target"].get<float>();
    1073              :     }
    1074              : 
    1075            0 :     if( amp == -1000000000 )
    1076              :     {
    1077            0 :         log<software_error>( { __FILE__, __LINE__, "Invalid requested amp: " + std::to_string( amp ) } );
    1078            0 :         return 0;
    1079              :     }
    1080              : 
    1081            0 :     std::unique_lock<std::mutex> lock( m_indiMutex );
    1082            0 :     m_amp = amp;
    1083            0 :     updateIfChanged( m_indiP_amp, "target", m_amp );
    1084              : 
    1085            0 :     m_restartSp = true;
    1086            0 :     return 0;
    1087            0 : }
    1088              : 
    1089            0 : INDI_NEWCALLBACK_DEFN( dmSpeckle, m_indiP_cross )
    1090              : ( const pcf::IndiProperty &ipRecv )
    1091              : {
    1092            0 :     if( ipRecv.createUniqueKey() != m_indiP_cross.createUniqueKey() )
    1093              :     {
    1094            0 :         log<software_error>( { __FILE__, __LINE__, "invalid indi property received" } );
    1095            0 :         return -1;
    1096              :     }
    1097              : 
    1098            0 :     if( !ipRecv.find( "toggle" ) )
    1099            0 :         return 0;
    1100              : 
    1101            0 :     std::unique_lock<std::mutex> lock( m_indiMutex );
    1102              : 
    1103            0 :     if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off )
    1104              :     {
    1105            0 :         m_cross = false;
    1106            0 :         indi::updateSwitchIfChanged( m_indiP_cross, "toggle", pcf::IndiElement::Off, m_indiDriver, INDI_IDLE );
    1107              :     }
    1108              : 
    1109            0 :     if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
    1110              :     {
    1111            0 :         m_cross = true;
    1112            0 :         indi::updateSwitchIfChanged( m_indiP_cross, "toggle", pcf::IndiElement::On, m_indiDriver, INDI_OK );
    1113              :     }
    1114              : 
    1115            0 :     m_restartSp = true;
    1116              : 
    1117            0 :     return 0;
    1118            0 : }
    1119              : 
    1120            0 : INDI_NEWCALLBACK_DEFN( dmSpeckle, m_indiP_frequency )
    1121              : ( const pcf::IndiProperty &ipRecv )
    1122              : {
    1123            0 :     if( ipRecv.getName() != m_indiP_frequency.getName() )
    1124              :     {
    1125            0 :         log<software_error>( { __FILE__, __LINE__, "wrong INDI property received." } );
    1126            0 :         return -1;
    1127              :     }
    1128              : 
    1129            0 :     float freq = -1;
    1130              : 
    1131            0 :     if( ipRecv.find( "current" ) )
    1132              :     {
    1133            0 :         freq = ipRecv["current"].get<float>();
    1134              :     }
    1135              : 
    1136            0 :     if( ipRecv.find( "target" ) )
    1137              :     {
    1138            0 :         freq = ipRecv["target"].get<float>();
    1139              :     }
    1140              : 
    1141            0 :     if( freq < 0 )
    1142              :     {
    1143            0 :         log<software_error>( { __FILE__, __LINE__, "Invalid requested frequency: " + std::to_string( freq ) } );
    1144            0 :         return 0;
    1145              :     }
    1146              : 
    1147            0 :     std::unique_lock<std::mutex> lock( m_indiMutex );
    1148            0 :     m_frequency = freq;
    1149            0 :     updateIfChanged( m_indiP_frequency, "target", m_frequency );
    1150              : 
    1151            0 :     m_restartSp = true;
    1152              : 
    1153            0 :     return 0;
    1154            0 : }
    1155              : 
    1156            0 : INDI_NEWCALLBACK_DEFN( dmSpeckle, m_indiP_dwell )
    1157              : ( const pcf::IndiProperty &ipRecv )
    1158              : {
    1159            0 :     if( ipRecv.createUniqueKey() != m_indiP_dwell.createUniqueKey() )
    1160              :     {
    1161            0 :         log<software_error>( { __FILE__, __LINE__, "wrong INDI property received." } );
    1162            0 :         return -1;
    1163              :     }
    1164              : 
    1165            0 :     unsigned dwell = 0;
    1166              : 
    1167            0 :     if( ipRecv.find( "current" ) )
    1168              :     {
    1169            0 :         dwell = ipRecv["current"].get<unsigned>();
    1170              :     }
    1171              : 
    1172            0 :     if( ipRecv.find( "target" ) )
    1173              :     {
    1174            0 :         dwell = ipRecv["target"].get<unsigned>();
    1175              :     }
    1176              : 
    1177            0 :     if( dwell == 0 )
    1178              :     {
    1179            0 :         log<software_error>( { __FILE__, __LINE__, "Invalid requested dwell: " + std::to_string( dwell ) } );
    1180            0 :         return 0;
    1181              :     }
    1182              : 
    1183            0 :     std::unique_lock<std::mutex> lock( m_indiMutex );
    1184            0 :     m_dwell = dwell;
    1185            0 :     updateIfChanged( m_indiP_dwell, "target", m_dwell );
    1186              : 
    1187            0 :     m_restartSp = true;
    1188              : 
    1189            0 :     return 0;
    1190            0 : }
    1191              : 
    1192            0 : INDI_NEWCALLBACK_DEFN( dmSpeckle, m_indiP_single )
    1193              : ( const pcf::IndiProperty &ipRecv )
    1194              : {
    1195            0 :     INDI_VALIDATE_CALLBACK_PROPS( m_indiP_single, ipRecv );
    1196              : 
    1197            0 :     int single = 0;
    1198              : 
    1199            0 :     if( ipRecv.find( "current" ) )
    1200              :     {
    1201            0 :         single = ipRecv["current"].get<int>();
    1202              :     }
    1203              : 
    1204            0 :     if( ipRecv.find( "target" ) )
    1205              :     {
    1206            0 :         single = ipRecv["target"].get<int>();
    1207              :     }
    1208              : 
    1209            0 :     if( single < -1 || single > 3 )
    1210              :     {
    1211            0 :         log<software_error>( { __FILE__, __LINE__, "Invalid requested dwell: " + std::to_string( single ) } );
    1212            0 :         return 0;
    1213              :     }
    1214              : 
    1215            0 :     std::unique_lock<std::mutex> lock( m_indiMutex );
    1216            0 :     m_single = single;
    1217            0 :     updateIfChanged( m_indiP_single, "target", m_single );
    1218            0 :     return 0;
    1219            0 : }
    1220              : 
    1221            0 : INDI_NEWCALLBACK_DEFN( dmSpeckle, m_indiP_modulating )
    1222              : ( const pcf::IndiProperty &ipRecv )
    1223              : {
    1224            0 :     if( ipRecv.getName() != m_indiP_modulating.getName() )
    1225              :     {
    1226            0 :         log<software_error>( { __FILE__, __LINE__, "invalid indi property received" } );
    1227            0 :         return -1;
    1228              :     }
    1229              : 
    1230            0 :     if( !ipRecv.find( "toggle" ) )
    1231            0 :         return 0;
    1232              : 
    1233            0 :     std::unique_lock<std::mutex> lock( m_indiMutex );
    1234              : 
    1235            0 :     if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off )
    1236              :     {
    1237            0 :         m_modulating = false;
    1238            0 :         indi::updateSwitchIfChanged( m_indiP_modulating, "toggle", pcf::IndiElement::Off, m_indiDriver, INDI_IDLE );
    1239              :     }
    1240              : 
    1241            0 :     if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
    1242              :     {
    1243            0 :         m_modulating = true;
    1244            0 :         indi::updateSwitchIfChanged( m_indiP_modulating, "toggle", pcf::IndiElement::On, m_indiDriver, INDI_OK );
    1245              :     }
    1246              : 
    1247            0 :     return 0;
    1248            0 : }
    1249              : 
    1250            0 : INDI_NEWCALLBACK_DEFN( dmSpeckle, m_indiP_zero )
    1251              : ( const pcf::IndiProperty &ipRecv )
    1252              : {
    1253            0 :     if( ipRecv.getName() != m_indiP_zero.getName() )
    1254              :     {
    1255            0 :         log<software_error>( { __FILE__, __LINE__, "invalid indi property received" } );
    1256            0 :         return -1;
    1257              :     }
    1258              : 
    1259            0 :     if( m_modulating == true )
    1260              :     {
    1261            0 :         log<text_log>( "zero requested but currently modulating", logPrio::LOG_NOTICE );
    1262            0 :         return 0;
    1263              :     }
    1264              : 
    1265            0 :     if( !ipRecv.find( "request" ) )
    1266            0 :         return 0;
    1267              : 
    1268            0 :     if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
    1269              :     {
    1270            0 :         m_imageStream.md->write = 1;
    1271              : 
    1272            0 :         memset( m_imageStream.array.raw, 0, m_width * m_height * m_typeSize );
    1273              :         timespec currtime;
    1274            0 :         clock_gettime( CLOCK_REALTIME, &currtime );
    1275            0 :         m_imageStream.md->atime     = currtime;
    1276            0 :         m_imageStream.md->writetime = currtime;
    1277              : 
    1278            0 :         m_imageStream.md->cnt0++;
    1279              : 
    1280            0 :         m_imageStream.md->write = 0;
    1281            0 :         ImageStreamIO_sempost( &m_imageStream, -1 );
    1282            0 :         log<text_log>( "zeroed" );
    1283              :     }
    1284              : 
    1285            0 :     return 0;
    1286              : }
    1287              : 
    1288            0 : inline int dmSpeckle::checkRecordTimes()
    1289              : {
    1290            0 :     return telemeter<dmSpeckle>::checkRecordTimes( telem_dmspeck() );
    1291              : }
    1292              : 
    1293            0 : inline int dmSpeckle::recordTelem( const telem_dmspeck * )
    1294              : {
    1295            0 :     return recordDmSpeck( true );
    1296              : }
    1297              : 
    1298            0 : inline int dmSpeckle::recordDmSpeck( bool force )
    1299              : {
    1300            0 :     static bool  lastModulating = m_modulating;
    1301            0 :     static bool  lastTrigger    = m_trigger;
    1302            0 :     static float lastFrequency  = m_frequency;
    1303            0 :     static float lastSeparation = m_separation;
    1304            0 :     static float lastAngle      = m_angle;
    1305            0 :     static float lastAmp        = m_amp;
    1306            0 :     static bool  lastCross      = m_cross;
    1307              : 
    1308            0 :     if( !( lastModulating == m_modulating ) || !( lastTrigger == m_trigger ) || !( lastFrequency == m_frequency ) ||
    1309            0 :         !( lastSeparation == m_separation ) || !( lastAngle == m_angle ) || !( lastAmp == m_amp ) ||
    1310            0 :         !( lastCross == m_cross ) || force )
    1311              :     {
    1312            0 :         telem<telem_dmspeck>( { m_modulating,
    1313            0 :                                 m_trigger,
    1314            0 :                                 m_frequency,
    1315            0 :                                 std::vector<float>( { m_separation } ),
    1316            0 :                                 std::vector<float>( { m_angle } ),
    1317            0 :                                 std::vector<float>( { m_amp } ),
    1318            0 :                                 std::vector<bool>( { m_cross } ) } );
    1319              : 
    1320            0 :         lastModulating = m_modulating;
    1321            0 :         lastTrigger    = m_trigger;
    1322            0 :         lastFrequency  = m_frequency;
    1323            0 :         lastSeparation = m_separation;
    1324            0 :         lastAngle      = m_angle;
    1325            0 :         lastAmp        = m_amp;
    1326            0 :         lastCross      = m_cross;
    1327              :     }
    1328              : 
    1329            0 :     return 0;
    1330              : }
    1331              : 
    1332              : } // namespace app
    1333              : } // namespace MagAOX
    1334              : 
    1335              : #endif // dmSpeckle_hpp
        

Generated by: LCOV version 2.0-1