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"
220 config.add(
"dm.channelName",
"",
"dm.channelName", argType::Required,
"dm",
"channelName",
false,
"string",
"The name of the DM channel to write to.");
221 config.add(
"dm.triggerChannel",
"",
"dm.triggerChannel", argType::Required,
"dm",
"triggerChannel",
false,
"string",
"The name of the DM channel to trigger on.");
222 config.add(
"dm.triggerSemaphore",
"",
"dm.triggerSemaphore", argType::Required,
"dm",
"triggerSemaphore",
false,
"int",
"The semaphore to use (default 9).");
223 config.add(
"dm.trigger",
"",
"dm.trigger", argType::True,
"dm",
"trigger",
false,
"bool",
"Run in trigger mode if true (default).");
224 config.add(
"dm.triggerDelay",
"",
"dm.triggerDelay", argType::Required,
"dm",
"triggerDelay",
false,
"float",
"Delay to apply to the trigger.");
226 config.add(
"dm.separation",
"",
"dm.separation", argType::Required,
"dm",
"separation",
false,
"float",
"The radial separation of the speckles (default 15.0).");
227 config.add(
"dm.angle",
"",
"dm.angle", argType::Required,
"dm",
"angle",
false,
"float",
"The angle of the speckle pattern c.c.w. from up on camsci1/2 (default 0.0).");
228 config.add(
"dm.angleOffset",
"",
"dm.angleOffset", argType::Required,
"dm",
"angleOffset",
false,
"float",
"The calibration offset of angle so that up on camsci1/2 is 0.");
229 config.add(
"dm.amp",
"",
"dm.amp", argType::Required,
"dm",
"amp",
false,
"float",
"The speckle amplitude on the DM (default 0.01).");
230 config.add(
"dm.cross",
"",
"dm.cross", argType::True,
"dm",
"cross",
false,
"bool",
"If true, also apply the cross speckles rotated by 90 degrees.");
232 config.add(
"dm.frequency",
"",
"dm.frequency", argType::Required,
"dm",
"frequency",
false,
"float",
"The frequency to modulate at if not triggering (default 2000 Hz).");
234 config.add(
"dm.dwell",
"",
"dm.dwell", argType::True,
"dm",
"dwell",
false,
"int",
"The dwell time for each speckle, or for how many frames it is held. Default=1.");
236 config.add(
"modulator.threadPrio",
"",
"modulator.threadPrio", argType::Required,
"modulator",
"threadPrio",
false,
"int",
"The real-time priority of the modulator thread.");
238 config.add(
"modulator.cpuset",
"",
"modulator.cpuset", argType::Required,
"modulator",
"cpuset",
false,
"string",
"The cpuset to assign the modulator thread to.");
251 if (
_config.isSet(
"dm.trigger"))
482 mx::sigproc::makeFourierMode(
m_shapes.image(0),
m,
n, 1);
487 mx::sigproc::makeFourierMode(
m_shapes.image(0), -
n,
m, 1);
497 mx::sigproc::makeFourierMode(
m_shapes.image(2),
m,
n, -1);
502 mx::sigproc::makeFourierMode(
m_shapes.image(2), -
n,
m, -1);
509 mx::fits::fitsFile<realT>
ff;
554 mx::sys::milliSleep(500);
621 t0 = mx::sys::get_curr_time();
628 mx::sys::nanoSleep(
dt);
629 t1 = mx::sys::get_curr_time();
653 mx::sys::nanoSleep(0.5 *
dnsec);
724(
const pcf::IndiProperty &
ipRecv)
726 if (
ipRecv.getName() != m_indiP_trigger.getName())
728 log<software_error>({__FILE__, __LINE__,
"invalid indi property received"});
732 if (!
ipRecv.find(
"toggle"))
735 std::unique_lock<std::mutex>
lock(m_indiMutex);
737 if (
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off)
743 if (
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On)
756 if (
ipRecv.getName() != m_indiP_delay.getName())
758 log<software_error>({__FILE__, __LINE__,
"wrong INDI property received."});
762 float del = -1000000000;
764 if (
ipRecv.find(
"current"))
766 del =
ipRecv[
"current"].get<
float>();
769 if (
ipRecv.find(
"target"))
771 del =
ipRecv[
"target"].get<
float>();
774 if (del == -1000000000)
776 log<software_error>({__FILE__, __LINE__,
"No requested delay"});
780 std::unique_lock<std::mutex>
lock(m_indiMutex);
781 m_triggerDelay = del;
790 if (
ipRecv.getName() != m_indiP_separation.getName())
792 log<software_error>({__FILE__, __LINE__,
"wrong INDI property received."});
796 float sep = -1000000000;
798 if (
ipRecv.find(
"current"))
800 sep =
ipRecv[
"current"].get<
float>();
803 if (
ipRecv.find(
"target"))
805 sep =
ipRecv[
"target"].get<
float>();
808 if (sep == -1000000000)
810 log<software_error>({__FILE__, __LINE__,
"No requested separation"});
814 std::unique_lock<std::mutex>
lock(m_indiMutex);
824(
const pcf::IndiProperty &
ipRecv)
826 if (
ipRecv.getName() != m_indiP_angle.getName())
828 log<software_error>({__FILE__, __LINE__,
"wrong INDI property received."});
832 float ang = -1000000000;
836 ang =
ipRecv[
"current"].get<
float>();
841 ang =
ipRecv[
"target"].get<
float>();
844 if (ang == -1000000000)
846 log<software_error>({__FILE__, __LINE__,
"No angle received"});
850 std::unique_lock<std::mutex>
lock(m_indiMutex);
859(
const pcf::IndiProperty &
ipRecv)
861 if (
ipRecv.getName() != m_indiP_amp.getName())
863 log<software_error>({__FILE__, __LINE__,
"wrong INDI property received."});
867 float amp = -1000000000;
869 if (
ipRecv.find(
"current"))
871 amp =
ipRecv[
"current"].get<
float>();
874 if (
ipRecv.find(
"target"))
876 amp =
ipRecv[
"target"].get<
float>();
879 if (amp == -1000000000)
881 log<software_error>({__FILE__, __LINE__,
"Invalid requested amp: " + std::to_string(amp)});
885 std::unique_lock<std::mutex>
lock(m_indiMutex);
894(
const pcf::IndiProperty &
ipRecv)
896 if (
ipRecv.createUniqueKey() != m_indiP_cross.createUniqueKey())
898 log<software_error>({__FILE__, __LINE__,
"invalid indi property received"});
902 if (!
ipRecv.find(
"toggle"))
905 std::unique_lock<std::mutex>
lock(m_indiMutex);
907 if (
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off)
913 if (
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On)
925(
const pcf::IndiProperty &
ipRecv)
927 if (
ipRecv.getName() != m_indiP_frequency.getName())
929 log<software_error>({__FILE__, __LINE__,
"wrong INDI property received."});
935 if (
ipRecv.find(
"current"))
937 freq =
ipRecv[
"current"].get<
float>();
940 if (
ipRecv.find(
"target"))
942 freq =
ipRecv[
"target"].get<
float>();
947 log<software_error>({__FILE__, __LINE__,
"Invalid requested frequency: " + std::to_string(freq)});
951 std::unique_lock<std::mutex>
lock(m_indiMutex);
961(
const pcf::IndiProperty &
ipRecv)
963 if (
ipRecv.createUniqueKey() != m_indiP_dwell.createUniqueKey())
965 log<software_error>({__FILE__, __LINE__,
"wrong INDI property received."});
971 if (
ipRecv.find(
"current"))
973 dwell =
ipRecv[
"current"].get<
unsigned>();
976 if (
ipRecv.find(
"target"))
978 dwell =
ipRecv[
"target"].get<
unsigned>();
983 log<software_error>({__FILE__, __LINE__,
"Invalid requested dwell: " + std::to_string(dwell)});
987 std::unique_lock<std::mutex>
lock(m_indiMutex);
997(
const pcf::IndiProperty &
ipRecv)
1003 if (
ipRecv.find(
"current"))
1008 if (
ipRecv.find(
"target"))
1013 if (single < -1 || single > 3 )
1015 log<software_error>({__FILE__, __LINE__,
"Invalid requested dwell: " + std::to_string(
single)});
1019 std::unique_lock<std::mutex>
lock(m_indiMutex);
1026(
const pcf::IndiProperty &
ipRecv)
1028 if (
ipRecv.getName() != m_indiP_modulating.getName())
1030 log<software_error>({__FILE__, __LINE__,
"invalid indi property received"});
1034 if (!
ipRecv.find(
"toggle"))
1037 std::unique_lock<std::mutex>
lock(m_indiMutex);
1039 if (
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off)
1041 m_modulating =
false;
1045 if (
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On)
1047 m_modulating =
true;
1056(
const pcf::IndiProperty &
ipRecv)
1058 if (
ipRecv.getName() != m_indiP_zero.getName())
1060 log<software_error>({__FILE__, __LINE__,
"invalid indi property received"});
1064 if (m_modulating ==
true)
1070 if (!
ipRecv.find(
"request"))
1073 if (
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
1075 m_imageStream.md->write = 1;
1077 memset(m_imageStream.array.raw, 0, m_width * m_height * m_typeSize);
1079 clock_gettime(CLOCK_REALTIME, &currtime);
1080 m_imageStream.md->atime = currtime;
1081 m_imageStream.md->writetime = currtime;
1083 m_imageStream.md->cnt0++;
1085 m_imageStream.md->write = 0;
1086 ImageStreamIO_sempost(&m_imageStream, -1);
1087 log<text_log>(
"zeroed");
1123 std::vector<float>({
m_angle}), std::vector<float>({
m_amp}), 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.
Log entry recording stdMotionStage status.
A simple text log, a string-type log.