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"
214 config.add(
"dm.name",
"",
"dm.name", argType::Required,
"dm",
"name",
false,
"string",
"The descriptive name of this dm. Default is the channel name.");
215 config.add(
"dm.channelName",
"",
"dm.channelName", argType::Required,
"dm",
"channelName",
false,
"string",
"The name of the DM channel to write to.");
216 config.add(
"dm.triggerChannel",
"",
"dm.triggerChannel", argType::Required,
"dm",
"triggerChannel",
false,
"string",
"The name of the DM channel to trigger on.");
217 config.add(
"dm.triggerSemaphore",
"",
"dm.triggerSemaphore", argType::Required,
"dm",
"triggerSemaphore",
false,
"int",
"The semaphore to use (default 9).");
218 config.add(
"dm.trigger",
"",
"dm.trigger", argType::True,
"dm",
"trigger",
false,
"bool",
"Run in trigger mode if true (default).");
219 config.add(
"dm.separation",
"",
"dm.separation", argType::Required,
"dm",
"separation",
false,
"float",
"The radial separation of the speckles (default 15.0).");
220 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).");
221 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.");
222 config.add(
"dm.amp",
"",
"dm.amp", argType::Required,
"dm",
"amp",
false,
"float",
"The speckle amplitude on the DM (default 0.01).");
223 config.add(
"dm.cross",
"",
"dm.cross", argType::True,
"dm",
"cross",
false,
"bool",
"If true, also apply the cross speckles rotated by 90 degrees.");
225 config.add(
"dm.frequency",
"",
"dm.frequency", argType::Required,
"dm",
"frequency",
false,
"float",
"The frequency to modulate at if not triggering (default 2000 Hz).");
227 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.");
229 config.add(
"modulator.threadPrio",
"",
"modulator.threadPrio", argType::Required,
"modulator",
"threadPrio",
false,
"int",
"The real-time priority of the modulator thread.");
231 config.add(
"modulator.cpuset",
"",
"modulator.cpuset", argType::Required,
"modulator",
"cpuset",
false,
"string",
"The cpuset to assign the modulator thread to.");
244 if(_config.isSet(
"dm.trigger")) _config(
m_trigger,
"dm.trigger");
248 _config(
m_amp,
"dm.amp");
249 if(_config.isSet(
"dm.cross")) _config(
m_cross,
"dm.cross");
278 createStandardIndiNumber<float>(
m_indiP_angle,
"angle", 0, 0,100,
"%f");
286 log<software_error>({__FILE__,__LINE__});
298 createStandardIndiNumber<float>(
m_indiP_amp,
"amp", -1, 0,1,
"%f");
303 createStandardIndiNumber<float>(
m_indiP_frequency,
"frequency", 0, 0,10000,
"%f");
311 log<software_error>({__FILE__,__LINE__});
323 createStandardIndiNumber<float>(
m_indiP_dwell,
"dwell", 1, 100,1,
"%d");
331 log<software_error>({__FILE__,__LINE__});
338 log<software_error>({__FILE__,__LINE__});
344 log<software_critical>({__FILE__, __LINE__});
420 log<software_error>({__FILE__, __LINE__});
447 mx::improc::eigenImage<realT> onesp, onespC;
456 mx::sigproc::makeFourierMode(
m_shapes.image(0), m, n, 1);
461 mx::sigproc::makeFourierMode(
m_shapes.image(0), -n, m, 1);
468 mx::sigproc::makeFourierMode(
m_shapes.image(2), m, n, -1);
473 mx::sigproc::makeFourierMode(
m_shapes.image(2), -n, m, -1);
480 mx::fits::fitsFile<realT> ff;
481 ff.write(
"/tmp/specks.fits",
m_shapes);
513 mx::sys::milliSleep(500);
528 bool triggered =
false;
529 sem_t * sem =
nullptr;
551 clock_gettime(CLOCK_REALTIME, &modstart);
553 unsigned dwelled = 0;
562 if(clock_gettime(CLOCK_REALTIME, &ts) < 0)
564 log<software_critical>({__FILE__,__LINE__,errno,0,
"clock_gettime"});
570 if(sem_timedwait(sem, &ts) == 0)
579 if(errno == EINTR)
break;
583 if(errno != ETIMEDOUT)
585 log<software_error>({__FILE__, __LINE__,errno,
"sem_timedwait"});
593 clock_gettime(CLOCK_REALTIME, &currtime);
595 dnsec = (currtime.tv_sec - modstart.tv_sec)*1000000000 + (currtime.tv_nsec - modstart.tv_nsec);
603 else if(dnsec >= freqNsec || triggered)
625 modstart.tv_nsec += freqNsec;
626 if(modstart.tv_nsec >= 1000000000)
628 modstart.tv_nsec -= 1000000000;
629 modstart.tv_sec += 1;
638 clock_gettime(CLOCK_REALTIME, &currtime);
650 log<text_log>(
"zeroed");
659 if(
ipRecv.getName() != m_indiP_trigger.getName())
661 log<software_error>({__FILE__, __LINE__,
"invalid indi property received"});
665 if(!
ipRecv.find(
"toggle"))
return 0;
667 std::unique_lock<std::mutex> lock(m_indiMutex);
669 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off)
675 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On)
686 if(
ipRecv.getName() != m_indiP_separation.getName())
688 log<software_error>({__FILE__,__LINE__,
"wrong INDI property received."});
692 float sep = -1000000000;
694 if(
ipRecv.find(
"current") )
696 sep =
ipRecv[
"current"].get<
float>();
699 if(
ipRecv.find(
"target") )
701 sep =
ipRecv[
"target"].get<
float>();
704 if(sep == -1000000000)
706 log<software_error>({__FILE__,__LINE__,
"No requested separation"});
710 std::unique_lock<std::mutex> lock(m_indiMutex);
718 if(
ipRecv.getName() != m_indiP_angle.getName())
720 log<software_error>({__FILE__,__LINE__,
"wrong INDI property received."});
724 float ang = -1000000000;
726 if(
ipRecv.find(
"current") )
728 ang =
ipRecv[
"current"].get<
float>();
731 if(
ipRecv.find(
"target") )
733 ang =
ipRecv[
"target"].get<
float>();
736 if(ang == -1000000000)
738 log<software_error>({__FILE__,__LINE__,
"No angle received"});
742 std::unique_lock<std::mutex> lock(m_indiMutex);
750 if(
ipRecv.getName() != m_indiP_amp.getName())
752 log<software_error>({__FILE__,__LINE__,
"wrong INDI property received."});
756 float amp = -1000000000;
758 if(
ipRecv.find(
"current") )
760 amp =
ipRecv[
"current"].get<
float>();
763 if(
ipRecv.find(
"target") )
765 amp =
ipRecv[
"target"].get<
float>();
768 if(amp == -1000000000)
770 log<software_error>({__FILE__,__LINE__,
"Invalid requested amp: " + std::to_string(amp)});
774 std::unique_lock<std::mutex> lock(m_indiMutex);
782 if(
ipRecv.createUniqueKey() != m_indiP_cross.createUniqueKey())
784 log<software_error>({__FILE__, __LINE__,
"invalid indi property received"});
788 if(!
ipRecv.find(
"toggle"))
return 0;
790 std::unique_lock<std::mutex> lock(m_indiMutex);
792 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off)
798 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On)
809 if(
ipRecv.getName() != m_indiP_frequency.getName())
811 log<software_error>({__FILE__,__LINE__,
"wrong INDI property received."});
817 if(
ipRecv.find(
"current") )
819 freq =
ipRecv[
"current"].get<
float>();
822 if(
ipRecv.find(
"target") )
824 freq =
ipRecv[
"target"].get<
float>();
829 log<software_error>({__FILE__,__LINE__,
"Invalid requested frequency: " + std::to_string(freq)});
833 std::unique_lock<std::mutex> lock(m_indiMutex);
841 if(
ipRecv.createUniqueKey() != m_indiP_dwell.createUniqueKey())
843 log<software_error>({__FILE__,__LINE__,
"wrong INDI property received."});
849 if(
ipRecv.find(
"current") )
851 dwell =
ipRecv[
"current"].get<
unsigned>();
854 if(
ipRecv.find(
"target") )
856 dwell =
ipRecv[
"target"].get<
unsigned>();
861 log<software_error>({__FILE__,__LINE__,
"Invalid requested dwell: " + std::to_string(dwell)});
865 std::unique_lock<std::mutex> lock(m_indiMutex);
875 if(
ipRecv.getName() != m_indiP_modulating.getName())
877 log<software_error>({__FILE__, __LINE__,
"invalid indi property received"});
881 if(!
ipRecv.find(
"toggle"))
return 0;
883 std::unique_lock<std::mutex> lock(m_indiMutex);
885 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off)
887 m_modulating =
false;
891 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On)
903 if(
ipRecv.getName() != m_indiP_zero.getName())
905 log<software_error>({__FILE__, __LINE__,
"invalid indi property received"});
909 if(m_modulating ==
true)
915 if(!
ipRecv.find(
"request"))
return 0;
917 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
919 m_imageStream.md->write = 1;
921 memset(m_imageStream.array.raw, 0, m_width*m_height*m_typeSize);
923 clock_gettime(CLOCK_REALTIME, &currtime);
924 m_imageStream.md->atime = currtime;
925 m_imageStream.md->writetime = currtime;
927 m_imageStream.md->cnt0++;
929 m_imageStream.md->write=0;
930 ImageStreamIO_sempost(&m_imageStream,-1);
931 log<text_log>(
"zeroed");
957 static float lastAngle =
m_angle;
958 static float lastAmp =
m_amp;
959 static bool lastCross =
m_cross;
966 !(lastAmp ==
m_amp) ||
971 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.
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.
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
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
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)
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 updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
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
void nanoSleep(unsigned long nsec)
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
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 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.