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"))
261 _config(
m_amp,
"dm.amp");
263 if (_config.isSet(
"dm.cross"))
291 createStandardIndiNumber<float>(
m_indiP_delay,
"delay", 0, 0, 1,
"%f");
301 createStandardIndiNumber<float>(
m_indiP_angle,
"angle", 0, 0, 100,
"%f");
309 log<software_error>({__FILE__, __LINE__});
321 createStandardIndiNumber<float>(
m_indiP_amp,
"amp", -1, 0, 1,
"%f");
326 createStandardIndiNumber<float>(
m_indiP_frequency,
"frequency", 0, 0, 10000,
"%f");
334 log<software_error>({__FILE__, __LINE__});
346 createStandardIndiNumber<int>(
m_indiP_dwell,
"dwell", 1, 100, 1,
"%d");
351 createStandardIndiNumber<int>(
m_indiP_single,
"single", -1, 3, 1,
"%d");
359 log<software_error>({__FILE__, __LINE__});
366 log<software_error>({__FILE__, __LINE__});
372 log<software_critical>({__FILE__, __LINE__});
446 log<software_error>({__FILE__, __LINE__});
473 mx::improc::eigenImage<realT> onesp, onespC;
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;
510 ff.write(
"/tmp/specks.fits",
m_shapes);
514 for(
int pp = 0; pp <
m_shapes.planes(); ++pp)
554 mx::sys::milliSleep(500);
570 bool triggered =
false;
571 sem_t *sem =
nullptr;
593 clock_gettime(CLOCK_REALTIME, &modstart);
595 unsigned dwelled = 0;
610 if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
612 log<software_critical>({__FILE__, __LINE__, errno, 0,
"clock_gettime"});
619 if (sem_timedwait(sem, &ts) == 0)
621 t0 = mx::sys::get_curr_time();
624 while(t1 - t0 < triggerDelay)
626 double dt = (1e8)*( triggerDelay - (t1-t0));
629 t1 = mx::sys::get_curr_time();
644 if (errno != ETIMEDOUT)
646 log<software_error>({__FILE__, __LINE__, errno,
"sem_timedwait"});
654 clock_gettime(CLOCK_REALTIME, &currtime);
656 dnsec = (currtime.tv_sec - modstart.tv_sec) * 1000000000 + (currtime.tv_nsec - modstart.tv_nsec);
664 else if (dnsec >= freqNsec || triggered)
690 modstart.tv_nsec += freqNsec;
691 if (modstart.tv_nsec >= 1000000000)
693 modstart.tv_nsec -= 1000000000;
694 modstart.tv_sec += 1;
705 clock_gettime(CLOCK_REALTIME, &currtime);
718 log<text_log>(
"zeroed");
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");
1109 static float lastAngle =
m_angle;
1110 static float lastAmp =
m_amp;
1111 static bool lastCross =
m_cross;
1118 !(lastAmp ==
m_amp) ||
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 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.
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.
INDI_VALIDATE_CALLBACK_PROPS(function, ipRecv)
void nanoSleep(unsigned long nsec)
const pcf::IndiProperty & ipRecv
updateIfChanged(m_indiP_angle, "target", m_angle)
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
std::unique_lock< std::mutex > lock(m_indiMutex)
constexpr static logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
constexpr static logPrioT LOG_INFO
Informational. The info log level is the lowest level recorded during normal operations.
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
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.
int appLogic()
Perform telemeter application logic.
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
Log entry recording stdMotionStage status.
A simple text log, a string-type log.