18#include <mx/improc/eigenImage.hpp>
19#include <mx/ioutils/fits/fitsFile.hpp>
21#include <boost/filesystem/operations.hpp>
23#include "../../ImageStreamIO/ImageStruct.hpp"
32template <
typename typeT>
41 return _DATATYPE_FLOAT;
47 return _DATATYPE_DOUBLE;
69template <
class derivedT,
typename realT>
98 static constexpr uint8_t
m_dmDataType = ImageStreamTypeCode<realT>();
359 pcf::IndiProperty ipFreq(pcf::IndiProperty::Switch);
363 ipFreq.add(pcf::IndiElement(
"toggle"));
364 ipFreq[
"toggle"] = pcf::IndiElement::Off;
365 derived().sendNewProperty(ipFreq);
367 derivedT::template log<text_log>(
"DM saturation threshold exceeded. Loop opened.", logPrio::LOG_WARNING);
409 const pcf::IndiProperty &
ipRecv
425 const pcf::IndiProperty &
ipRecv
441 const pcf::IndiProperty &
ipRecv
457 const pcf::IndiProperty &
ipRecv
473 const pcf::IndiProperty &
ipRecv
489 const pcf::IndiProperty &
ipRecv
505 const pcf::IndiProperty &
ipRecv
521 const pcf::IndiProperty &
ipRecv
545 typedef int32_t cbIndexT;
547 double m_t0 {0}, m_tf {0}, m_tsat0 {0}, m_tsatf {0};
548 double m_tact0 {0}, m_tact1 {0}, m_tact2 {0}, m_tact3 {0}, m_tact4 {0};
550 mx::sigproc::circularBufferIndex<double, cbIndexT> m_piTimes;
552 mx::sigproc::circularBufferIndex<double, cbIndexT> m_satSem;
554 mx::sigproc::circularBufferIndex<double, cbIndexT> m_actProc;
556 mx::sigproc::circularBufferIndex<double, cbIndexT> m_actCom;
558 mx::sigproc::circularBufferIndex<double, cbIndexT> m_satUp;
560 std::vector<double> m_piTimesD;
561 std::vector<double> m_satSemD;
562 std::vector<double> m_actProcD;
563 std::vector<double> m_actComD;
564 std::vector<double> m_satUpD;
571 return *
static_cast<derivedT *
>(
this);
575template <
class derivedT,
typename realT>
578 config.add(
"dm.calibPath",
"",
"dm.calibPath", argType::Required,
"dm",
"calibPath",
false,
"string",
"The path to calibration files, relative to the MagAO-X calibration path.");
580 config.add(
"dm.flatPath",
"",
"dm.flatPath", argType::Required,
"dm",
"flatPath",
false,
"string",
"The path to flat files. Default is the calibration path.");
581 config.add(
"dm.flatDefault",
"",
"dm.flatDefault", argType::Required,
"dm",
"flatDefault",
false,
"string",
"The default flat file (path and extension are not required).");
583 config.add(
"dm.testPath",
"",
"dm.testPath", argType::Required,
"dm",
"testPath",
false,
"string",
"The path to test files. Default is the calibration path plus /tests.");
584 config.add(
"dm.testDefault",
"",
"dm.testDefault", argType::Required,
"dm",
"testDefault",
false,
"string",
"The default test file (path and extension are not required).");
589 config.add(
"dm.threadPrio",
"",
"dm.threadPrio", argType::Required,
"dm",
"threadPrio",
false,
"int",
"The real-time priority of the dm control thread.");
590 config.add(
"dm.cpuset",
"",
"dm.cpuset", argType::Required,
"dm",
"cpuset",
false,
"int",
"The cpuset for the dm control thread.");
592 config.add(
"dm.shmimName",
"",
"dm.shmimName", argType::Required,
"dm",
"shmimName",
false,
"string",
"The name of the ImageStreamIO shared memory image to monitor for DM comands. Will be used as /tmp/<shmimName>.im.shm.");
594 config.add(
"dm.shmimFlat",
"",
"dm.shmimFlat", argType::Required,
"dm",
"shmimFlat",
false,
"string",
"The name of the ImageStreamIO shared memory image to write the flat command to. Default is shmimName with 00 apended (i.e. dm00disp -> dm00disp00). ");
596 config.add(
"dm.shmimTest",
"",
"dm.shmimTest", argType::Required,
"dm",
"shmimTest",
false,
"string",
"The name of the ImageStreamIO shared memory image to write the test command to. Default is shmimName with 01 apended (i.e. dm00disp -> dm00disp01). ");
598 config.add(
"dm.shmimSat",
"",
"dm.shmimSat", argType::Required,
"dm",
"shmimSat",
false,
"string",
"The name of the ImageStreamIO shared memory image to write the saturation map to. Default is shmimName with SA apended (i.e. dm00disp -> dm00dispSA). This is created.");
600 config.add(
"dm.shmimSatPerc",
"",
"dm.shmimSatPerc", argType::Required,
"dm",
"shmimSatPerc",
false,
"string",
"The name of the ImageStreamIO shared memory image to write the saturation percentage map to. Default is shmimName with SP apended (i.e. dm00disp -> dm00dispSP). This is created.");
602 config.add(
"dm.satAvgInt",
"",
"dm.satAvgInt", argType::Required,
"dm",
"satAvgInt",
false,
"int",
"The interval in milliseconds over which saturation is accumulated before updating. Default is 100 ms.");
604 config.add(
"dm.width",
"",
"dm.width", argType::Required,
"dm",
"width",
false,
"string",
"The width of the DM in actuators.");
605 config.add(
"dm.height",
"",
"dm.height", argType::Required,
"dm",
"height",
false,
"string",
"The height of the DM in actuators.");
607 config.add(
"dm.percThreshold",
"",
"dm.percThreshold", argType::Required,
"dm",
"percThreshold",
false,
"float",
"Threshold on percentage of frames an actuator is saturated over an interval. Default is 0.98.");
608 config.add(
"dm.intervalSatThreshold",
"",
"dm.intervalSatThreshold", argType::Required,
"dm",
"intervalSatThreshold",
false,
"float",
"Threshold on percentage of actuators which exceed percThreshold in an interval. Default is 0.5.");
609 config.add(
"dm.intervalSatCountThreshold",
"",
"dm.intervalSatCountThreshold", argType::Required,
"dm",
"intervalSatCountThreshold",
false,
"float",
"Threshold one number of consecutive intervals the intervalSatThreshold is exceeded. Default is 10.");
611 config.add(
"dm.satTriggerDevice",
"",
"dm.satTriggerDevice", argType::Required,
"dm",
"satTriggerDevice",
false,
"vector<string>",
"Device(s) with a toggle switch to toggle on saturation trigger.");
612 config.add(
"dm.satTriggerProperty",
"",
"dm.satTriggerProperty", argType::Required,
"dm",
"satTriggerProperty",
false,
"vector<string>",
"Property with a toggle switch to toggle on saturation trigger, one per entry in satTriggerDevice.");
617template <
class derivedT,
typename realT>
621 m_calibPath = derived().m_calibDir +
"/" + m_calibRelDir;
622 config(m_calibPath,
"dm.calibPath");
625 m_flatPath = m_calibPath +
"/flats";
626 config(m_flatPath,
"dm.flatPath");
628 config(m_flatDefault,
"dm.flatDefault");
629 if (m_flatDefault !=
"")
631 m_flatDefault = mx::ioutils::pathStem(m_flatDefault);
632 m_flatCurrent =
"default";
636 m_testPath = m_calibPath +
"/tests";
637 config(m_testPath,
"dm.testPath");
639 config(m_testDefault,
"dm.testDefault");
640 if (m_testDefault !=
"")
642 m_testDefault = mx::ioutils::pathStem(m_testDefault);
643 m_testCurrent =
"default";
648 config(derived().m_smThreadPrio,
"dm.threadPrio");
649 config(derived().m_smCpuset,
"dm.cpuset");
651 config(derived().m_shmimName,
"dm.shmimName");
653 if (derived().m_shmimName !=
"")
655 m_shmimFlat = derived().m_shmimName +
"00";
656 config(m_shmimFlat,
"dm.shmimFlat");
658 m_shmimTest = derived().m_shmimName +
"02";
659 config(m_shmimTest,
"dm.shmimTest");
661 m_shmimSat = derived().m_shmimName +
"ST";
662 config(m_shmimSat,
"dm.shmimSat");
664 m_shmimSatPerc = derived().m_shmimName +
"SP";
665 config(m_shmimSatPerc,
"dm.shmimSatPerc");
667 config(m_satAvgInt,
"dm.satAvgInt");
671 config.isSet(
"dm.shmimFlat");
672 config.isSet(
"dm.shmimTest");
673 config.isSet(
"dm.shmimSat");
674 config.isSet(
"dm.shmimSatPerc");
675 config.isSet(
"dm.satAvgInt");
678 config(m_dmWidth,
"dm.width");
679 config(m_dmHeight,
"dm.height");
681 config(m_percThreshold,
"dm.percThreshold");
682 config(m_intervalSatThreshold,
"dm.intervalSatThreshold");
683 config(m_intervalSatCountThreshold,
"dm.intervalSatCountThreshold");
684 config(m_satTriggerDevice,
"dm.satTriggerDevice");
685 config(m_satTriggerProperty,
"dm.satTriggerProperty");
690template <
class derivedT,
typename realT>
693 if (m_dmDataType == 0)
695 derivedT::template log<software_error>({__FILE__, __LINE__,
"unsupported DM data type"});
704 m_indiP_flatShmim = pcf::IndiProperty(pcf::IndiProperty::Text);
705 m_indiP_flatShmim.setDevice(derived().configName());
706 m_indiP_flatShmim.setName(
"flat_shmim");
707 m_indiP_flatShmim.setPerm(pcf::IndiProperty::ReadOnly);
708 m_indiP_flatShmim.setState(pcf::IndiProperty::Idle);
709 m_indiP_flatShmim.add(pcf::IndiElement(
"channel"));
710 m_indiP_flatShmim[
"channel"] = m_shmimFlat;
712 if (derived().registerIndiPropertyReadOnly(m_indiP_flatShmim) < 0)
715 derivedT::template log<software_error>({__FILE__, __LINE__});
721 derived().createStandardIndiToggleSw(m_indiP_setFlat,
"flat_set");
722 if (derived().registerIndiPropertyNew(m_indiP_setFlat, st_newCallBack_setFlat) < 0)
725 derivedT::template log<software_error>({__FILE__, __LINE__});
735 m_indiP_testShmim = pcf::IndiProperty(pcf::IndiProperty::Text);
736 m_indiP_testShmim.setDevice(derived().configName());
737 m_indiP_testShmim.setName(
"test_shmim");
738 m_indiP_testShmim.setPerm(pcf::IndiProperty::ReadOnly);
739 m_indiP_testShmim.setState(pcf::IndiProperty::Idle);
740 m_indiP_testShmim.add(pcf::IndiElement(
"channel"));
741 m_indiP_testShmim[
"channel"] = m_shmimTest;
742 derived().createStandardIndiToggleSw(m_indiP_setTest,
"test_shmim");
743 if (derived().registerIndiPropertyReadOnly(m_indiP_testShmim) < 0)
746 derivedT::template log<software_error>({__FILE__, __LINE__});
752 derived().createStandardIndiToggleSw(m_indiP_setTest,
"test_set");
753 if (derived().registerIndiPropertyNew(m_indiP_setTest, st_newCallBack_setTest) < 0)
756 derivedT::template log<software_error>({__FILE__, __LINE__});
762 derived().createStandardIndiRequestSw(m_indiP_init,
"initDM");
763 if (derived().registerIndiPropertyNew(m_indiP_init, st_newCallBack_init) < 0)
766 derivedT::template log<software_error>({__FILE__, __LINE__});
772 derived().createStandardIndiRequestSw(m_indiP_zero,
"zeroDM");
773 if (derived().registerIndiPropertyNew(m_indiP_zero, st_newCallBack_zero) < 0)
776 derivedT::template log<software_error>({__FILE__, __LINE__});
782 derived().createStandardIndiRequestSw(m_indiP_release,
"releaseDM");
783 if (derived().registerIndiPropertyNew(m_indiP_release, st_newCallBack_release) < 0)
786 derivedT::template log<software_error>({__FILE__, __LINE__});
791 derived().createStandardIndiRequestSw(m_indiP_zeroAll,
"zeroAll");
792 if (derived().registerIndiPropertyNew(m_indiP_zeroAll, st_newCallBack_zeroAll) < 0)
795 derivedT::template log<software_error>({__FILE__, __LINE__});
800 if (m_flatDefault !=
"")
805 if (m_testDefault !=
"")
810 if (sem_init(&m_satSemaphore, 0, 0) < 0)
812 return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno, 0,
"Initializing sat semaphore"});
815 if (derived().threadStart(m_satThread, m_satThreadInit, m_satThreadID, m_satThreadProp, m_satThreadPrio,
"",
"saturation",
this, satThreadStart) < 0)
817 derivedT::template log<software_error, -1>({__FILE__, __LINE__});
824template <
class derivedT,
typename realT>
828 if (pthread_tryjoin_np(m_satThread.native_handle(), 0) == 0)
830 derivedT::template log<software_error>({__FILE__, __LINE__,
"saturation thread has exited"});
839 if (m_intervalSatTrip)
842 m_intervalSatTrip =
false;
846 static uint64_t lastMono = 0;
848 if(m_piTimes.size() >= m_piTimes.maxEntries() && m_piTimes.maxEntries() > 0 && m_piTimes.mono() != lastMono)
850 cbIndexT refEntry = m_piTimes.earliest();
852 m_piTimesD.resize(m_piTimes.maxEntries());
853 m_satSemD.resize(m_satSem.maxEntries());
854 m_actProcD.resize(m_actProc.maxEntries());
855 m_actComD.resize(m_actCom.maxEntries());
856 m_satUpD.resize(m_satUp.maxEntries());
858 for(
size_t n=0; n < m_piTimesD.size(); ++n)
860 m_piTimesD[n] = m_piTimes.at(refEntry,n);
861 m_satSemD[n] = m_satSem.at(refEntry,n);
862 m_actProcD[n] = m_actProc.at(refEntry,n);
863 m_actComD[n] = m_actCom.at(refEntry,n);
864 m_satUpD[n] = m_satUp.at(refEntry,n);
867 std::cerr <<
"Act. Process: " << mx::math::vectorMean(m_actProcD) <<
" +/- " << sqrt(mx::math::vectorVariance(m_actProcD)) <<
"\n";
868 std::cerr <<
"Act. Command: " << mx::math::vectorMean(m_actComD) <<
" +/- " << sqrt(mx::math::vectorVariance(m_actComD)) <<
"\n";
869 std::cerr <<
"Sat. Update: " << mx::math::vectorMean(m_satUpD) <<
" +/- " << sqrt(mx::math::vectorVariance(m_satUpD)) <<
"\n";
870 std::cerr <<
"Tot. CommandDM: " << mx::math::vectorMean(m_piTimesD) <<
" +/- " << sqrt(mx::math::vectorVariance(m_piTimesD)) <<
"\n";
871 std::cerr <<
"Sat. Semaphore: " << mx::math::vectorMean(m_satSemD) <<
" +/- " << sqrt(mx::math::vectorVariance(m_satSemD)) <<
"\n";
874 lastMono = m_piTimes.mono();
881template <
class derivedT,
typename realT>
884 if (m_satThread.joinable())
886 pthread_kill(m_satThread.native_handle(), SIGUSR1);
899template <
class derivedT,
typename realT>
907template <
class derivedT,
typename realT>
916template <
class derivedT,
typename realT>
919 std::vector<std::string> dmlist = mx::ioutils::getFileNames(
"/milk/shm/", derived().m_shmimName,
".im",
".shm");
921 if (dmlist.size() == 0)
923 derivedT::template log<software_error>({__FILE__, __LINE__,
"no dm channels found for " + derived().m_shmimName});
928 for (
size_t n = 0; n < dmlist.size(); ++n)
931 snprintf(nstr,
sizeof(nstr),
"%02d.im.shm", (
int)n);
932 std::string tgt = derived().m_shmimName;
935 for (
size_t m = 0; m < dmlist.size(); ++m)
937 if (dmlist[m].find(tgt) != std::string::npos)
939 if ((
int)n > m_channels)
947 derivedT::template log<text_log>({std::string(
"Found ") + std::to_string(m_channels) +
" channels for " + derived().m_shmimName});
952template <
class derivedT,
typename realT>
955 static_cast<void>(sp);
959 if (derived().m_width != m_dmWidth)
961 derivedT::template log<software_critical>({__FILE__, __LINE__,
"shmim width does not match configured DM width"});
965 if (derived().m_height != m_dmHeight)
967 derivedT::template log<software_critical>({__FILE__, __LINE__,
"shmim height does not match configured DM height"});
971 if (derived().m_dataType != m_dmDataType)
973 derivedT::template log<software_critical>({__FILE__, __LINE__,
"shmim data type does not match configured DM data type"});
980 m_instSatMap.resize(m_dmWidth, m_dmHeight);
981 m_instSatMap.setZero();
983 m_accumSatMap.resize(m_dmWidth, m_dmHeight);
984 m_accumSatMap.setZero();
986 m_satPercMap.resize(m_dmWidth, m_dmHeight);
987 m_satPercMap.setZero();
989 if (findDMChannels() < 0)
991 derivedT::template log<software_critical>({__FILE__, __LINE__,
"error finding DM channels"});
997 m_piTimes.maxEntries(2000);
998 m_satSem.maxEntries(2000);
999 m_actProc.maxEntries(2000);
1000 m_actCom.maxEntries(2000);
1001 m_satUp.maxEntries(2000);
1007template <
class derivedT,
typename realT>
1011 static_cast<void>(sp);
1013 #ifdef XWC_DMTIMINGS
1014 m_t0 = mx::sys::get_curr_time();
1017 int rv = derived().commandDM(curr_src);
1019 #ifdef XWC_DMTIMINGS
1020 m_tf = mx::sys::get_curr_time();
1025 derivedT::template log<software_critical>({__FILE__, __LINE__, errno, rv,
"Error from commandDM"});
1029 #ifdef XWC_DMTIMINGS
1030 m_tsat0 = mx::sys::get_curr_time();
1034 if (sem_post(&m_satSemaphore) < 0)
1036 derivedT::template log<software_critical>({__FILE__, __LINE__, errno, 0,
"Error posting to semaphore"});
1040 #ifdef XWC_DMTIMINGS
1041 m_tsatf = mx::sys::get_curr_time();
1044 #ifdef XWC_DMTIMINGS
1046 if(m_piTimes.maxEntries() > 0)
1048 m_piTimes.nextEntry(m_tf-m_t0);
1049 m_satSem.nextEntry(m_tsatf - m_tsat0);
1050 m_actProc.nextEntry(m_tact1 - m_tact0);
1051 m_actCom.nextEntry(m_tact2 - m_tact1);
1052 m_satUp.nextEntry(m_tact4 - m_tact3);
1058template <
class derivedT,
typename realT>
1062 if ((rv = derived().releaseDM()) < 0)
1064 derivedT::template log<software_critical>({__FILE__, __LINE__, errno, rv,
"Error from releaseDM"});
1068 if ((rv = zeroAll(
true)) < 0)
1070 derivedT::template log<software_error>({__FILE__, __LINE__, errno, rv,
"Error from zeroAll"});
1077template <
class derivedT,
typename realT>
1080 std::vector<std::string> tfs = mx::ioutils::getFileNames(m_flatPath,
"",
"",
".fits");
1083 for (
size_t n = 0; n < tfs.size(); ++n)
1085 if (mx::ioutils::pathStem(tfs[n]) ==
"default")
1087 tfs.erase(tfs.begin() + n);
1092 unsigned m_nFlatFiles = 5;
1095 if (tfs.size() >= m_nFlatFiles)
1097 std::vector<std::time_t> wtimes(tfs.size());
1099 for (
size_t n = 0; n < wtimes.size(); ++n)
1101 wtimes[n] = boost::filesystem::last_write_time(tfs[n]);
1104 std::sort(wtimes.begin(), wtimes.end());
1106 std::time_t tn = wtimes[wtimes.size() - m_nFlatFiles];
1108 for (
size_t n = 0; n < tfs.size(); ++n)
1110 std::time_t lmt = boost::filesystem::last_write_time(tfs[n]);
1113 tfs.erase(tfs.begin() + n);
1119 for (
auto it = m_flatCommands.begin();
it != m_flatCommands.end(); ++
it)
1124 bool changed =
false;
1125 for (
size_t n = 0; n < tfs.size(); ++n)
1127 auto ir = m_flatCommands.insert(std::pair<std::string, std::string>(mx::ioutils::pathStem(tfs[n]), tfs[n]));
1128 if (ir.second ==
true)
1131 ir.first->second = tfs[n];
1134 for (
auto it = m_flatCommands.begin();
it != m_flatCommands.end(); ++
it)
1136 if (
it->second ==
"")
1142 m_flatCommands.erase(itdel);
1149 if (derived().m_indiDriver)
1151 derived().m_indiDriver->sendDelProperty(m_indiP_flats);
1152 derived().m_indiNewCallBacks.erase(m_indiP_flats.createUniqueKey());
1155 m_indiP_flats = pcf::IndiProperty(pcf::IndiProperty::Switch);
1156 m_indiP_flats.setDevice(derived().configName());
1157 m_indiP_flats.setName(
"flat");
1158 m_indiP_flats.setPerm(pcf::IndiProperty::ReadWrite);
1159 m_indiP_flats.setState(pcf::IndiProperty::Idle);
1160 m_indiP_flats.setRule(pcf::IndiProperty::OneOfMany);
1163 for (
auto it = m_flatCommands.begin();
it != m_flatCommands.end(); ++
it)
1165 if (
it->first == m_flatCurrent || m_flatCurrent ==
"")
1167 m_indiP_flats.add(pcf::IndiElement(
it->first, pcf::IndiElement::On));
1168 m_flatCurrent =
it->first;
1172 m_indiP_flats.add(pcf::IndiElement(
it->first, pcf::IndiElement::Off));
1176 if (m_flatDefault !=
"")
1178 if (m_flatCurrent ==
"default")
1180 m_indiP_flats.add(pcf::IndiElement(
"default", pcf::IndiElement::On));
1184 m_indiP_flats.add(pcf::IndiElement(
"default", pcf::IndiElement::Off));
1188 if (derived().registerIndiPropertyNew(m_indiP_flats, st_newCallBack_flats) < 0)
1190#ifndef DM_TEST_NOLOG
1191 derivedT::template log<software_error>({__FILE__, __LINE__});
1196 if (derived().m_indiDriver)
1198 derived().m_indiDriver->sendDefProperty(m_indiP_flats);
1205template <
class derivedT,
typename realT>
1208 std::string target = intarget;
1210 std::string targetPath;
1212 if (target ==
"default")
1214 target = m_flatDefault;
1215 targetPath = m_flatPath +
"/" + m_flatDefault +
".fits";
1221 targetPath = m_flatCommands.at(target);
1225 derivedT::template log<text_log>(
"flat file " + target +
" not found", logPrio::LOG_ERROR);
1230 m_flatLoaded =
false;
1232 mx::fits::fitsFile<realT> ff;
1233 if (ff.read(m_flatCommand, targetPath) < 0)
1235 derivedT::template log<text_log>(
"flat file " + targetPath +
" not found", logPrio::LOG_ERROR);
1239 derivedT::template log<text_log>(
"loaded flat file " + targetPath);
1240 m_flatLoaded =
true;
1242 m_flatCurrent = intarget;
1244 if (m_indiP_flats.find(
"default"))
1246 if (m_flatCurrent ==
"default")
1248 m_indiP_flats[
"default"] = pcf::IndiElement::On;
1252 m_indiP_flats[
"default"] = pcf::IndiElement::Off;
1256 for (
auto i = m_flatCommands.begin(); i != m_flatCommands.end(); ++i)
1258 if (!m_indiP_flats.find(i->first))
1263 if (i->first == m_flatCurrent)
1265 m_indiP_flats[i->first] = pcf::IndiElement::On;
1269 m_indiP_flats[i->first] = pcf::IndiElement::Off;
1273 if (derived().m_indiDriver)
1275 derived().m_indiDriver->sendSetProperty(m_indiP_flats);
1286template <
class derivedT,
typename realT>
1289 if (m_shmimFlat ==
"")
1292 if (ImageStreamIO_openIm(&m_flatImageStream, m_shmimFlat.c_str()) != 0)
1294 derivedT::template log<text_log>(
"could not connect to flat channel " + m_shmimFlat, logPrio::LOG_WARNING);
1298 if (m_flatImageStream.md[0].size[0] != m_dmWidth)
1300 ImageStreamIO_closeIm(&m_flatImageStream);
1301 derivedT::template log<text_log>(
"width mismatch between " + m_shmimFlat +
" and configured DM", logPrio::LOG_ERROR);
1305 if (m_flatImageStream.md[0].size[1] != m_dmHeight)
1307 ImageStreamIO_closeIm(&m_flatImageStream);
1308 derivedT::template log<text_log>(
"height mismatch between " + m_shmimFlat +
" and configured DM", logPrio::LOG_ERROR);
1314 bool flatSet = m_flatSet;
1317 if (loadFlat(m_flatCurrent) < 0)
1319 derivedT::template log<text_log>(
"error loading flat " + m_flatCurrent, logPrio::LOG_ERROR);
1321 m_flatSet = flatSet;
1326 ImageStreamIO_closeIm(&m_flatImageStream);
1327 derivedT::template log<text_log>(
"no flat loaded", logPrio::LOG_ERROR);
1331 if (m_flatCommand.rows() != m_dmWidth)
1333 ImageStreamIO_closeIm(&m_flatImageStream);
1334 derivedT::template log<text_log>(
"width mismatch between flat file and configured DM", logPrio::LOG_ERROR);
1338 if (m_flatCommand.cols() != m_dmHeight)
1340 ImageStreamIO_closeIm(&m_flatImageStream);
1341 derivedT::template log<text_log>(
"height mismatch between flat file and configured DM", logPrio::LOG_ERROR);
1345 m_flatImageStream.md->write = 1;
1349 memcpy(m_flatImageStream.array.raw, m_flatCommand.data(), m_dmWidth * m_dmHeight *
sizeof(realT));
1352 clock_gettime(CLOCK_REALTIME, &m_flatImageStream.md->writetime);
1355 m_flatImageStream.md->atime = m_flatImageStream.md->writetime;
1357 m_flatImageStream.md->cnt0++;
1358 m_flatImageStream.md->write = 0;
1359 ImageStreamIO_sempost(&m_flatImageStream, -1);
1364 ImageStreamIO_closeIm(&m_flatImageStream);
1368 derived().updateSwitchIfChanged(m_indiP_setFlat,
"toggle", pcf::IndiElement::On, pcf::IndiProperty::Busy);
1370 derivedT::template log<text_log>(
"flat set");
1376template <
class derivedT,
typename realT>
1379 if (m_shmimFlat ==
"")
1382 if (ImageStreamIO_openIm(&m_flatImageStream, m_shmimFlat.c_str()) != 0)
1384 derivedT::template log<text_log>(
"could not connect to flat channel " + m_shmimFlat, logPrio::LOG_WARNING);
1388 if (m_flatImageStream.md[0].size[0] != m_dmWidth)
1390 ImageStreamIO_closeIm(&m_flatImageStream);
1391 derivedT::template log<text_log>(
"width mismatch between " + m_shmimFlat +
" and configured DM", logPrio::LOG_ERROR);
1395 if (m_flatImageStream.md[0].size[1] != m_dmHeight)
1397 ImageStreamIO_closeIm(&m_flatImageStream);
1398 derivedT::template log<text_log>(
"height mismatch between " + m_shmimFlat +
" and configured DM", logPrio::LOG_ERROR);
1402 m_flatImageStream.md->write = 1;
1406 memset(m_flatImageStream.array.raw, 0, m_dmWidth * m_dmHeight *
sizeof(realT));
1409 clock_gettime(CLOCK_REALTIME, &m_flatImageStream.md->writetime);
1412 m_flatImageStream.md->atime = m_flatImageStream.md->writetime;
1414 m_flatImageStream.md->cnt0++;
1415 m_flatImageStream.md->write = 0;
1416 ImageStreamIO_sempost(&m_flatImageStream, -1);
1421 ImageStreamIO_closeIm(&m_flatImageStream);
1423 derived().updateSwitchIfChanged(m_indiP_setFlat,
"toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle);
1425 derivedT::template log<text_log>(
"flat zeroed");
1430template <
class derivedT,
typename realT>
1433 std::vector<std::string> tfs = mx::ioutils::getFileNames(m_testPath,
"",
"",
".fits");
1435 for (
auto it = m_testCommands.begin();
it != m_testCommands.end(); ++
it)
1440 bool changed =
false;
1441 for (
size_t n = 0; n < tfs.size(); ++n)
1443 auto ir = m_testCommands.insert(std::pair<std::string, std::string>(mx::ioutils::pathStem(tfs[n]), tfs[n]));
1444 if (ir.second ==
true)
1447 ir.first->second = tfs[n];
1450 for (
auto it = m_testCommands.begin();
it != m_testCommands.end(); ++
it)
1452 if (
it->second ==
"")
1458 m_testCommands.erase(itdel);
1465 if (derived().m_indiDriver)
1467 derived().m_indiDriver->sendDelProperty(m_indiP_tests);
1468 derived().m_indiNewCallBacks.erase(m_indiP_tests.createUniqueKey());
1471 m_indiP_tests = pcf::IndiProperty(pcf::IndiProperty::Switch);
1472 m_indiP_tests.setDevice(derived().configName());
1473 m_indiP_tests.setName(
"test");
1474 m_indiP_tests.setPerm(pcf::IndiProperty::ReadWrite);
1475 m_indiP_tests.setState(pcf::IndiProperty::Idle);
1476 m_indiP_tests.setRule(pcf::IndiProperty::OneOfMany);
1479 for (
auto it = m_testCommands.begin();
it != m_testCommands.end(); ++
it)
1481 if (
it->first == m_testCurrent || m_testCurrent ==
"")
1483 m_indiP_tests.add(pcf::IndiElement(
it->first, pcf::IndiElement::On));
1484 m_testCurrent =
it->first;
1488 m_indiP_tests.add(pcf::IndiElement(
it->first, pcf::IndiElement::Off));
1492 if (m_testDefault !=
"")
1494 if (m_testCurrent ==
"default")
1496 m_indiP_tests.add(pcf::IndiElement(
"default", pcf::IndiElement::On));
1500 m_indiP_tests.add(pcf::IndiElement(
"default", pcf::IndiElement::Off));
1504 if (derived().registerIndiPropertyNew(m_indiP_tests, st_newCallBack_tests) < 0)
1506#ifndef DM_TEST_NOLOG
1507 derivedT::template log<software_error>({__FILE__, __LINE__});
1512 if (derived().m_indiDriver)
1514 derived().m_indiDriver->sendDefProperty(m_indiP_tests);
1521template <
class derivedT,
typename realT>
1524 std::string target = intarget;
1526 if (target ==
"default")
1528 target = m_testDefault;
1531 std::string targetPath;
1535 targetPath = m_testCommands.at(target);
1539 derivedT::template log<text_log>(
"test file " + target +
" not found", logPrio::LOG_ERROR);
1543 m_testLoaded =
false;
1545 mx::fits::fitsFile<realT> ff;
1546 if (ff.read(m_testCommand, targetPath) < 0)
1548 derivedT::template log<text_log>(
"test file " + targetPath +
" not found", logPrio::LOG_ERROR);
1552 derivedT::template log<text_log>(
"loaded test file " + targetPath);
1553 m_testLoaded =
true;
1555 m_testCurrent = intarget;
1557 if (m_indiP_tests.find(
"default"))
1559 if (m_testCurrent ==
"default")
1561 m_indiP_tests[
"default"] = pcf::IndiElement::On;
1565 m_indiP_tests[
"default"] = pcf::IndiElement::Off;
1569 for (
auto i = m_testCommands.begin(); i != m_testCommands.end(); ++i)
1571 if (!m_indiP_tests.find(i->first))
1576 if (i->first == m_testCurrent)
1578 m_indiP_tests[i->first] = pcf::IndiElement::On;
1582 m_indiP_tests[i->first] = pcf::IndiElement::Off;
1586 if (derived().m_indiDriver)
1587 derived().m_indiDriver->sendSetProperty(m_indiP_tests);
1595template <
class derivedT,
typename realT>
1599 if (m_shmimTest ==
"")
1602 if (ImageStreamIO_openIm(&m_testImageStream, m_shmimTest.c_str()) != 0)
1604 derivedT::template log<text_log>(
"could not connect to test channel " + m_shmimTest, logPrio::LOG_WARNING);
1608 if (m_testImageStream.md->size[0] != m_dmWidth)
1610 ImageStreamIO_closeIm(&m_testImageStream);
1611 derivedT::template log<text_log>(
"width mismatch between " + m_shmimTest +
" and configured DM", logPrio::LOG_ERROR);
1615 if (m_testImageStream.md->size[1] != m_dmHeight)
1617 ImageStreamIO_closeIm(&m_testImageStream);
1618 derivedT::template log<text_log>(
"height mismatch between " + m_shmimTest +
" and configured DM", logPrio::LOG_ERROR);
1624 bool testSet = m_testSet;
1627 if (loadTest(m_testCurrent) < 0)
1629 derivedT::template log<text_log>(
"error loading test " + m_testCurrent, logPrio::LOG_ERROR);
1631 m_testSet = testSet;
1636 ImageStreamIO_closeIm(&m_testImageStream);
1637 derivedT::template log<text_log>(
"no test loaded", logPrio::LOG_ERROR);
1641 if (m_testCommand.rows() != m_dmWidth)
1643 ImageStreamIO_closeIm(&m_testImageStream);
1644 derivedT::template log<text_log>(
"width mismatch between test file and configured DM", logPrio::LOG_ERROR);
1648 if (m_testCommand.cols() != m_dmHeight)
1650 ImageStreamIO_closeIm(&m_testImageStream);
1651 derivedT::template log<text_log>(
"height mismatch between test file and configured DM", logPrio::LOG_ERROR);
1655 m_testImageStream.md->write = 1;
1659 memcpy(m_testImageStream.array.raw, m_testCommand.data(), m_dmWidth * m_dmHeight *
sizeof(realT));
1662 clock_gettime(CLOCK_REALTIME, &m_testImageStream.md->writetime);
1665 m_testImageStream.md->atime = m_testImageStream.md->writetime;
1667 m_testImageStream.md->cnt0++;
1668 m_testImageStream.md->write = 0;
1669 ImageStreamIO_sempost(&m_testImageStream, -1);
1674 ImageStreamIO_closeIm(&m_testImageStream);
1676 derived().updateSwitchIfChanged(m_indiP_setTest,
"toggle", pcf::IndiElement::On, pcf::IndiProperty::Busy);
1678 derivedT::template log<text_log>(
"test set");
1683template <
class derivedT,
typename realT>
1686 if (m_shmimTest ==
"")
1689 if (ImageStreamIO_openIm(&m_testImageStream, m_shmimTest.c_str()) != 0)
1691 derivedT::template log<text_log>(
"could not connect to test channel " + m_shmimTest, logPrio::LOG_WARNING);
1695 if (m_testImageStream.md[0].size[0] != m_dmWidth)
1697 ImageStreamIO_closeIm(&m_testImageStream);
1698 derivedT::template log<text_log>(
"width mismatch between " + m_shmimTest +
" and configured DM", logPrio::LOG_ERROR);
1702 if (m_testImageStream.md[0].size[1] != m_dmHeight)
1704 ImageStreamIO_closeIm(&m_testImageStream);
1705 derivedT::template log<text_log>(
"height mismatch between " + m_shmimTest +
" and configured DM", logPrio::LOG_ERROR);
1709 m_testImageStream.md->write = 1;
1713 memset(m_testImageStream.array.raw, 0, m_dmWidth * m_dmHeight *
sizeof(realT));
1716 clock_gettime(CLOCK_REALTIME, &m_testImageStream.md->writetime);
1719 m_testImageStream.md->atime = m_testImageStream.md->writetime;
1721 m_testImageStream.md->cnt0++;
1722 m_testImageStream.md->write = 0;
1725 ImageStreamIO_sempost(&m_testImageStream, -1);
1729 ImageStreamIO_closeIm(&m_testImageStream);
1731 derived().updateSwitchIfChanged(m_indiP_setTest,
"toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle);
1733 derivedT::template log<text_log>(
"test zeroed");
1738template <
class derivedT,
typename realT>
1741 if (derived().m_shmimName ==
"")
1748 for (
int n = 0; n < m_channels; ++n)
1751 snprintf(nstr,
sizeof(nstr),
"%02d", n);
1752 std::string shmimN = derived().m_shmimName + nstr;
1754 if (ImageStreamIO_openIm(&imageStream, shmimN.c_str()) != 0)
1756 derivedT::template log<text_log>(
"could not connect to channel " + shmimN, logPrio::LOG_WARNING);
1760 if (imageStream.md->size[0] != m_dmWidth)
1762 ImageStreamIO_closeIm(&imageStream);
1763 derivedT::template log<text_log>(
"width mismatch between " + shmimN +
" and configured DM", logPrio::LOG_ERROR);
1764 derived().updateSwitchIfChanged(m_indiP_zeroAll,
"request", pcf::IndiElement::Off,
INDI_IDLE);
1768 if (imageStream.md->size[1] != m_dmHeight)
1770 ImageStreamIO_closeIm(&imageStream);
1771 derivedT::template log<text_log>(
"height mismatch between " + shmimN +
" and configured DM", logPrio::LOG_ERROR);
1772 derived().updateSwitchIfChanged(m_indiP_zeroAll,
"request", pcf::IndiElement::Off,
INDI_IDLE);
1776 imageStream.md->write = 1;
1777 memset(imageStream.array.raw, 0, m_dmWidth * m_dmHeight *
sizeof(realT));
1779 clock_gettime(CLOCK_REALTIME, &imageStream.md->writetime);
1782 imageStream.md->atime = imageStream.md->writetime;
1784 imageStream.md->cnt0++;
1785 imageStream.md->write = 0;
1788 if (n == m_channels - 1 && !nosem)
1789 ImageStreamIO_sempost(&imageStream, -1);
1791 ImageStreamIO_closeIm(&imageStream);
1794 derivedT::template log<text_log>(
"all channels zeroed", logPrio::LOG_NOTICE);
1796 derived().updateSwitchIfChanged(m_indiP_zeroAll,
"request", pcf::IndiElement::Off,
INDI_IDLE);
1800 derived().updateSwitchIfChanged(m_indiP_setFlat,
"toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle);
1804 derived().updateSwitchIfChanged(m_indiP_setTest,
"toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle);
1807 if ((rv = clearSat()) < 0)
1809 derivedT::template log<software_error>({__FILE__, __LINE__, errno, rv,
"Error from clearSat"});
1816template <
class derivedT,
typename realT>
1819 if(m_shmimSat ==
"" || m_dmWidth == 0 || m_dmHeight == 0)
1826 std::vector<std::string> sats = {m_shmimSat, m_shmimSatPerc};
1828 for (
size_t n = 0; n < sats.size(); ++n)
1830 std::string shmimN = sats[n];
1832 if (ImageStreamIO_openIm(&imageStream, shmimN.c_str()) != 0)
1834 derivedT::template log<text_log>(
"could not connect to sat map " + shmimN, logPrio::LOG_WARNING);
1838 if (imageStream.md->size[0] != m_dmWidth)
1840 ImageStreamIO_closeIm(&imageStream);
1841 derivedT::template log<text_log>(
"width mismatch between " + shmimN +
" and configured DM", logPrio::LOG_ERROR);
1842 derived().updateSwitchIfChanged(m_indiP_zeroAll,
"request", pcf::IndiElement::Off,
INDI_IDLE);
1846 if (imageStream.md->size[1] != m_dmHeight)
1848 ImageStreamIO_closeIm(&imageStream);
1849 derivedT::template log<text_log>(
"height mismatch between " + shmimN +
" and configured DM", logPrio::LOG_ERROR);
1850 derived().updateSwitchIfChanged(m_indiP_zeroAll,
"request", pcf::IndiElement::Off,
INDI_IDLE);
1854 imageStream.md->write = 1;
1855 memset(imageStream.array.raw, 0, m_dmWidth * m_dmHeight * ImageStreamIO_typesize(imageStream.md->datatype));
1857 clock_gettime(CLOCK_REALTIME, &imageStream.md->writetime);
1860 imageStream.md->atime = imageStream.md->writetime;
1862 imageStream.md->cnt0++;
1863 imageStream.md->write = 0;
1865 ImageStreamIO_closeIm(&imageStream);
1868 m_accumSatMap.setZero();
1869 m_instSatMap.setZero();
1874template <
class derivedT,
typename realT>
1880template <
class derivedT,
typename realT>
1884 m_satThreadID = syscall(SYS_gettid);
1887 while (m_satThreadInit ==
true && derived().shutdown() == 0)
1891 if (derived().shutdown())
1894 uint32_t imsize[3] = {0, 0, 0};
1897 while ((m_shmimSat ==
"" || m_accumSatMap.rows() == 0 || m_accumSatMap.cols() == 0) && !derived().shutdown())
1902 if (derived().shutdown())
1907 imsize[0] = m_dmWidth;
1908 imsize[1] = m_dmHeight;
1911 ImageStreamIO_createIm_gpu(&m_satImageStream, m_shmimSat.c_str(), 3, imsize,
IMAGESTRUCT_UINT8, -1, 1, IMAGE_NB_SEMAPHORE, 0, CIRCULAR_BUFFER | ZAXIS_TEMPORAL, 0);
1912 ImageStreamIO_createIm_gpu(&m_satPercImageStream, m_shmimSatPerc.c_str(), 3, imsize,
IMAGESTRUCT_FLOAT, -1, 1, IMAGE_NB_SEMAPHORE, 0, CIRCULAR_BUFFER | ZAXIS_TEMPORAL, 0);
1916 m_satImageStream.md->cnt1 = 0;
1917 m_satPercImageStream.md->cnt1 = 0;
1920 mx::improc::eigenImage<uint8_t> satmap(m_dmWidth, m_dmHeight);
1923 double t_accumst = mx::sys::get_curr_time();
1926 while (!derived().shutdown())
1930 if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
1932 derivedT::template log<software_critical>({__FILE__, __LINE__, errno, 0,
"clock_gettime"});
1938 if (sem_timedwait(&m_satSemaphore, &ts) == 0)
1941 for (
int rr = 0; rr < m_instSatMap.rows(); ++rr)
1943 for (
int cc = 0; cc < m_instSatMap.cols(); ++cc)
1945 m_accumSatMap(rr, cc) += m_instSatMap(rr, cc);
1951 if (mx::sys::get_curr_time(ts) - t_accumst < m_satAvgInt / 1000.0)
1958 for (
int rr = 0; rr < m_instSatMap.rows(); ++rr)
1960 for (
int cc = 0; cc < m_instSatMap.cols(); ++cc)
1962 m_satPercMap(rr, cc) = m_accumSatMap(rr, cc) / naccum;
1963 if (m_satPercMap(rr, cc) >= m_percThreshold)
1967 satmap(rr, cc) = (m_accumSatMap(rr, cc) > 0);
1973 if (m_overSatAct / (m_satPercMap.rows() * m_satPercMap.cols() * 0.75) > m_intervalSatThreshold)
1975 ++m_intervalSatExceeds;
1979 m_intervalSatExceeds = 0;
1983 if (m_intervalSatExceeds >= m_intervalSatCountThreshold)
1985 m_intervalSatTrip =
true;
1988 m_satImageStream.md->write = 1;
1989 m_satPercImageStream.md->write = 1;
1991 memcpy(m_satImageStream.array.raw, satmap.data(), m_dmWidth * m_dmHeight *
sizeof(uint8_t));
1992 memcpy(m_satPercImageStream.array.raw, m_satPercMap.data(), m_dmWidth * m_dmHeight *
sizeof(
float));
1995 clock_gettime(CLOCK_REALTIME, &m_satImageStream.md->writetime);
1996 m_satPercImageStream.md->writetime = m_satImageStream.md->writetime;
1999 m_satImageStream.md->atime = m_satImageStream.md->writetime;
2000 m_satPercImageStream.md->atime = m_satPercImageStream.md->writetime;
2003 m_satImageStream.md->cnt1 = 0;
2004 m_satPercImageStream.md->cnt1 = 0;
2007 m_satImageStream.md->cnt0++;
2008 m_satPercImageStream.md->cnt0++;
2010 m_satImageStream.writetimearray[0] = m_satImageStream.md->writetime;
2011 m_satImageStream.atimearray[0] = m_satImageStream.md->atime;
2012 m_satImageStream.cntarray[0] = m_satImageStream.md->cnt0;
2014 m_satPercImageStream.writetimearray[0] = m_satPercImageStream.md->writetime;
2015 m_satPercImageStream.atimearray[0] = m_satPercImageStream.md->atime;
2016 m_satPercImageStream.cntarray[0] = m_satPercImageStream.md->cnt0;
2019 m_satImageStream.md->write = 0;
2020 ImageStreamIO_sempost(&m_satImageStream, -1);
2022 m_satPercImageStream.md->write = 0;
2023 ImageStreamIO_sempost(&m_satPercImageStream, -1);
2025 m_accumSatMap.setZero();
2027 t_accumst = mx::sys::get_curr_time(ts);
2039 if (errno != ETIMEDOUT)
2041 derivedT::template log<software_error>({__FILE__, __LINE__, errno,
"sem_timedwait"});
2049 ImageStreamIO_destroyIm(&m_satImageStream);
2051 ImageStreamIO_destroyIm(&m_satPercImageStream);
2055template <
class derivedT,
typename realT>
2058 if (!derived().m_indiDriver)
2064template <
class derivedT,
typename realT>
2066 const pcf::IndiProperty &
ipRecv)
2068 return static_cast<derivedT *
>(app)->newCallBack_init(
ipRecv);
2071template <
class derivedT,
typename realT>
2074 if (
ipRecv.createUniqueKey() != m_indiP_init.createUniqueKey())
2076 return derivedT::template log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2079 if (!
ipRecv.find(
"request"))
2082 if (
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
2084 return derived().initDM();
2089template <
class derivedT,
typename realT>
2091 const pcf::IndiProperty &
ipRecv)
2093 return static_cast<derivedT *
>(app)->newCallBack_zero(
ipRecv);
2096template <
class derivedT,
typename realT>
2099 if (
ipRecv.createUniqueKey() != m_indiP_zero.createUniqueKey())
2101 return derivedT::template log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2104 if (!
ipRecv.find(
"request"))
2107 if (
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
2109 return derived().zeroDM();
2114template <
class derivedT,
typename realT>
2116 const pcf::IndiProperty &
ipRecv)
2118 return static_cast<derivedT *
>(app)->newCallBack_release(
ipRecv);
2121template <
class derivedT,
typename realT>
2124 if (
ipRecv.createUniqueKey() != m_indiP_release.createUniqueKey())
2126 return derivedT::template log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2129 if (!
ipRecv.find(
"request"))
2132 if (
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
2139template <
class derivedT,
typename realT>
2141 const pcf::IndiProperty &
ipRecv)
2143 return static_cast<derivedT *
>(app)->newCallBack_flats(
ipRecv);
2146template <
class derivedT,
typename realT>
2149 if (
ipRecv.createUniqueKey() != m_indiP_flats.createUniqueKey())
2151 derivedT::template log<software_error>({__FILE__, __LINE__,
"invalid indi property received"});
2155 std::string newFlat;
2157 if (
ipRecv.find(
"default"))
2159 if (
ipRecv[
"default"].getSwitchState() == pcf::IndiElement::On)
2161 newFlat =
"default";
2166 for (
auto i = m_flatCommands.begin(); i != m_flatCommands.end(); ++i)
2168 if (!
ipRecv.find(i->first))
2171 if (
ipRecv[i->first].getSwitchState() == pcf::IndiElement::On)
2175 derivedT::template log<text_log>(
"More than one flat selected", logPrio::LOG_ERROR);
2186 return loadFlat(newFlat);
2189template <
class derivedT,
typename realT>
2191 const pcf::IndiProperty &
ipRecv)
2193 return static_cast<derivedT *
>(app)->newCallBack_setFlat(
ipRecv);
2196template <
class derivedT,
typename realT>
2199 if (
ipRecv.createUniqueKey() != m_indiP_setFlat.createUniqueKey())
2201 return derivedT::template log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2204 if (!
ipRecv.find(
"toggle"))
2207 if (
ipRecv[
"toggle"] == pcf::IndiElement::On)
2217template <
class derivedT,
typename realT>
2219 const pcf::IndiProperty &
ipRecv)
2221 return static_cast<derivedT *
>(app)->newCallBack_tests(
ipRecv);
2224template <
class derivedT,
typename realT>
2227 if (
ipRecv.createUniqueKey() != m_indiP_tests.createUniqueKey())
2229 derivedT::template log<software_error>({__FILE__, __LINE__,
"invalid indi property received"});
2233 std::string newTest;
2235 if (
ipRecv.find(
"default"))
2237 if (
ipRecv[
"default"].getSwitchState() == pcf::IndiElement::On)
2239 newTest =
"default";
2244 for (
auto i = m_testCommands.begin(); i != m_testCommands.end(); ++i)
2246 if (!
ipRecv.find(i->first))
2249 if (
ipRecv[i->first].getSwitchState() == pcf::IndiElement::On)
2253 derivedT::template log<text_log>(
"More than one test selected", logPrio::LOG_ERROR);
2264 return loadTest(newTest);
2267template <
class derivedT,
typename realT>
2269 const pcf::IndiProperty &
ipRecv)
2271 return static_cast<derivedT *
>(app)->newCallBack_setTest(
ipRecv);
2274template <
class derivedT,
typename realT>
2277 if (
ipRecv.createUniqueKey() != m_indiP_setTest.createUniqueKey())
2279 return derivedT::template log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2282 if (!
ipRecv.find(
"toggle"))
2285 if (
ipRecv[
"toggle"] == pcf::IndiElement::On)
2295template <
class derivedT,
typename realT>
2297 const pcf::IndiProperty &
ipRecv)
2299 return static_cast<derivedT *
>(app)->newCallBack_zeroAll(
ipRecv);
2302template <
class derivedT,
typename realT>
2305 if (
ipRecv.createUniqueKey() != m_indiP_zeroAll.createUniqueKey())
2307 return derivedT::template log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2310 if (!
ipRecv.find(
"request"))
2313 if (
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
2317 std::lock_guard<std::mutex> guard(derived().m_indiMutex);
2327#define DM_SETUP_CONFIG(cfig) \
2328 if (dmT::setupConfig(cfig) < 0) \
2330 log<software_error>({__FILE__, __LINE__, "Error from dmT::setupConfig"}); \
2331 m_shutdown = true; \
2339#define DM_LOAD_CONFIG(cfig) \
2340 if (dmT::loadConfig(cfig) < 0) \
2342 return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::loadConfig"}); \
2346#define DM_APP_STARTUP \
2347 if (dmT::appStartup() < 0) \
2349 return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::appStartup"}); \
2353#define DM_APP_LOGIC \
2354 if (dmT::appLogic() < 0) \
2356 return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::appLogic"}); \
2360#define DM_UPDATE_INDI \
2361 if (dmT::updateINDI() < 0) \
2363 return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::updateINDI"}); \
2367#define DM_APP_SHUTDOWN \
2368 if (dmT::appShutdown() < 0) \
2370 return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::appShutdown"}); \
#define IMAGESTRUCT_FLOAT
#define IMAGESTRUCT_UINT8
std::string m_calibPath
The path to this DM's calibration files.
std::map< std::string, std::string > m_flatCommands
Map of flat file name to full path.
int newCallBack_tests(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
bool m_testSet
Flag indicating whether the test command has been set.
std::string m_flatPath
The path to this DM's flat files (usually the same as calibPath)
pcf::IndiProperty m_indiP_setFlat
INDI toggle switch to set the current flat.
static void satThreadStart(dm *d)
Thread starter, called by MagAOXApp::threadStart on thread construction. Calls satThreadExec.
pcf::IndiProperty m_indiP_init
uint32_t m_dmWidth
The width of the images in the stream.
pcf::IndiProperty m_indiP_flats
INDI Selection switch containing the flat files.
int zeroFlat()
Zero the flat command on the DM.
static int st_newCallBack_init(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for initializing the DM.
int checkFlats()
Check the flats directory and update the list of flats if anything changes.
pcf::IndiProperty m_indiP_testShmim
Publish the shmim being used for the test command.
int checkTests()
Check the tests directory and update the list of tests if anything changes.
static int st_newCallBack_tests(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for selecting the test file.
std::string m_flatDefault
The file name of the this DM's default flat command. Path and extension will be ignored and can be om...
int setTest()
Send the current test command to the DM.
int clearSat()
Clear the saturation maps and zero the shared membory.
static int st_newCallBack_zero(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for initializing the DM.
int loadTest(const std::string &target)
Load a test file.
int loadFlat(const std::string &target)
Load a flat file.
int m_satThreadPrio
Priority of the saturation thread, should normally be > 0.
IMAGE m_satPercImageStream
The ImageStreamIO shared memory buffer for the sat percentage map.
std::string m_calibRelDir
The directory relative to the calibPath. Set this before calling dm<derivedT,realT>::loadConfig().
pcf::IndiProperty m_indiP_setTest
INDI toggle switch to set the current test pattern.
int findDMChannels()
Find the DM comb channels.
static constexpr uint8_t m_dmDataType
The ImageStreamIO type code.
pcf::IndiProperty m_indiP_tests
INDI Selection switch containing the test pattern files.
std::string m_shmimFlat
The name of the shmim stream to write the flat to.
int processImage(void *curr_src, const dev::shmimT &sp)
Called by shmimMonitor when a new DM command is available. This is just a pass-through to derivedT::c...
uint32_t m_dmHeight
The height of the images in the stream.
int appShutdown()
DM shutdown.
std::string m_testPath
The path to this DM's test files (default is calibPath/tests;.
int newCallBack_zeroAll(const pcf::IndiProperty &ipRecv)
The callback for the zeroAll toggle switch, called by the static version.
bool m_flatSet
Flag indicating whether the flat command has been set.
pcf::IndiProperty m_satThreadProp
The property to hold the saturation thread details.
std::string m_shmimSatPerc
The name of the shmim stream to write the saturation percentage map to.
int newCallBack_setTest(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
static int st_newCallBack_flats(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for selecting the flat file.
std::string m_testCurrent
bool m_flatLoaded
Flag indicating whether a flat is loaded in memory.
bool m_testLoaded
Flag indicating whether a test command is loaded in memory.
mx::improc::eigenImage< float > m_satPercMap
Map of the percentage of time each actator was saturated during the avg. interval.
int setFlat(bool update=false)
Send the current flat command to the DM.
static int st_newCallBack_zeroAll(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for zeroing all channels.
int newCallBack_init(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
int zeroAll(bool nosem=false)
Zero all channels.
pid_t m_satThreadID
The ID of the saturation thread.
int zeroTest()
Zero the test command on the DM.
int m_satAvgInt
The time in milliseconds to accumulate saturation over.
int newCallBack_release(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
std::vector< std::string > m_satTriggerProperty
int updateINDI()
Update the INDI properties for this device controller.
int newCallBack_flats(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
sem_t m_satSemaphore
Semaphore used to tell the saturation thread to run.
pcf::IndiProperty m_indiP_flat
Property used to set and report the current flat.
IMAGE m_satImageStream
The ImageStreamIO shared memory buffer for the sat map.
int newCallBack_zero(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
bool m_satThreadInit
Synchronizer for thread startup, to allow priority setting to finish.
std::vector< std::string > m_satTriggerDevice
mx::improc::eigenImage< realT > m_flatCommand
Data storage for the flat command.
mx::improc::eigenImage< uint8_t > m_instSatMap
The instantaneous saturation map, 0/1, set by the commandDM() function of the derived class.
void satThreadExec()
Execute saturation processing.
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
static int st_newCallBack_release(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for initializing the DM.
pcf::IndiProperty m_indiP_flatShmim
Publish the shmim being used for the flat.
float m_intervalSatThreshold
int whilePowerOff()
DM Poweroff Updates.
static int st_newCallBack_setFlat(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for setting the flat.
static int st_newCallBack_setTest(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for setting the test shape.
pcf::IndiProperty m_indiP_zeroAll
std::map< std::string, std::string > m_testCommands
Map of test file name to full path.
IMAGE m_testImageStream
The ImageStreamIO shared memory buffer for the test.
std::string m_testDefault
The file name of the this DM's default test command. Path and extension will be ignored and can be om...
int m_channels
The number of dmcomb channels found as part of allocation.
std::string m_shmimTest
The name of the shmim stream to write the test to.
int m_intervalSatCountThreshold
int appStartup()
Startup function.
int newCallBack_setFlat(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
mx::improc::eigenImage< uint16_t > m_accumSatMap
The accumulated saturation map, which acccumulates for m_satAvgInt then is publised as a 0/1 image.
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
int onPowerOff()
DM Poweroff.
std::string m_shmimSat
The name of the shmim stream to write the saturation map to.
mx::improc::eigenImage< realT > m_testCommand
Data storage for the test command.
IMAGE m_flatImageStream
The ImageStreamIO shared memory buffer for the flat.
std::thread m_satThread
A separate thread for the actual saturation processing.
pcf::IndiProperty m_indiP_release
pcf::IndiProperty m_indiP_zero
int releaseDM()
Calls derived()->releaseDM() and then 0s all channels and the sat map.
int allocate(const dev::shmimT &sp)
Called after shmimMonitor connects to the dmXXdisp stream. Checks for proper size.
std::string m_flatCurrent
The name of the current flat command.
int appLogic()
DM application logic.
constexpr uint8_t ImageStreamTypeCode< float >()
constexpr uint8_t ImageStreamTypeCode()
constexpr uint8_t ImageStreamTypeCode< double >()
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