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

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

Generated by: LCOV version 2.0-1