7 #ifndef dmModulator_hpp
8 #define dmModulator_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"
20 #include <mx/math/randomT.hpp>
190 config.add(
"dm.shapeCube",
"",
"dm.shapeCube", argType::Required,
"dm",
"shapeCube",
false,
"string",
"Full path to the FITS file containing the shapes to modulate on this DM.");
191 config.add(
"dm.name",
"",
"dm.name", argType::Required,
"dm",
"name",
false,
"string",
"The descriptive name of this dm. Default is the channel name.");
192 config.add(
"dm.channelName",
"",
"dm.channelName", argType::Required,
"dm",
"channelName",
false,
"string",
"The name of the DM channel to write to.");
193 config.add(
"dm.triggerChannel",
"",
"dm.triggerChannel", argType::Required,
"dm",
"triggerChannel",
false,
"string",
"The name of the DM channel to trigger on.");
194 config.add(
"dm.triggerSemaphore",
"",
"dm.triggerSemaphore", argType::Required,
"dm",
"triggerSemaphore",
false,
"int",
"The semaphore to use (default 9).");
195 config.add(
"dm.trigger",
"",
"dm.trigger", argType::True,
"dm",
"trigger",
false,
"bool",
"Run in trigger mode if true (default).");
196 config.add(
"dm.amp",
"",
"dm.amp", argType::Required,
"dm",
"amp",
false,
"float",
"The speckle amplitude on the DM (default 0.01).");
198 config.add(
"dm.frequency",
"",
"dm.frequency", argType::Required,
"dm",
"frequency",
false,
"float",
"The frequency to modulate at if not triggering (default 2000 Hz).");
200 config.add(
"modulator.threadPrio",
"",
"modulator.threadPrio", argType::Required,
"modulator",
"threadPrio",
false,
"int",
"The real-time priority of the modulator thread.");
202 config.add(
"modulator.cpuset",
"",
"modulator.cpuset", argType::Required,
"modulator",
"cpuset",
false,
"string",
"The cpuset to assign the modulator thread to.");
215 if(_config.isSet(
"dm.trigger")) _config(
m_trigger,
"dm.trigger");
216 _config(
m_amp,
"dm.amp");
233 mx::fits::fitsFile<realT> ff;
246 createStandardIndiNumber<float>(
m_indiP_amp,
"amp", -1, 0,1,
"%f");
251 createStandardIndiNumber<float>(
m_indiP_frequency,
"frequency", 0, 0,10000,
"%f");
259 log<software_error>({__FILE__,__LINE__});
274 log<software_error>({__FILE__,__LINE__});
281 log<software_error>({__FILE__,__LINE__});
287 log<software_critical>({__FILE__, __LINE__});
387 mx::improc::eigenCube<realT> ampShapes;
389 mx::improc::eigenImage<realT> noise;
401 mx::sys::milliSleep(500);
416 bool triggered =
false;
417 sem_t * sem =
nullptr;
432 for(
int cc=0;cc<noise.cols(); ++cc)
434 for(
int rr=0;rr<noise.rows(); ++rr)
440 for(
int p =0; p < ampShapes.planes(); ++p)
449 clock_gettime(CLOCK_REALTIME, &modstart);
457 if(clock_gettime(CLOCK_REALTIME, &ts) < 0)
459 log<software_critical>({__FILE__,__LINE__,errno,0,
"clock_gettime"});
465 if(sem_timedwait(sem, &ts) == 0)
474 if(errno == EINTR)
break;
478 if(errno != ETIMEDOUT)
480 log<software_error>({__FILE__, __LINE__,errno,
"sem_timedwait"});
488 clock_gettime(CLOCK_REALTIME, &currtime);
490 dnsec = (currtime.tv_sec - modstart.tv_sec)*1000000000 + (currtime.tv_nsec - modstart.tv_nsec);
494 if(dnsec >= freqNsec || triggered)
522 for(
int cc=0;cc<noise.cols(); ++cc)
524 for(
int rr=0;rr<noise.rows(); ++rr)
533 modstart.tv_nsec += freqNsec;
534 if(modstart.tv_nsec >= 1000000000)
536 modstart.tv_nsec -= 1000000000;
537 modstart.tv_sec += 1;
545 clock_gettime(CLOCK_REALTIME, &currtime);
557 log<text_log>(
"zeroed");
566 if(
ipRecv.getName() != m_indiP_amp.getName())
568 log<software_error>({__FILE__,__LINE__,
"wrong INDI property received."});
572 float amp = -1000000000;
574 if(
ipRecv.find(
"current") )
576 amp =
ipRecv[
"current"].get<
float>();
579 if(
ipRecv.find(
"target") )
581 amp =
ipRecv[
"target"].get<
float>();
584 if(amp == -1000000000)
586 log<software_error>({__FILE__,__LINE__,
"Invalid requested amp: " + std::to_string(amp)});
590 std::unique_lock<std::mutex> lock(m_indiMutex);
598 if(
ipRecv.getName() != m_indiP_frequency.getName())
600 log<software_error>({__FILE__,__LINE__,
"wrong INDI property received."});
606 if(
ipRecv.find(
"current") )
608 freq =
ipRecv[
"current"].get<
float>();
611 if(
ipRecv.find(
"target") )
613 freq =
ipRecv[
"target"].get<
float>();
618 log<software_error>({__FILE__,__LINE__,
"Invalid requested frequency: " + std::to_string(freq)});
622 std::unique_lock<std::mutex> lock(m_indiMutex);
630 if(
ipRecv.getName() != m_indiP_trigger.getName())
632 log<software_error>({__FILE__, __LINE__,
"invalid indi property received"});
636 if(!
ipRecv.find(
"toggle"))
return 0;
638 std::unique_lock<std::mutex> lock(m_indiMutex);
640 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off)
646 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On)
657 if(
ipRecv.getName() != m_indiP_modulating.getName())
659 log<software_error>({__FILE__, __LINE__,
"invalid indi property received"});
663 if(!
ipRecv.find(
"toggle"))
return 0;
665 std::unique_lock<std::mutex> lock(m_indiMutex);
667 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off)
669 m_modulating =
false;
673 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On)
685 if(
ipRecv.getName() != m_indiP_zero.getName())
687 log<software_error>({__FILE__, __LINE__,
"invalid indi property received"});
691 if(m_modulating ==
true)
697 if(!
ipRecv.find(
"request"))
return 0;
699 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
701 m_imageStream.md->write = 1;
703 memset(m_imageStream.array.raw, 0, m_width*m_height*m_typeSize);
705 clock_gettime(CLOCK_REALTIME, &currtime);
706 m_imageStream.md->atime = currtime;
707 m_imageStream.md->writetime = currtime;
709 m_imageStream.md->cnt0++;
711 m_imageStream.md->write=0;
712 ImageStreamIO_sempost(&m_imageStream,-1);
713 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.
~dmModulator() noexcept
D'tor, declared and defined for noexcept.
std::vector< std::string > m_elNames
INDI_NEWCALLBACK_DECL(dmModulator, m_indiP_amp)
bool m_modThreadInit
Synchronizer to ensure f.g. thread initializes before doing dangerous things.
pcf::IndiProperty m_indiP_amp
realT m_frequency
The frequency to modulate at if not triggering (default 2000 Hz)
virtual int appLogic()
Implementation of the FSM for dmModulator.
virtual int appStartup()
Startup function.
mx::improc::eigenCube< realT > m_shapes
std::thread m_modThread
A separate thread for the modulation.
pid_t m_modThreadID
Modulate thread PID.
pcf::IndiProperty m_indiP_trigger
virtual int appShutdown()
Shutdown the app.
pcf::IndiProperty m_indiP_dm
virtual void loadConfig()
std::string m_dmChannelName
The name of the DM channel to write to.
static void modThreadStart(dmModulator *d)
Thread starter, called by modThreadStart on thread construction. Calls modThreadExec.
INDI_NEWCALLBACK_DECL(dmModulator, m_indiP_zero)
dmModulator()
Default c'tor.
mx::math::normDistT< realT > m_normDist
void modThreadExec()
Execute the frame grabber main loop.
INDI_NEWCALLBACK_DECL(dmModulator, m_indiP_modulating)
std::string m_modThreadCpuset
The cpuset for the modulator thread.
uint32_t m_height
The height of the image.
virtual void setupConfig()
INDI_NEWCALLBACK_DECL(dmModulator, m_indiP_trigger)
int m_modThreadPrio
Priority of the modulator thread, should normally be > 00.
size_t m_typeSize
The size of the type, in bytes.
friend class dmModulator_test
realT m_amp
The speckle amplitude on the DM.
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
pcf::IndiProperty m_indiP_modulating
std::string m_dmName
The descriptive name of this dm. Default is the channel name.
pcf::IndiProperty m_indiP_frequency
uint32_t m_width
The width of the image.
bool m_trigger
Run in trigger mode if true (default)
pcf::IndiProperty m_modThreadProp
The property to hold the modulator thread details.
uint8_t m_dataType
The ImageStreamIO type code.
int m_triggerSemaphore
The semaphore to use (default 9)
pcf::IndiProperty m_indiP_zero
INDI_NEWCALLBACK_DECL(dmModulator, m_indiP_frequency)
std::string m_dmTriggerChannel
The DM channel to monitor as a trigger.
#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_ERROR
An error has occured which the software will attempt to correct.
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
A simple text log, a string-type log.