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"
129 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",
283 "The radial separation of the speckles (default 15.0)." );
284 config.add(
"dm.angle",
292 "The angle of the speckle pattern c.c.w. from up on camsci1/2 (default 0.0)." );
293 config.add(
"dm.angleOffset",
301 "The calibration offset of angle so that up on camsci1/2 is 0." );
302 config.add(
"dm.amp",
310 "The speckle amplitude on the DM (default 0.01)." );
311 config.add(
"dm.cross",
319 "If true, also apply the cross speckles rotated by 90 degrees." );
321 config.add(
"dm.frequency",
329 "The frequency to modulate at if not triggering (default 2000 Hz)." );
331 config.add(
"dm.dwell",
339 "The dwell time for each speckle, or for how many frames it is held. Default=1." );
341 config.add(
"modulator.threadPrio",
343 "modulator.threadPrio",
349 "The real-time priority of the modulator thread." );
351 config.add(
"modulator.cpuset",
359 "The cpuset to assign the modulator thread to." );
360 config.add(
"modulator.opMode",
368 "operation mode, either sparkle (default) or arbcube. If arbcub modulator.fileName must be set." );
369 config.add(
"modulator.fileName",
371 "modulator.fileName",
377 "File name containing the FITS cube to modulate the DM with. Only valid if opMode==arbcube." );
390 if(
_config.isSet(
"dm.trigger" ) )
397 std::string
opMode =
"sparkle";
416 if(
_config.isSet(
"dm.cross" ) )
442 m_indiP_dm.add( pcf::IndiElement(
"channel" ) );
641 mx::fits::fitsFile<float>
ff;
652 for(
int p =0; p <
m_shapes.planes(); ++p)
668 mx::sigproc::makeFourierMode(
m_shapes.image( 0 ),
m,
n, 1 );
673 mx::sigproc::makeFourierMode(
m_shapes.image( 0 ), -
n,
m, 1 );
680 mx::sigproc::makeFourierMode(
m_shapes.image( 2 ),
m,
n, -1 );
685 mx::sigproc::makeFourierMode(
m_shapes.image( 2 ), -
n,
m, -1 );
692 mx::fits::fitsFile<realT>
ff;
736 mx::sys::milliSleep( 500 );
773 std::vector<float>( {
m_angle } ),
774 std::vector<float>( {
m_amp } ),
775 std::vector<bool>( {
m_cross } ) },
807 t0 = mx::sys::get_curr_time();
816 mx::sys::nanoSleep(
dt );
817 t1 = mx::sys::get_curr_time();
842 mx::sys::nanoSleep( 0.5 *
dnsec );
881 if(
modstart.tv_nsec >= 1000000000 )
915(
const pcf::IndiProperty &
ipRecv )
917 if(
ipRecv.getName() != m_indiP_trigger.getName() )
919 log<software_error>( { __FILE__, __LINE__,
"invalid indi property received" } );
923 if( !
ipRecv.find(
"toggle" ) )
926 std::unique_lock<std::mutex>
lock( m_indiMutex );
928 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off )
934 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On )
947 if(
ipRecv.getName() != m_indiP_delay.getName() )
949 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
953 float del = -1000000000;
955 if(
ipRecv.find(
"current" ) )
957 del =
ipRecv[
"current"].get<
float>();
960 if(
ipRecv.find(
"target" ) )
962 del =
ipRecv[
"target"].get<
float>();
965 if( del == -1000000000 )
967 log<software_error>( { __FILE__, __LINE__,
"No requested delay" } );
971 std::unique_lock<std::mutex>
lock( m_indiMutex );
972 m_triggerDelay = del;
981 if(
ipRecv.getName() != m_indiP_separation.getName() )
983 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
987 float sep = -1000000000;
989 if(
ipRecv.find(
"current" ) )
991 sep =
ipRecv[
"current"].get<
float>();
994 if(
ipRecv.find(
"target" ) )
996 sep =
ipRecv[
"target"].get<
float>();
999 if( sep == -1000000000 )
1001 log<software_error>( { __FILE__, __LINE__,
"No requested separation" } );
1005 std::unique_lock<std::mutex>
lock( m_indiMutex );
1015(
const pcf::IndiProperty &
ipRecv )
1017 if(
ipRecv.getName() != m_indiP_angle.getName() )
1019 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
1023 float ang = -1000000000;
1027 ang =
ipRecv[
"current"].get<
float>();
1032 ang =
ipRecv[
"target"].get<
float>();
1035 if( ang == -1000000000 )
1037 log<software_error>( { __FILE__, __LINE__,
"No angle received" } );
1041 std::unique_lock<std::mutex>
lock( m_indiMutex );
1050(
const pcf::IndiProperty &
ipRecv )
1052 if(
ipRecv.getName() != m_indiP_amp.getName() )
1054 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
1058 float amp = -1000000000;
1060 if(
ipRecv.find(
"current" ) )
1062 amp =
ipRecv[
"current"].get<
float>();
1065 if(
ipRecv.find(
"target" ) )
1067 amp =
ipRecv[
"target"].get<
float>();
1070 if( amp == -1000000000 )
1072 log<software_error>( { __FILE__, __LINE__,
"Invalid requested amp: " + std::to_string( amp ) } );
1076 std::unique_lock<std::mutex>
lock( m_indiMutex );
1085(
const pcf::IndiProperty &
ipRecv )
1087 if(
ipRecv.createUniqueKey() != m_indiP_cross.createUniqueKey() )
1089 log<software_error>( { __FILE__, __LINE__,
"invalid indi property received" } );
1093 if( !
ipRecv.find(
"toggle" ) )
1096 std::unique_lock<std::mutex>
lock( m_indiMutex );
1098 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off )
1104 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On )
1116(
const pcf::IndiProperty &
ipRecv )
1118 if(
ipRecv.getName() != m_indiP_frequency.getName() )
1120 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
1126 if(
ipRecv.find(
"current" ) )
1128 freq =
ipRecv[
"current"].get<
float>();
1131 if(
ipRecv.find(
"target" ) )
1133 freq =
ipRecv[
"target"].get<
float>();
1138 log<software_error>( { __FILE__, __LINE__,
"Invalid requested frequency: " + std::to_string( freq ) } );
1142 std::unique_lock<std::mutex>
lock( m_indiMutex );
1152(
const pcf::IndiProperty &
ipRecv )
1154 if(
ipRecv.createUniqueKey() != m_indiP_dwell.createUniqueKey() )
1156 log<software_error>( { __FILE__, __LINE__,
"wrong INDI property received." } );
1162 if(
ipRecv.find(
"current" ) )
1164 dwell =
ipRecv[
"current"].get<
unsigned>();
1167 if(
ipRecv.find(
"target" ) )
1169 dwell =
ipRecv[
"target"].get<
unsigned>();
1174 log<software_error>( { __FILE__, __LINE__,
"Invalid requested dwell: " + std::to_string( dwell ) } );
1178 std::unique_lock<std::mutex>
lock( m_indiMutex );
1188(
const pcf::IndiProperty &
ipRecv )
1194 if(
ipRecv.find(
"current" ) )
1199 if(
ipRecv.find(
"target" ) )
1204 if( single < -1 || single > 3 )
1206 log<software_error>( { __FILE__, __LINE__,
"Invalid requested dwell: " + std::to_string(
single ) } );
1210 std::unique_lock<std::mutex>
lock( m_indiMutex );
1217(
const pcf::IndiProperty &
ipRecv )
1219 if(
ipRecv.getName() != m_indiP_modulating.getName() )
1221 log<software_error>( { __FILE__, __LINE__,
"invalid indi property received" } );
1225 if( !
ipRecv.find(
"toggle" ) )
1228 std::unique_lock<std::mutex>
lock( m_indiMutex );
1230 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off )
1232 m_modulating =
false;
1236 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On )
1238 m_modulating =
true;
1246(
const pcf::IndiProperty &
ipRecv )
1248 if(
ipRecv.getName() != m_indiP_zero.getName() )
1250 log<software_error>( { __FILE__, __LINE__,
"invalid indi property received" } );
1254 if( m_modulating ==
true )
1260 if( !
ipRecv.find(
"request" ) )
1263 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On )
1265 m_imageStream.md->write = 1;
1267 memset( m_imageStream.array.raw, 0, m_width * m_height * m_typeSize );
1269 clock_gettime( CLOCK_REALTIME, &currtime );
1270 m_imageStream.md->atime = currtime;
1271 m_imageStream.md->writetime = currtime;
1273 m_imageStream.md->cnt0++;
1275 m_imageStream.md->write = 0;
1276 ImageStreamIO_sempost( &m_imageStream, -1 );
1277 log<text_log>(
"zeroed" );
1311 std::vector<float>( {
m_angle } ),
1312 std::vector<float>( {
m_amp } ),
1313 std::vector<bool>( {
m_cross } ) } );
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.
pid_t m_modThreadID
Modulate thread PID.
INDI_NEWCALLBACK_DECL(dmSpeckle, m_indiP_delay)
virtual void loadConfig()
static void modThreadStart(dmSpeckle *d)
Thread starter, called by modThreadStart on thread construction. Calls modThreadExec.
friend class dmSpeckle_test
unsigned m_dwell
The dwell time for each speckle, or for how many frames it is held.
INDI_NEWCALLBACK_DECL(dmSpeckle, m_indiP_trigger)
pcf::IndiProperty m_indiP_modulating
realT m_angle
The angle of the speckle pattern c.c.w. from up on camsci1/2 (default 0.0)
bool m_modThreadInit
Synchronizer to ensure f.g. thread initializes before doing dangerous things.
~dmSpeckle() noexcept
D'tor, declared and defined for noexcept.
INDI_NEWCALLBACK_DECL(dmSpeckle, m_indiP_angle)
uint32_t m_width
The width of the image.
INDI_NEWCALLBACK_DECL(dmSpeckle, m_indiP_amp)
size_t m_typeSize
The size of the type, in bytes.
int m_triggerSemaphore
The semaphore to use (default 9)
pcf::IndiProperty m_indiP_trigger
mx::improc::eigenCube< realT > m_shapes
void modThreadExec()
Execute the frame grabber main loop.
virtual int appStartup()
Startup function.
bool m_trigger
Run in trigger mode if true (default)
virtual int appShutdown()
Shutdown the app.
pcf::IndiProperty m_indiP_cross
std::string m_dmTriggerChannel
The DM channel to monitor as a trigger.
uint32_t m_height
The height of the image.
realT m_angleOffset
The calibration offset of angle so that up on camsci1/2 is 0.
virtual int appLogic()
Implementation of the FSM for dmSpeckle.
pcf::IndiProperty m_indiP_single
INDI_NEWCALLBACK_DECL(dmSpeckle, m_indiP_separation)
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
realT m_amp
The speckle amplitude on the DM.
pcf::IndiProperty m_indiP_frequency
INDI_NEWCALLBACK_DECL(dmSpeckle, m_indiP_single)
int m_modThreadPrio
Priority of the modulator thread, should normally be > 00.
pcf::IndiProperty m_indiP_angle
bool m_cross
If true, also apply the cross speckles rotated by 90 degrees.
std::thread m_modThread
A separate thread for the modulation.
pcf::IndiProperty m_indiP_amp
pcf::IndiProperty m_indiP_zero
pcf::IndiProperty m_indiP_separation
realT m_separation
The radial separation of the speckles (default 15.0)
pcf::IndiProperty m_indiP_dm
pcf::IndiProperty m_indiP_delay
INDI_NEWCALLBACK_DECL(dmSpeckle, m_indiP_zero)
dmSpeckle()
Default c'tor.
virtual void setupConfig()
std::string m_modThreadCpuset
The cpuset for the modulator thread.
int recordTelem(const telem_dmspeck *)
uint8_t m_dataType
The ImageStreamIO type code.
INDI_NEWCALLBACK_DECL(dmSpeckle, m_indiP_frequency)
pcf::IndiProperty m_modThreadProp
The property to hold the modulator thread details.
std::string m_dmName
The descriptive name of this dm. Default is the channel name.
INDI_NEWCALLBACK_DECL(dmSpeckle, m_indiP_cross)
INDI_NEWCALLBACK_DECL(dmSpeckle, m_indiP_dwell)
realT m_frequency
The frequency to modulate at if not triggering (default 2000 Hz)
int m_single
if >= 0 a single frame is non-zero.
INDI_NEWCALLBACK_DECL(dmSpeckle, m_indiP_modulating)
std::string m_dmChannelName
The name of the DM channel to write to.
pcf::IndiProperty m_indiP_dwell
int recordDmSpeck(bool force=false)
#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_INFO
Informational. The info log level is the lowest level recorded during normal operations.
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.
Software CRITICAL log entry.
Log entry recording stdMotionStage status.
A simple text log, a string-type log.