10#include <mx/improc/eigenImage.hpp>
11#include <mx/improc/milkImage.hpp>
12#include <mx/improc/eigenCube.hpp>
13using namespace mx::improc;
17#include "../../ImageStreamIO/pixaccess.hpp"
137template<
class derivedT>
430 return *
static_cast<derivedT *
>(
this);
435template<
class derivedT>
440 derivedT::template log<software_error>({__FILE__, __LINE__,
"shmimMonitorT::setupConfig"});
444 config.add(
"wfscam.camDevName",
"",
"wfscam.camDevName", argType::Required,
"wfscam",
"camDevName",
false,
"string",
"INDI device name of the WFS camera. Default is wfscam.shmimName.");
445 config.add(
"wfscam.loopSemWait",
"",
"wfscam.loopSemWait", argType::Required,
"wfscam",
"loopSemWait",
false,
"float",
"The semaphore wait time for the wfs loop start signal");
446 config.add(
"wfscam.imageSemWait",
"",
"wfscam.imageSemWait", argType::Required,
"wfscam",
"imageSemWait",
false,
"float",
"The semaphore wait time for the image availability signal");
448 if(derived().darkShmimMonitor().setupConfig(config) < 0)
450 derivedT::template log<software_error>({__FILE__, __LINE__,
"darkShmimMonitorT::setupConfig"});
454 config.add(
"pokecen.dmChannel",
"",
"pokecen.dmChannel", argType::Required,
"pokecen",
"dmChannel",
false,
"string",
"The dm channel to use for pokes, e.g. dm01disp06.");
455 config.add(
"pokecen.pokeX",
"",
"pokecen.pokeX", argType::Required,
"pokecen",
"pokeX",
false,
"vector<int>",
"The x-coordinates of the actuators to poke. ");
456 config.add(
"pokecen.pokeY",
"",
"pokecen.pokeY", argType::Required,
"pokecen",
"pokeY",
false,
"vector<int>",
"The y-coordinates of the actuators to poke. ");
457 config.add(
"pokecen.pokeAmp",
"",
"pokecen.pokeAmp", argType::Required,
"pokecen",
"pokeAmp",
false,
"float",
"The poke amplitude, in DM command units. Default is 0.");
458 config.add(
"pokecen.dmSleep",
"",
"pokecen.dmSleep", argType::Required,
"pokecen",
"dmSleep",
false,
"float",
"The time to sleep for the DM command to be applied, in microseconds. Default is 10000.");
459 config.add(
"pokecen.nPokeImages",
"",
"pokecen.nPokeImages", argType::Required,
"pokecen",
"nPokeImages",
false,
"int",
"The number of poke images to average. Default 5.");
460 config.add(
"pokecen.nPokeAverage",
"",
"pokecen.nPokeAverage", argType::Required,
"pokecen",
"nPokeAverage",
false,
"int",
"The number of poke sequences to average. Default 10.");
467template<
class derivedT>
472 return derivedT::template log<software_error, -1>({__FILE__, __LINE__,
"shmimMonitorT::loadConfig"});
475 m_wfsCamDevName = derived().shmimMonitor().shmimName();
476 config(m_wfsCamDevName,
"wfscam.camDevName");
479 config(m_wfsSemWait,
"wfscam.loopSemWait");
481 m_wfsSemWait_sec = floor(m_wfsSemWait);
482 m_wfsSemWait_nsec = (m_wfsSemWait - m_wfsSemWait_sec) * 1e9;
484 config(m_imageSemWait,
"wfscam.imageSemWait");
486 m_imageSemWait_sec = floor(m_imageSemWait);
487 m_imageSemWait_nsec = (m_imageSemWait - m_imageSemWait_sec) * 1e9;
489 if(derived().darkShmimMonitor().loadConfig(config) < 0)
491 return derivedT::template log<software_error, -1>({__FILE__, __LINE__,
"darkShmimMonitorT::loadConfig"});
494 config(m_dmChan,
"pokecen.dmChannel");
496 config(m_poke_x,
"pokecen.pokeX");
498 config(m_poke_y,
"pokecen.pokeY");
500 if(m_poke_x.size() == 0 || (m_poke_x.size() != m_poke_y.size()))
502 return derivedT::template log<software_error,-1>({__FILE__, __LINE__,
"invalid poke specification"});
505 config(m_poke_amp,
"pokecen.pokeAmp");
507 config(m_dmSleep,
"pokecen.dmSleep");
509 config(m_nPokeImages,
"pokecen.nPokeImages");
511 config(m_nPokeAverage,
"pokecen.nPokeAverage");
516template<
class derivedT>
521 return derivedT::template log<software_error, -1>({__FILE__,__LINE__});
524 if( derived().darkShmimMonitor().appStartup() < 0)
526 return derivedT::template log<software_error, -1>({__FILE__,__LINE__});
530 m_indiP_poke_amp[
"current"].setValue(m_poke_amp);
531 m_indiP_poke_amp[
"target"].setValue(m_poke_amp);
534 m_indiP_nPokeImages[
"current"].setValue(m_nPokeImages);
535 m_indiP_nPokeImages[
"target"].setValue(m_nPokeImages);
538 m_indiP_nPokeAverage[
"current"].setValue(m_nPokeAverage);
539 m_indiP_nPokeAverage[
"target"].setValue(m_nPokeAverage);
549 derived().template registerIndiPropertyReadOnly( m_indiP_measurement,
"measurement", pcf::IndiProperty::Number, pcf::IndiProperty::ReadOnly, pcf::IndiProperty::Idle);
550 m_indiP_measurement.add({
"delta_x", 0.0});
551 m_indiP_measurement.add({
"delta_y", 0.0});
552 m_indiP_measurement.add({
"counter", 0});
554 if(sem_init(&m_wfsSemaphore, 0,0) < 0)
556 return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno,0,
"Initializing wfs semaphore"});
559 if(sem_init(&m_imageSemaphore, 0,0) < 0)
561 return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno,0,
"Initializing image semaphore"});
564 if(derived().template threadStart( m_wfsThread, m_wfsThreadInit, m_wfsThreadID, m_wfsThreadProp, m_wfsThreadPrio, m_wfsCpuset,
"wfs",
this, wfsThreadStart) < 0)
566 return derivedT::template log<software_critical,-1>({__FILE__, __LINE__});
572template<
class derivedT>
578 return derivedT::template log<software_error, -1>({__FILE__,__LINE__});
581 if( derived().darkShmimMonitor().appLogic() < 0)
583 return derivedT::template log<software_error, -1>({__FILE__,__LINE__});
590 if(pthread_tryjoin_np(m_wfsThread.native_handle(),0) == 0)
592 derivedT::template log<software_error>({__FILE__, __LINE__,
"WFS thread has exited"});
598 derivedT::template log<software_error>({__FILE__, __LINE__,
"WFS thread has exited"});
606 derived().template updateSwitchIfChanged(m_indiP_continuous,
"toggle", pcf::IndiElement::SwitchStateType::On,
INDI_OK);
610 derived().template updateSwitchIfChanged(m_indiP_continuous,
"toggle", pcf::IndiElement::SwitchStateType::Off,
INDI_IDLE);
615 derived().template updateSwitchIfChanged(m_indiP_single,
"toggle", pcf::IndiElement::SwitchStateType::On,
INDI_OK);
619 derived().template updateSwitchIfChanged(m_indiP_single,
"toggle", pcf::IndiElement::SwitchStateType::Off,
INDI_IDLE);
624 derived().template updateSwitchIfChanged(m_indiP_continuous,
"toggle", pcf::IndiElement::SwitchStateType::Off,
INDI_IDLE);
625 derived().template updateSwitchIfChanged(m_indiP_single,
"toggle", pcf::IndiElement::SwitchStateType::Off,
INDI_IDLE);
628 derived().template
updateIfChanged( m_indiP_nPokeImages,
"current", m_nPokeImages);
629 derived().template
updateIfChanged( m_indiP_nPokeAverage,
"current", m_nPokeAverage);
630 derived().template
updateIfChanged( m_indiP_poke_amp,
"current", m_poke_amp);
635template<
class derivedT>
640 derivedT::template log<software_error>({__FILE__, __LINE__,
"error from shmimMonitorT::appShutdown"});
643 if(derived().darkShmimMonitor().appShutdown() < 0)
645 derivedT::template log<software_error>({__FILE__, __LINE__,
"error from darkShmimMonitorT::appShutdown"});
648 if (m_wfsThread.joinable())
650 pthread_kill(m_wfsThread.native_handle(), SIGUSR1);
663template<
class derivedT>
666 static_cast<void>(dummy);
668 std::unique_lock<std::mutex>
lock(m_wfsImageMutex);
670 m_rawImage.create( derived().m_configName +
"_raw", derived().
shmimMonitor().width(), derived().
shmimMonitor().height());
672 wfsPixget = getPixPointer<float>(derived().
shmimMonitor().dataType());
676 m_dmStream.open(m_dmChan);
678 catch(
const std::exception& e)
680 return derivedT::template log<software_error,-1>({__FILE__, __LINE__, std::string(
"exception opening DM: ") + e.what()});
683 m_dmStream.passive(
true);
685 m_dmImage.resize(m_dmStream.rows(), m_dmStream.cols());
687 if(derived().darkShmimMonitor().width() == derived().shmimMonitor().width() &&
688 derived().darkShmimMonitor().height() == derived().shmimMonitor().height() )
697 if(m_pokeImage.rows() != derived().shmimMonitor().width() || m_pokeImage.cols() != derived().shmimMonitor().height())
699 m_pokeImage.create(derived().m_configName +
"_poke", derived().shmimMonitor().width(), derived().shmimMonitor().height());
702 m_pokeLocal.resize(derived().shmimMonitor().width(), derived().shmimMonitor().height());
707template<
class derivedT>
712 static_cast<void>(dummy);
714 std::unique_lock<std::mutex>
lock(m_wfsImageMutex);
716 float * data = m_rawImage().data();
717 float * darkData = m_darkImage.data();
720 uint64_t Npix = derived().shmimMonitor().width()*derived().shmimMonitor().height();
724 for(
unsigned nn=0; nn < Npix; ++nn)
726 data[nn] = wfsPixget(curr_src, nn) - darkData[nn];
731 for(
unsigned nn=0; nn < Npix; ++nn)
733 data[nn] = wfsPixget(curr_src, nn);
737 if(sem_post(&m_imageSemaphore) < 0)
739 return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno, 0,
"Error posting to semaphore"});
747template<
class derivedT>
750 static_cast<void>(dummy);
752 std::unique_lock<std::mutex>
lock(m_wfsImageMutex);
754 m_darkImage.resize(derived().darkShmimMonitor().width(), derived().darkShmimMonitor().height());
756 darkPixget = getPixPointer<float>(derived().darkShmimMonitor().dataType());
758 if(derived().darkShmimMonitor().width() == derived().
shmimMonitor().width() &&
759 derived().darkShmimMonitor().height() == derived().
shmimMonitor().height() )
761 std::cerr <<
"dark is valid " << derived().darkShmimMonitor().width() <<
" " << derived().shmimMonitor().width() <<
" ";
762 std::cerr << derived().darkShmimMonitor().height() <<
" " << derived().shmimMonitor().height() <<
"\n";
773template<
class derivedT>
778 static_cast<void>(dummy);
780 std::unique_lock<std::mutex>
lock(m_wfsImageMutex);
782 float * darkData = m_darkImage.data();
785 uint64_t nPix = derived().darkShmimMonitor().width()*derived().darkShmimMonitor().height();
786 for(
unsigned nn=0; nn < nPix; ++nn)
788 darkData[nn] = darkPixget(curr_src, nn);
794template<
class derivedT>
800template<
class derivedT>
803 m_wfsThreadID = syscall(SYS_gettid);
806 while(m_wfsThreadInit ==
true && derived().m_shutdown == 0)
811 while(derived().m_shutdown == 0)
823 else if(m_continuous)
835 while(!m_pokeImage.valid())
837 mx::sys::milliSleep(10);
840 m_stopMeasurement =
false;
842 bool firstRun =
true;
844 while(!m_stopMeasurement && !derived().m_shutdown)
846 if( derived().runSensor(firstRun) < 0)
848 derivedT::template log<software_error>({__FILE__, __LINE__,
"runSensor returned error"});
852 if(m_stopMeasurement || derived().m_shutdown)
857 if( derived().analyzeSensor() < 0)
859 derivedT::template log<software_error>({__FILE__, __LINE__,
"runSensor returned error"});
864 derived().updateIfChanged(m_indiP_measurement,
"counter", m_counter);
865 derived().recordPokeLoop();
888template<
class derivedT>
894 if(pokeSign < 0) sign = -1;
899 for(
size_t nn = 0; nn < m_poke_x.size(); ++nn)
901 m_dmImage( m_poke_x[nn], m_poke_y[nn]) = pokeSign*m_poke_amp;
905 m_dmStream = m_dmImage;
907 mx::sys::microSleep(m_dmSleep);
915 while(!ready && !(m_stopMeasurement || derived().m_shutdown))
925 while(n < m_nPokeImages && !(m_stopMeasurement || derived().m_shutdown))
932 m_pokeLocal += sign*m_rawImage();
937 if(m_stopMeasurement || derived().m_shutdown)
940 m_dmStream = m_dmImage;
947template<
class derivedT>
952 if(!m_pokeImage.valid())
954 return derivedT::template log<software_error,-1>({__FILE__, __LINE__,
"poke image is not allocated"});
957 m_pokeLocal.setZero();
959 for(
unsigned nseq = 0; nseq < m_nPokeAverage; ++nseq)
964 rv = basicTimedPoke(+1);
968 derivedT::template log<software_error>({__FILE__, __LINE__});
976 if(m_stopMeasurement || derived().m_shutdown)
983 rv = basicTimedPoke(-1);
987 derivedT::template log<software_error>({__FILE__, __LINE__});
995 if(m_stopMeasurement || derived().m_shutdown)
1003 m_pokeImage = m_pokeLocal/(2.0*m_nPokeImages*m_nPokeAverage);
1005 catch(
const std::exception& e)
1007 return derivedT::template log<software_error,-1>({__FILE__, __LINE__, e.what()});
1012 m_dmImage.setZero();
1013 m_dmStream = m_dmImage;
1018template<
class derivedT>
1025 m_indiP_measurement[
"delta_x"] = deltaX;
1026 m_indiP_measurement[
"delta_y"] = deltaY;
1031template<
class derivedT>
1038 if( derived().
template indiTargetUpdate(m_indiP_nPokeImages, target,
ipRecv,
false) < 0)
1040 return derivedT::template log<software_error,-1>({__FILE__, __LINE__});
1043 m_nPokeImages = target;
1048template<
class derivedT>
1055 if( derived().
template indiTargetUpdate(m_indiP_nPokeAverage, target,
ipRecv,
false) < 0)
1057 return derivedT::template log<software_error,-1>({__FILE__, __LINE__});
1060 m_nPokeAverage = target;
1065template<
class derivedT>
1072 if( derived().
template indiTargetUpdate(m_indiP_poke_amp, target,
ipRecv,
false) < 0)
1074 return derivedT::template log<software_error,-1>({__FILE__, __LINE__});
1077 m_poke_amp = target;
1082template<
class derivedT>
1087 if(
ipRecv.find(
"current") !=
true )
1092 m_wfsFps =
ipRecv[
"current"].get<
float>();
1097template<
class derivedT>
1102 if(
ipRecv.find(
"toggle") !=
true )
1107 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On )
1109 if(m_measuring == 0)
1113 if(sem_post(&m_wfsSemaphore) < 0)
1115 return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno, 0,
"Error posting to semaphore"});
1123template<
class derivedT>
1128 if(
ipRecv.find(
"toggle") !=
true )
1133 if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::On )
1135 if(m_measuring == 0)
1139 if(sem_post(&m_wfsSemaphore) < 0)
1141 return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno, 0,
"Error posting to semaphore"});
1145 else if(
ipRecv[
"toggle"].getSwitchState() == pcf::IndiElement::Off )
1147 if(m_measuring != 0)
1149 m_stopMeasurement =
true;
1156template<
class derivedT>
1161 if(
ipRecv.find(
"request") !=
true )
1166 if(
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On )
1168 if(m_measuring != 0)
1170 m_stopMeasurement =
true;
1177template<
class derivedT>
1180 return recordPokeLoop(
true);
1183template<
class derivedT>
1186 static int measuring = -1;
1187 static float deltaX = std::numeric_limits<float>::max();
1188 static float deltaY = std::numeric_limits<float>::max();
1189 static uint64_t counter = std::numeric_limits<uint64_t>::max();
1191 if(force || (m_counter != counter) || (m_deltaX != deltaX) || (m_deltaY != deltaY) || (m_measuring != measuring))
1193 uint8_t meas = m_measuring;
1194 derived().template telem<telem_pokeloop>({meas, m_deltaX, m_deltaY, m_counter});
1196 measuring = m_measuring;
1199 counter = m_counter;
1209#define DMPOKEWFS_SETUP_CONFIG( cfig ) \
1210 if(dmPokeWFST::setupConfig(cfig) < 0) \
1212 log<software_error>({__FILE__, __LINE__, "Error from dmPokeWFST::setupConfig"}); \
1213 m_shutdown = true; \
1221#define DMPOKEWFS_LOAD_CONFIG( cfig ) \
1222 if(dmPokeWFST::loadConfig(cfig) < 0) \
1224 return log<software_error,-1>({__FILE__, __LINE__, "Error from dmPokeWFST::loadConfig"}); \
1228#define DMPOKEWFS_APP_STARTUP \
1229 if( dmPokeWFST::appStartup() < 0) \
1231 return log<software_error, -1>({__FILE__,__LINE__}); \
1235#define DMPOKEWFS_APP_LOGIC \
1236 if( dmPokeWFST::appLogic() < 0) \
1238 return log<software_error, -1>({__FILE__,__LINE__}); \
1242#define DMPOKEWFS_APP_SHUTDOWN \
1243 if(dmPokeWFST::appShutdown() < 0) \
1245 log<software_error>({__FILE__, __LINE__, "error from dmPokeWFST::appShutdown"}); \
A base class to coordinate poking a deformable mirror's actuators and synchronizedreads of a camera i...
pcf::IndiProperty m_indiP_poke_amp
mx::improc::eigenImage< float > m_pokeLocal
mx::improc::milkImage< float > m_rawImage
bool m_continuous
True if continuous measurements are in progress.
INDI_NEWCALLBACK_DECL(derivedT, m_indiP_nPokeAverage)
INDI_NEWCALLBACK_DECL(derivedT, m_indiP_continuous)
std::string m_wfsCamDevName
INDI device name of the WFS camera. Default is wfscam.shmimName.
double m_imageSemWait
The time in sec to wait on the image semaphore. Default 0.5 sec.
void wfsThreadExec()
Execute the frame grabber main loop.
unsigned m_wfsSemWait_nsec
The timeoutfor the WFS semaphore, nanoseconds component.
int updateMeasurement(float deltaX, float deltaY)
float(* wfsPixget)(void *, size_t)
sem_t m_imageSemaphore
Semaphore used to signal that an image is ready.
INDI_NEWCALLBACK_DECL(derivedT, m_indiP_single)
static void wfsThreadStart(dmPokeWFS *s)
Thread starter, called by wfsThreadStart on thread construction. Calls wfsThreadExec.
sem_t m_wfsSemaphore
Semaphore used to signal the WFS thread to start WFSing.
float m_wfsFps
Pointer to a function to extract the image data as float.
float m_dmSleep
The time to sleep for the DM command to be applied, in microseconds. Default is 10000.
pcf::IndiProperty m_indiP_nPokeAverage
std::vector< int > m_poke_y
int processImage(void *curr_src, const darkShmimT &)
float(* darkPixget)(void *, size_t)
mx::improc::eigenImage< float > m_dmImage
int m_wfsThreadPrio
Priority of the WFS thread, should normally be > 00.
unsigned m_nPokeImages
The number of images to average for the poke images. Default is 5.
int appShutdown()
dmPokeWFS shutdown
pcf::IndiProperty m_wfsThreadProp
The property to hold the WFS thread details.
bool m_darkValid
Flag indicating if dark is valid based on its size.
mx::improc::milkImage< float > m_pokeImage
unsigned m_wfsSemWait_sec
The timeout for the WFS semaphore, seconds component.
pcf::IndiProperty m_indiP_single
Switch to start a single measurement.
pcf::IndiProperty m_indiP_measurement
Property to report the delta measurement, including the loop counter.
int basicTimedPoke(float pokeSign)
Apply a single DM poke pattern and record the results.
int allocate(const wfsShmimT &)
unsigned m_imageSemWait_sec
The timeout for the image semaphore, seconds component.
unsigned m_imageSemWait_nsec
The timeout for the image semaphore, nanoseconds component.
mx::improc::milkImage< float > m_dmStream
Pointer to a function to extract the dark image data as float.
double m_wfsSemWait
The time in sec to wait on the WFS semaphore. Default 0.5 sec.
INDI_NEWCALLBACK_DECL(derivedT, m_indiP_nPokeImages)
unsigned m_nPokeAverage
The number of poke sequences to average. Default is 10.
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
pcf::IndiProperty m_indiP_wfsFps
Property to get the FPS from the WFS camera.
INDI_NEWCALLBACK_DECL(derivedT, m_indiP_poke_amp)
pcf::IndiProperty m_indiP_stop
Switch to request that measurement stop.
bool m_wfsThreadInit
Synchronizer to ensure wfs thread initializes before doing dangerous things.
pcf::IndiProperty m_indiP_nPokeImages
INDI_NEWCALLBACK_DECL(derivedT, m_indiP_stop)
pid_t m_wfsThreadID
WFS thread PID.
int basicRunSensor()
Run the basic +/- poke sensor steps.
int allocate(const darkShmimT &)
std::mutex m_wfsImageMutex
int recordPokeLoop(bool force=false)
std::thread m_wfsThread
A separate thread for the actual WFSing.
int appStartup()
Startup function.
int m_measuring
Status of measuring: 0 no, 1 single in progress, 2 continuous in progress.
bool m_stopMeasurement
Used to request that the measurement in progress stop.
INDI_SETCALLBACK_DECL(derivedT, m_indiP_wfsFps)
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
std::string m_wfsCpuset
The cpuset for the framegrabber thread. Ignored if empty (the default).
int appLogic()
dmPokeWFS application logic
bool m_single
True a single measurement is in progress.
int recordTelem(const telem_pokeloop *)
mx::improc::eigenImage< float > m_darkImage
The dark image.
int processImage(void *curr_src, const wfsShmimT &)
pcf::IndiProperty m_indiP_continuous
Switch to start continuous measurement.
std::vector< int > m_poke_x
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define CREATE_REG_INDI_NEW_NUMBERI_DERIVED(prop, name, min, max, step, format, label, group)
Create and register a NEW INDI property as a standard number as int, using the standard callback name...
#define CREATE_REG_INDI_NEW_REQUESTSWITCH_DERIVED(prop, name)
Create and register a NEW INDI property as a standard request switch, using the standard callback nam...
#define INDI_SETCALLBACK_DEFN(class, prop)
Define the callback for a set property request.
#define CREATE_REG_INDI_NEW_TOGGLESWITCH_DERIVED(prop, name)
Create and register a NEW INDI property as a standard toggle switch, using the standard callback name...
#define CREATE_REG_INDI_NEW_NUMBERF_DERIVED(prop, name, min, max, step, format, label, group)
Create and register a NEW INDI property as a standard number as float, using the standard callback na...
@ OPERATING
The device is operating, other than homing.
@ READY
The device is ready for operation, but is not operating.
#define INDI_VALIDATE_CALLBACK_PROPS_DERIVED(prop1, prop2)
Standard check for matching INDI properties in a callback in a CRTP base class.
#define REG_INDI_SETPROP_DERIVED(prop, devName, propName)
const pcf::IndiProperty & ipRecv
updateIfChanged(m_indiP_angle, "target", m_angle)
std::unique_lock< std::mutex > lock(m_indiMutex)
XWC app semaphore Utilities to use CRTP derived classes.
#define XWC_SEM_FLUSH_DERIVED(sem)
#define XWC_SEM_WAIT_TS_RETVOID_DERIVED(ts, sec, nsec)
Add the wait time to a timespec for a sem_timedwait call, with no value returned on error,...
#define XWC_SEM_TIMEDWAIT_LOOP_DERIVED(sem, ts)
Perform a sem_timedwait in the context of a standard loop in MagAO-X code using the derived class.
#define XWC_SEM_WAIT_TS_DERIVED(ts, sec, nsec)
Add the wait time to a timespec for a sem_timedwait call, with -1 returned on error.
static std::string indiPrefix()
static std::string configSection()
static std::string configSection()
static std::string indiPrefix()