7#ifndef sparkleClock_hpp
8#define sparkleClock_hpp
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>
17#include "../../libMagAOX/libMagAOX.hpp"
18#include "../../magaox_git_version.h"
125 mx::app::appConfigurator &
_config );
229 config.add(
"dm.channelName",
237 "The name of the DM channel to write to." );
238 config.add(
"dm.triggerChannel",
246 "The name of the DM channel to trigger on." );
247 config.add(
"dm.triggerSemaphore",
249 "dm.triggerSemaphore",
255 "The semaphore to use (default 9)." );
256 config.add(
"dm.trigger",
264 "Run in trigger mode if true (default)." );
265 config.add(
"dm.triggerDelay",
273 "Delay to apply to the trigger." );
275 config.add(
"dm.separation_1",
283 "The radial separation of the first set of speckles (default 10.0)." );
284 config.add(
"dm.separation_2",
292 "The radial separation of the first set of speckles (default 20.0)." );
293 config.add(
"dm.angle",
301 "The angle of the speckle pattern c.c.w. from up on camsci1/2 (default 0.0)." );
302 config.add(
"dm.angleOffset",
310 "The calibration offset of angle so that up on camsci1/2 is 0." );
311 config.add(
"dm.amp",
319 "The speckle amplitude on the DM (default 0.01)." );
320 config.add(
"dm.cross",
328 "If true, also apply the cross speckles rotated by 90 degrees." );
330 config.add(
"dm.frequency",
338 "The frequency to modulate at if not triggering (default 2000 Hz)." );
340 config.add(
"dm.dwell",
348 "The dwell time for each speckle, or for how many frames it is held. Default=1." );
350 config.add(
"modulator.threadPrio",
352 "modulator.threadPrio",
358 "The real-time priority of the modulator thread." );
360 config.add(
"modulator.cpuset",
368 "The cpuset to assign the modulator thread to." );
382 if(
_config.isSet(
"dm.trigger" ) )
395 if(
_config.isSet(
"dm.cross" ) )
420 m_indiP_dm.add( pcf::IndiElement(
"channel" ) );
632 std::cerr <<
"Got nFrames = " <<
nFrames <<
" to be evenly divisible by 4, but integer rounding would give "
634 <<
" Hz frequency\n";
682 mx::fits::fitsFile<realT>
ff;
730 mx::sys::milliSleep( 500 );
801 t0 = mx::sys::get_curr_time();
810 mx::sys::nanoSleep(
dt );
811 t1 = mx::sys::get_curr_time();
836 mx::sys::nanoSleep( 0.5 *
dnsec );
875 if(
modstart.tv_nsec >= 1000000000 )
909(
const pcf::IndiProperty &
ipRecv )
911 if(
ipRecv.getName() != m_indiP_trigger.getName() )
913 log<software_error>( { __FILE__, __LINE__,
"invalid indi property received" } );
917 if( !
ipRecv.find(
"toggle" ) )
920 std::unique_lock<std::mutex>
lock( m_indiMutex );
922 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off )
928 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On )
941 if(
ipRecv.getName() != m_indiP_delay.getName() )
943 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
947 float del = -1000000000;
949 if(
ipRecv.find(
"current" ) )
951 del =
ipRecv[
"current"].get<
float>();
954 if(
ipRecv.find(
"target" ) )
956 del =
ipRecv[
"target"].get<
float>();
959 if( del == -1000000000 )
961 log<software_error>( { __FILE__, __LINE__,
"No requested delay" } );
965 std::unique_lock<std::mutex>
lock( m_indiMutex );
966 m_triggerDelay = del;
975 if(
ipRecv.getName() != m_indiP_separation_1.getName() )
977 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
981 float sep = -1000000000;
983 if(
ipRecv.find(
"current" ) )
985 sep =
ipRecv[
"current"].get<
float>();
988 if(
ipRecv.find(
"target" ) )
990 sep =
ipRecv[
"target"].get<
float>();
993 if( sep == -1000000000 )
995 log<software_error>( { __FILE__, __LINE__,
"No requested separation_1" } );
999 std::unique_lock<std::mutex>
lock( m_indiMutex );
1000 m_separation_1 = sep;
1010 if(
ipRecv.getName() != m_indiP_separation_2.getName() )
1012 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
1016 float sep = -1000000000;
1018 if(
ipRecv.find(
"current" ) )
1020 sep =
ipRecv[
"current"].get<
float>();
1023 if(
ipRecv.find(
"target" ) )
1025 sep =
ipRecv[
"target"].get<
float>();
1028 if( sep == -1000000000 )
1030 log<software_error>( { __FILE__, __LINE__,
"No requested separation_2" } );
1034 std::unique_lock<std::mutex>
lock( m_indiMutex );
1035 m_separation_2 = sep;
1044(
const pcf::IndiProperty &
ipRecv )
1046 if(
ipRecv.getName() != m_indiP_angle.getName() )
1048 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
1052 float ang = -1000000000;
1054 if(
ipRecv.find(
"current" ) )
1056 ang =
ipRecv[
"current"].get<
float>();
1059 if(
ipRecv.find(
"target" ) )
1061 ang =
ipRecv[
"target"].get<
float>();
1064 if( ang == -1000000000 )
1066 log<software_error>( { __FILE__, __LINE__,
"No angle received" } );
1070 std::unique_lock<std::mutex>
lock( m_indiMutex );
1079(
const pcf::IndiProperty &
ipRecv )
1081 if(
ipRecv.getName() != m_indiP_amp.getName() )
1083 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
1087 float amp = -1000000000;
1089 if(
ipRecv.find(
"current" ) )
1091 amp =
ipRecv[
"current"].get<
float>();
1094 if(
ipRecv.find(
"target" ) )
1096 amp =
ipRecv[
"target"].get<
float>();
1099 if( amp == -1000000000 )
1101 log<software_error>( { __FILE__, __LINE__,
"Invalid requested amp: " + std::to_string( amp ) } );
1105 std::unique_lock<std::mutex>
lock( m_indiMutex );
1114(
const pcf::IndiProperty &
ipRecv )
1116 if(
ipRecv.createUniqueKey() != m_indiP_cross.createUniqueKey() )
1118 log<software_error>( { __FILE__, __LINE__,
"invalid indi property received" } );
1122 if( !
ipRecv.find(
"toggle" ) )
1125 std::unique_lock<std::mutex>
lock( m_indiMutex );
1127 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off )
1133 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On )
1145(
const pcf::IndiProperty &
ipRecv )
1147 if(
ipRecv.getName() != m_indiP_frequency.getName() )
1149 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
1155 if(
ipRecv.find(
"current" ) )
1157 freq =
ipRecv[
"current"].get<
float>();
1160 if(
ipRecv.find(
"target" ) )
1162 freq =
ipRecv[
"target"].get<
float>();
1167 log<software_error>( { __FILE__, __LINE__,
"Invalid requested frequency: " + std::to_string( freq ) } );
1171 std::unique_lock<std::mutex>
lock( m_indiMutex );
1181(
const pcf::IndiProperty &
ipRecv )
1183 if(
ipRecv.getName() != m_indiP_interval.getName() )
1185 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
1189 float interval = -1;
1191 if(
ipRecv.find(
"current" ) )
1193 interval =
ipRecv[
"current"].get<
float>();
1196 if(
ipRecv.find(
"target" ) )
1198 interval =
ipRecv[
"target"].get<
float>();
1203 log<software_error>( { __FILE__, __LINE__,
"Invalid requested interval: " + std::to_string( interval ) } );
1207 std::unique_lock<std::mutex>
lock( m_indiMutex );
1217(
const pcf::IndiProperty &
ipRecv )
1219 if(
ipRecv.createUniqueKey() != m_indiP_dwell.createUniqueKey() )
1221 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
1227 if(
ipRecv.find(
"current" ) )
1229 dwell =
ipRecv[
"current"].get<
unsigned>();
1232 if(
ipRecv.find(
"target" ) )
1234 dwell =
ipRecv[
"target"].get<
unsigned>();
1239 log<software_error>( { __FILE__, __LINE__,
"Invalid requested dwell: " + std::to_string( dwell ) } );
1243 std::unique_lock<std::mutex>
lock( m_indiMutex );
1253(
const pcf::IndiProperty &
ipRecv )
1259 if(
ipRecv.find(
"current" ) )
1264 if(
ipRecv.find(
"target" ) )
1269 if( single < -1 || single > 3 )
1271 log<software_error>( { __FILE__, __LINE__,
"Invalid requested dwell: " + std::to_string(
single ) } );
1275 std::unique_lock<std::mutex>
lock( m_indiMutex );
1282(
const pcf::IndiProperty &
ipRecv )
1284 if(
ipRecv.getName() != m_indiP_modulating.getName() )
1286 log<software_error>( { __FILE__, __LINE__,
"invalid indi property received" } );
1290 if( !
ipRecv.find(
"toggle" ) )
1293 std::unique_lock<std::mutex>
lock( m_indiMutex );
1295 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off )
1297 m_modulating =
false;
1301 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On )
1303 m_modulating =
true;
1311(
const pcf::IndiProperty &
ipRecv )
1313 if(
ipRecv.getName() != m_indiP_zero.getName() )
1315 log<software_error>( { __FILE__, __LINE__,
"invalid indi property received" } );
1319 if( m_modulating ==
true )
1325 if( !
ipRecv.find(
"request" ) )
1328 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On )
1330 m_imageStream.md->write = 1;
1332 memset( m_imageStream.array.raw, 0, m_width * m_height * m_typeSize );
1334 clock_gettime( CLOCK_REALTIME, &currtime );
1335 m_imageStream.md->atime = currtime;
1336 m_imageStream.md->writetime = currtime;
1338 m_imageStream.md->cnt0++;
1340 m_imageStream.md->write = 0;
1341 ImageStreamIO_sempost( &m_imageStream, -1 );
1342 log<text_log>(
"zeroed" );
The base-class for MagAO-X applications.
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI property element value if it has changed.
int createStandardIndiRequestSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single request element.
stateCodes::stateCodeT state()
Get the current state code.
int registerIndiPropertyNew(pcf::IndiProperty &prop, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for.
int createStandardIndiToggleSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single toggle element.
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
indiDriver< MagAOXApp > * m_indiDriver
The INDI driver wrapper. Constructed and initialized by execute, which starts and stops communication...
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
int threadStart(std::thread &thrd, bool &thrdInit, pid_t &tpid, pcf::IndiProperty &thProp, int thrdPrio, const std::string &cpuset, const std::string &thrdName, thisPtr *thrdThis, Function &&thrdStart)
Start a thread, using this class's privileges to set priority, etc.
The MagAO-X DM mode commander.
std::thread m_modThread
A separate thread for the modulation.
pcf::IndiProperty m_indiP_separation_1
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
INDI_NEWCALLBACK_DECL(sparkleClock, m_indiP_amp)
INDI_NEWCALLBACK_DECL(sparkleClock, m_indiP_angle)
realT m_separation_2
The radial separation of the second set of speckles (default 20.0)
pcf::IndiProperty m_indiP_cross
pcf::IndiProperty m_indiP_dwell
~sparkleClock() noexcept
D'tor, declared and defined for noexcept.
std::string m_modThreadCpuset
The cpuset for the modulator thread.
size_t m_typeSize
The size of the type, in bytes.
pcf::IndiProperty m_indiP_interval
virtual void setupConfig()
realT m_amp
The speckle amplitude on the DM.
pcf::IndiProperty m_indiP_dm
virtual void loadConfig()
INDI_NEWCALLBACK_DECL(sparkleClock, m_indiP_delay)
pid_t m_modThreadID
Modulate thread PID.
pcf::IndiProperty m_indiP_angle
realT m_interval
time for one complete cycle of the sparkle clock (e.g. exposure time of some camera) (default 1....
pcf::IndiProperty m_indiP_zero
INDI_NEWCALLBACK_DECL(sparkleClock, m_indiP_dwell)
realT m_frequency
The frequency to modulate at if not triggering (default 2000 Hz)
INDI_NEWCALLBACK_DECL(sparkleClock, m_indiP_cross)
friend class sparkleClock_test
int m_modThreadPrio
Priority of the modulator thread, should normally be > 00.
virtual int appLogic()
Implementation of the FSM for sparkleClock.
std::string m_dmTriggerChannel
The DM channel to monitor as a trigger.
INDI_NEWCALLBACK_DECL(sparkleClock, m_indiP_separation_1)
uint32_t m_width
The width of the image.
pcf::IndiProperty m_indiP_amp
realT m_sparkleClockInterval
INDI_NEWCALLBACK_DECL(sparkleClock, m_indiP_single)
pcf::IndiProperty m_indiP_modulating
static void modThreadStart(sparkleClock *d)
Thread starter, called by modThreadStart on thread construction. Calls modThreadExec.
int recordTelem(const telem_sparkleclock *)
virtual int appShutdown()
Shutdown the app.
int m_single
if >= 0 a single frame is non-zero.
INDI_NEWCALLBACK_DECL(sparkleClock, m_indiP_trigger)
unsigned m_dwell
The dwell time for each speckle, or for how many frames it is held.
bool m_cross
If true, also apply the cross speckles rotated by 90 degrees.
bool m_trigger
Run in trigger mode if true (default)
realT m_angleOffset
The calibration offset of angle so that up on camsci1/2 is 0.
void modThreadExec()
Execute the frame grabber main loop.
uint32_t m_height
The height of the image.
pcf::IndiProperty m_indiP_single
pcf::IndiProperty m_modThreadProp
The property to hold the modulator thread details.
INDI_NEWCALLBACK_DECL(sparkleClock, m_indiP_modulating)
int m_triggerSemaphore
The semaphore to use (default 9)
int recordSparkleClock(bool force=false)
pcf::IndiProperty m_indiP_trigger
INDI_NEWCALLBACK_DECL(sparkleClock, m_indiP_frequency)
realT m_separation_1
The radial separation of the first set of speckles (default 10.0)
virtual int appStartup()
Startup function.
uint8_t m_dataType
The ImageStreamIO type code.
INDI_NEWCALLBACK_DECL(sparkleClock, m_indiP_separation_2)
realT m_angle
The angle of the speckle pattern c.c.w. from up on camsci1/2 (default 0.0)
INDI_NEWCALLBACK_DECL(sparkleClock, m_indiP_zero)
mx::improc::eigenCube< realT > m_shapes
bool m_modThreadInit
Synchronizer to ensure f.g. thread initializes before doing dangerous things.
sparkleClock()
Default c'tor.
pcf::IndiProperty m_indiP_separation_2
int generateSparkleClock()
std::string m_dmName
The descriptive name of this dm. Default is the channel name.
pcf::IndiProperty m_indiP_frequency
INDI_NEWCALLBACK_DECL(sparkleClock, m_indiP_interval)
pcf::IndiProperty m_indiP_delay
std::string m_dmChannelName
The name of the DM channel to write to.
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define REG_INDI_NEWPROP_NOCB(prop, propName, type)
Register a NEW INDI property with the class, with no callback.
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
@ READY
The device is ready for operation, but is not operating.
@ CONNECTED
The application has connected to the device or service.
@ NOTCONNECTED
The application is not connected to the device or service.
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
const pcf::IndiProperty & ipRecv
updateIfChanged(m_indiP_angle, "target", m_angle)
std::unique_lock< std::mutex > lock(m_indiMutex)
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
static constexpr logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
A device base class which saves telemetry.
int appShutdown()
Perform telemeter application shutdown.
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
Log entry recording sparkle clock status.
A simple text log, a string-type log.