16 #include <mx/improc/eigenImage.hpp>
17 #include <mx/ioutils/fits/fitsFile.hpp>
19 #include <boost/filesystem/operations.hpp>
21 #include "../../ImageStreamIO/ImageStruct.hpp"
30 template <
typename typeT>
39 return _DATATYPE_FLOAT;
45 return _DATATYPE_DOUBLE;
67 template <
class derivedT,
typename realT>
96 static constexpr uint8_t
m_dmDataType = ImageStreamTypeCode<realT>();
357 pcf::IndiProperty ipFreq(pcf::IndiProperty::Switch);
361 ipFreq.add(pcf::IndiElement(
"toggle"));
362 ipFreq[
"toggle"] = pcf::IndiElement::Off;
363 derived().sendNewProperty(ipFreq);
365 derivedT::template log<text_log>(
"DM saturation threshold exceeded. Loop opened.",
logPrio::LOG_WARNING);
407 const pcf::IndiProperty &
ipRecv
423 const pcf::IndiProperty &
ipRecv
439 const pcf::IndiProperty &
ipRecv
455 const pcf::IndiProperty &
ipRecv
471 const pcf::IndiProperty &
ipRecv
487 const pcf::IndiProperty &
ipRecv
503 const pcf::IndiProperty &
ipRecv
519 const pcf::IndiProperty &
ipRecv
543 typedef uint16_t cbIndexT;
545 double m_t0 {0}, m_tf {0}, m_tsat0 {0}, m_tsatf {0};
546 double m_tact0 {0}, m_tact1 {0}, m_tact2 {0}, m_tact3 {0}, m_tact4 {0};
548 mx::sigproc::circularBufferIndex<double, cbIndexT> m_piTimes;
550 mx::sigproc::circularBufferIndex<double, cbIndexT> m_satSem;
552 mx::sigproc::circularBufferIndex<double, cbIndexT> m_actProc;
554 mx::sigproc::circularBufferIndex<double, cbIndexT> m_actCom;
556 mx::sigproc::circularBufferIndex<double, cbIndexT> m_satUp;
558 std::vector<double> m_piTimesD;
559 std::vector<double> m_satSemD;
560 std::vector<double> m_actProcD;
561 std::vector<double> m_actComD;
562 std::vector<double> m_satUpD;
569 return *
static_cast<derivedT *
>(
this);
573 template <
class derivedT,
typename realT>
576 config.add(
"dm.calibPath",
"",
"dm.calibPath", argType::Required,
"dm",
"calibPath",
false,
"string",
"The path to calibration files, relative to the MagAO-X calibration path.");
578 config.add(
"dm.flatPath",
"",
"dm.flatPath", argType::Required,
"dm",
"flatPath",
false,
"string",
"The path to flat files. Default is the calibration path.");
579 config.add(
"dm.flatDefault",
"",
"dm.flatDefault", argType::Required,
"dm",
"flatDefault",
false,
"string",
"The default flat file (path and extension are not required).");
581 config.add(
"dm.testPath",
"",
"dm.testPath", argType::Required,
"dm",
"testPath",
false,
"string",
"The path to test files. Default is the calibration path plus /tests.");
582 config.add(
"dm.testDefault",
"",
"dm.testDefault", argType::Required,
"dm",
"testDefault",
false,
"string",
"The default test file (path and extension are not required).");
587 config.add(
"dm.threadPrio",
"",
"dm.threadPrio", argType::Required,
"dm",
"threadPrio",
false,
"int",
"The real-time priority of the dm control thread.");
588 config.add(
"dm.cpuset",
"",
"dm.cpuset", argType::Required,
"dm",
"cpuset",
false,
"int",
"The cpuset for the dm control thread.");
590 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.");
592 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). ");
594 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). ");
596 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.");
598 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.");
600 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.");
602 config.add(
"dm.width",
"",
"dm.width", argType::Required,
"dm",
"width",
false,
"string",
"The width of the DM in actuators.");
603 config.add(
"dm.height",
"",
"dm.height", argType::Required,
"dm",
"height",
false,
"string",
"The height of the DM in actuators.");
605 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.");
606 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.");
607 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.");
609 config.add(
"dm.satTriggerDevice",
"",
"dm.satTriggerDevice", argType::Required,
"dm",
"satTriggerDevice",
false,
"vector<string>",
"Device(s) with a toggle switch to toggle on saturation trigger.");
610 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.");
615 template <
class derivedT,
typename realT>
619 m_calibPath = derived().m_calibDir +
"/" + m_calibRelDir;
620 config(m_calibPath,
"dm.calibPath");
623 m_flatPath = m_calibPath +
"/flats";
624 config(m_flatPath,
"dm.flatPath");
626 config(m_flatDefault,
"dm.flatDefault");
627 if (m_flatDefault !=
"")
629 m_flatDefault = mx::ioutils::pathStem(m_flatDefault);
630 m_flatCurrent =
"default";
634 m_testPath = m_calibPath +
"/tests";
635 config(m_testPath,
"dm.testPath");
637 config(m_testDefault,
"dm.testDefault");
638 if (m_testDefault !=
"")
640 m_testDefault = mx::ioutils::pathStem(m_testDefault);
641 m_testCurrent =
"default";
646 config(derived().m_smThreadPrio,
"dm.threadPrio");
647 config(derived().m_smCpuset,
"dm.cpuset");
649 config(derived().m_shmimName,
"dm.shmimName");
651 if (derived().m_shmimName !=
"")
653 m_shmimFlat = derived().m_shmimName +
"00";
654 config(m_shmimFlat,
"dm.shmimFlat");
656 m_shmimTest = derived().m_shmimName +
"02";
657 config(m_shmimTest,
"dm.shmimTest");
659 m_shmimSat = derived().m_shmimName +
"ST";
660 config(m_shmimSat,
"dm.shmimSat");
662 m_shmimSatPerc = derived().m_shmimName +
"SP";
663 config(m_shmimSatPerc,
"dm.shmimSatPerc");
665 config(m_satAvgInt,
"dm.satAvgInt");
669 config.isSet(
"dm.shmimFlat");
670 config.isSet(
"dm.shmimTest");
671 config.isSet(
"dm.shmimSat");
672 config.isSet(
"dm.shmimSatPerc");
673 config.isSet(
"dm.satAvgInt");
676 config(m_dmWidth,
"dm.width");
677 config(m_dmHeight,
"dm.height");
679 config(m_percThreshold,
"dm.percThreshold");
680 config(m_intervalSatThreshold,
"dm.intervalSatThreshold");
681 config(m_intervalSatCountThreshold,
"dm.intervalSatCountThreshold");
682 config(m_satTriggerDevice,
"dm.satTriggerDevice");
683 config(m_satTriggerProperty,
"dm.satTriggerProperty");
688 template <
class derivedT,
typename realT>
691 if (m_dmDataType == 0)
693 derivedT::template log<software_error>({__FILE__, __LINE__,
"unsupported DM data type"});
702 m_indiP_flatShmim = pcf::IndiProperty(pcf::IndiProperty::Text);
703 m_indiP_flatShmim.setDevice(derived().configName());
704 m_indiP_flatShmim.setName(
"flat_shmim");
705 m_indiP_flatShmim.setPerm(pcf::IndiProperty::ReadOnly);
706 m_indiP_flatShmim.setState(pcf::IndiProperty::Idle);
707 m_indiP_flatShmim.add(pcf::IndiElement(
"channel"));
708 m_indiP_flatShmim[
"channel"] = m_shmimFlat;
710 if (derived().registerIndiPropertyReadOnly(m_indiP_flatShmim) < 0)
712 #ifndef DM_TEST_NOLOG
713 derivedT::template log<software_error>({__FILE__, __LINE__});
719 derived().createStandardIndiToggleSw(m_indiP_setFlat,
"flat_set");
720 if (derived().registerIndiPropertyNew(m_indiP_setFlat, st_newCallBack_setFlat) < 0)
722 #ifndef DM_TEST_NOLOG
723 derivedT::template log<software_error>({__FILE__, __LINE__});
733 m_indiP_testShmim = pcf::IndiProperty(pcf::IndiProperty::Text);
734 m_indiP_testShmim.setDevice(derived().configName());
735 m_indiP_testShmim.setName(
"test_shmim");
736 m_indiP_testShmim.setPerm(pcf::IndiProperty::ReadOnly);
737 m_indiP_testShmim.setState(pcf::IndiProperty::Idle);
738 m_indiP_testShmim.add(pcf::IndiElement(
"channel"));
739 m_indiP_testShmim[
"channel"] = m_shmimTest;
740 derived().createStandardIndiToggleSw(m_indiP_setTest,
"test_shmim");
741 if (derived().registerIndiPropertyReadOnly(m_indiP_testShmim) < 0)
743 #ifndef DM_TEST_NOLOG
744 derivedT::template log<software_error>({__FILE__, __LINE__});
750 derived().createStandardIndiToggleSw(m_indiP_setTest,
"test_set");
751 if (derived().registerIndiPropertyNew(m_indiP_setTest, st_newCallBack_setTest) < 0)
753 #ifndef DM_TEST_NOLOG
754 derivedT::template log<software_error>({__FILE__, __LINE__});
760 derived().createStandardIndiRequestSw(m_indiP_init,
"initDM");
761 if (derived().registerIndiPropertyNew(m_indiP_init, st_newCallBack_init) < 0)
763 #ifndef DM_TEST_NOLOG
764 derivedT::template log<software_error>({__FILE__, __LINE__});
770 derived().createStandardIndiRequestSw(m_indiP_zero,
"zeroDM");
771 if (derived().registerIndiPropertyNew(m_indiP_zero, st_newCallBack_zero) < 0)
773 #ifndef DM_TEST_NOLOG
774 derivedT::template log<software_error>({__FILE__, __LINE__});
780 derived().createStandardIndiRequestSw(m_indiP_release,
"releaseDM");
781 if (derived().registerIndiPropertyNew(m_indiP_release, st_newCallBack_release) < 0)
783 #ifndef DM_TEST_NOLOG
784 derivedT::template log<software_error>({__FILE__, __LINE__});
789 derived().createStandardIndiRequestSw(m_indiP_zeroAll,
"zeroAll");
790 if (derived().registerIndiPropertyNew(m_indiP_zeroAll, st_newCallBack_zeroAll) < 0)
792 #ifndef DM_TEST_NOLOG
793 derivedT::template log<software_error>({__FILE__, __LINE__});
798 if (m_flatDefault !=
"")
803 if (m_testDefault !=
"")
808 if (sem_init(&m_satSemaphore, 0, 0) < 0)
810 return derivedT::template
log<software_critical, -1>({__FILE__, __LINE__, errno, 0,
"Initializing sat semaphore"});
813 if (derived().threadStart(m_satThread, m_satThreadInit, m_satThreadID, m_satThreadProp, m_satThreadPrio,
"",
"saturation",
this, satThreadStart) < 0)
815 derivedT::template
log<software_error, -1>({__FILE__, __LINE__});
822 template <
class derivedT,
typename realT>
826 if (pthread_tryjoin_np(m_satThread.native_handle(), 0) == 0)
828 derivedT::template log<software_error>({__FILE__, __LINE__,
"saturation thread has exited"});
837 if (m_intervalSatTrip)
840 m_intervalSatTrip =
false;
844 static uint64_t lastMono = 0;
846 if(m_piTimes.size() >= m_piTimes.maxEntries() && m_piTimes.maxEntries() > 0 && m_piTimes.mono() != lastMono)
848 cbIndexT refEntry = m_piTimes.earliest();
850 m_piTimesD.resize(m_piTimes.maxEntries());
851 m_satSemD.resize(m_satSem.maxEntries());
852 m_actProcD.resize(m_actProc.maxEntries());
853 m_actComD.resize(m_actCom.maxEntries());
854 m_satUpD.resize(m_satUp.maxEntries());
856 for(
size_t n=0; n < m_piTimesD.size(); ++n)
858 m_piTimesD[n] = m_piTimes.at(refEntry,n);
859 m_satSemD[n] = m_satSem.at(refEntry,n);
860 m_actProcD[n] = m_actProc.at(refEntry,n);
861 m_actComD[n] = m_actCom.at(refEntry,n);
862 m_satUpD[n] = m_satUp.at(refEntry,n);
865 std::cerr <<
"Act. Process: " << mx::math::vectorMean(m_actProcD) <<
" +/- " << sqrt(mx::math::vectorVariance(m_actProcD)) <<
"\n";
866 std::cerr <<
"Act. Command: " << mx::math::vectorMean(m_actComD) <<
" +/- " << sqrt(mx::math::vectorVariance(m_actComD)) <<
"\n";
867 std::cerr <<
"Sat. Update: " << mx::math::vectorMean(m_satUpD) <<
" +/- " << sqrt(mx::math::vectorVariance(m_satUpD)) <<
"\n";
868 std::cerr <<
"Tot. CommandDM: " << mx::math::vectorMean(m_piTimesD) <<
" +/- " << sqrt(mx::math::vectorVariance(m_piTimesD)) <<
"\n";
869 std::cerr <<
"Sat. Semaphore: " << mx::math::vectorMean(m_satSemD) <<
" +/- " << sqrt(mx::math::vectorVariance(m_satSemD)) <<
"\n";
872 lastMono = m_piTimes.mono();
879 template <
class derivedT,
typename realT>
882 if (m_satThread.joinable())
884 pthread_kill(m_satThread.native_handle(), SIGUSR1);
897 template <
class derivedT,
typename realT>
905 template <
class derivedT,
typename realT>
914 template <
class derivedT,
typename realT>
917 std::vector<std::string> dmlist = mx::ioutils::getFileNames(
"/milk/shm/", derived().m_shmimName,
".im",
".shm");
919 if (dmlist.size() == 0)
921 derivedT::template log<software_error>({__FILE__, __LINE__,
"no dm channels found for " + derived().m_shmimName});
926 for (
size_t n = 0; n < dmlist.size(); ++n)
929 snprintf(nstr,
sizeof(nstr),
"%02d.im.shm", (
int)n);
930 std::string tgt = derived().m_shmimName;
933 for (
size_t m = 0; m < dmlist.size(); ++m)
935 if (dmlist[m].find(tgt) != std::string::npos)
937 if ((
int)n > m_channels)
945 derivedT::template log<text_log>({std::string(
"Found ") + std::to_string(m_channels) +
" channels for " + derived().m_shmimName});
950 template <
class derivedT,
typename realT>
953 static_cast<void>(sp);
957 if (derived().m_width != m_dmWidth)
959 derivedT::template log<software_critical>({__FILE__, __LINE__,
"shmim width does not match configured DM width"});
963 if (derived().m_height != m_dmHeight)
965 derivedT::template log<software_critical>({__FILE__, __LINE__,
"shmim height does not match configured DM height"});
969 if (derived().m_dataType != m_dmDataType)
971 derivedT::template log<software_critical>({__FILE__, __LINE__,
"shmim data type does not match configured DM data type"});
978 m_instSatMap.resize(m_dmWidth, m_dmHeight);
979 m_instSatMap.setZero();
981 m_accumSatMap.resize(m_dmWidth, m_dmHeight);
982 m_accumSatMap.setZero();
984 m_satPercMap.resize(m_dmWidth, m_dmHeight);
985 m_satPercMap.setZero();
987 if (findDMChannels() < 0)
989 derivedT::template log<software_critical>({__FILE__, __LINE__,
"error finding DM channels"});
995 m_piTimes.maxEntries(2000);
996 m_satSem.maxEntries(2000);
997 m_actProc.maxEntries(2000);
998 m_actCom.maxEntries(2000);
999 m_satUp.maxEntries(2000);
1005 template <
class derivedT,
typename realT>
1009 static_cast<void>(sp);
1011 #ifdef XWC_DMTIMINGS
1012 m_t0 = mx::sys::get_curr_time();
1015 int rv = derived().commandDM(curr_src);
1017 #ifdef XWC_DMTIMINGS
1018 m_tf = mx::sys::get_curr_time();
1023 derivedT::template log<software_critical>({__FILE__, __LINE__, errno, rv,
"Error from commandDM"});
1027 #ifdef XWC_DMTIMINGS
1028 m_tsat0 = mx::sys::get_curr_time();
1032 if (sem_post(&m_satSemaphore) < 0)
1034 derivedT::template log<software_critical>({__FILE__, __LINE__, errno, 0,
"Error posting to semaphore"});
1038 #ifdef XWC_DMTIMINGS
1039 m_tsatf = mx::sys::get_curr_time();
1042 #ifdef XWC_DMTIMINGS
1044 if(m_piTimes.maxEntries() > 0)
1046 m_piTimes.nextEntry(m_tf-m_t0);
1047 m_satSem.nextEntry(m_tsatf - m_tsat0);
1048 m_actProc.nextEntry(m_tact1 - m_tact0);
1049 m_actCom.nextEntry(m_tact2 - m_tact1);
1050 m_satUp.nextEntry(m_tact4 - m_tact3);
1056 template <
class derivedT,
typename realT>
1060 if ((rv = derived().releaseDM()) < 0)
1062 derivedT::template log<software_critical>({__FILE__, __LINE__, errno, rv,
"Error from releaseDM"});
1066 if ((rv = zeroAll(
true)) < 0)
1068 derivedT::template log<software_error>({__FILE__, __LINE__, errno, rv,
"Error from zeroAll"});
1075 template <
class derivedT,
typename realT>
1078 std::vector<std::string> tfs = mx::ioutils::getFileNames(m_flatPath,
"",
"",
".fits");
1081 for (
size_t n = 0; n < tfs.size(); ++n)
1083 if (mx::ioutils::pathStem(tfs[n]) ==
"default")
1085 tfs.erase(tfs.begin() + n);
1090 unsigned m_nFlatFiles = 5;
1093 if (tfs.size() >= m_nFlatFiles)
1095 std::vector<std::time_t> wtimes(tfs.size());
1097 for (
size_t n = 0; n < wtimes.size(); ++n)
1099 wtimes[n] = boost::filesystem::last_write_time(tfs[n]);
1102 std::sort(wtimes.begin(), wtimes.end());
1104 std::time_t tn = wtimes[wtimes.size() - m_nFlatFiles];
1106 for (
size_t n = 0; n < tfs.size(); ++n)
1108 std::time_t lmt = boost::filesystem::last_write_time(tfs[n]);
1111 tfs.erase(tfs.begin() + n);
1117 for (
auto it = m_flatCommands.begin();
it != m_flatCommands.end(); ++
it)
1122 bool changed =
false;
1123 for (
size_t n = 0; n < tfs.size(); ++n)
1125 auto ir = m_flatCommands.insert(std::pair<std::string, std::string>(mx::ioutils::pathStem(tfs[n]), tfs[n]));
1126 if (ir.second ==
true)
1129 ir.first->second = tfs[n];
1132 for (
auto it = m_flatCommands.begin();
it != m_flatCommands.end(); ++
it)
1134 if (
it->second ==
"")
1140 m_flatCommands.erase(itdel);
1147 if (derived().m_indiDriver)
1149 derived().m_indiDriver->sendDelProperty(m_indiP_flats);
1150 derived().m_indiNewCallBacks.erase(m_indiP_flats.createUniqueKey());
1153 m_indiP_flats = pcf::IndiProperty(pcf::IndiProperty::Switch);
1154 m_indiP_flats.setDevice(derived().configName());
1155 m_indiP_flats.setName(
"flat");
1156 m_indiP_flats.setPerm(pcf::IndiProperty::ReadWrite);
1157 m_indiP_flats.setState(pcf::IndiProperty::Idle);
1158 m_indiP_flats.setRule(pcf::IndiProperty::OneOfMany);
1161 for (
auto it = m_flatCommands.begin();
it != m_flatCommands.end(); ++
it)
1163 if (
it->first == m_flatCurrent || m_flatCurrent ==
"")
1165 m_indiP_flats.add(pcf::IndiElement(
it->first, pcf::IndiElement::On));
1166 m_flatCurrent =
it->first;
1170 m_indiP_flats.add(pcf::IndiElement(
it->first, pcf::IndiElement::Off));
1174 if (m_flatDefault !=
"")
1176 if (m_flatCurrent ==
"default")
1178 m_indiP_flats.add(pcf::IndiElement(
"default", pcf::IndiElement::On));
1182 m_indiP_flats.add(pcf::IndiElement(
"default", pcf::IndiElement::Off));
1186 if (derived().registerIndiPropertyNew(m_indiP_flats, st_newCallBack_flats) < 0)
1188 #ifndef DM_TEST_NOLOG
1189 derivedT::template log<software_error>({__FILE__, __LINE__});
1194 if (derived().m_indiDriver)
1196 derived().m_indiDriver->sendDefProperty(m_indiP_flats);
1203 template <
class derivedT,
typename realT>
1206 std::string target = intarget;
1208 std::string targetPath;
1210 if (target ==
"default")
1212 target = m_flatDefault;
1213 targetPath = m_flatPath +
"/" + m_flatDefault +
".fits";
1219 targetPath = m_flatCommands.at(target);
1223 derivedT::template log<text_log>(
"flat file " + target +
" not found",
logPrio::LOG_ERROR);
1228 m_flatLoaded =
false;
1230 mx::fits::fitsFile<realT> ff;
1231 if (ff.read(m_flatCommand, targetPath) < 0)
1233 derivedT::template log<text_log>(
"flat file " + targetPath +
" not found",
logPrio::LOG_ERROR);
1237 derivedT::template log<text_log>(
"loaded flat file " + targetPath);
1238 m_flatLoaded =
true;
1240 m_flatCurrent = intarget;
1242 if (m_indiP_flats.find(
"default"))
1244 if (m_flatCurrent ==
"default")
1246 m_indiP_flats[
"default"] = pcf::IndiElement::On;
1250 m_indiP_flats[
"default"] = pcf::IndiElement::Off;
1254 for (
auto i = m_flatCommands.begin(); i != m_flatCommands.end(); ++i)
1256 if (!m_indiP_flats.find(i->first))
1261 if (i->first == m_flatCurrent)
1263 m_indiP_flats[i->first] = pcf::IndiElement::On;
1267 m_indiP_flats[i->first] = pcf::IndiElement::Off;
1271 if (derived().m_indiDriver)
1273 derived().m_indiDriver->sendSetProperty(m_indiP_flats);
1284 template <
class derivedT,
typename realT>
1287 if (m_shmimFlat ==
"")
1290 if (ImageStreamIO_openIm(&m_flatImageStream, m_shmimFlat.c_str()) != 0)
1292 derivedT::template log<text_log>(
"could not connect to flat channel " + m_shmimFlat,
logPrio::LOG_WARNING);
1296 if (m_flatImageStream.md[0].size[0] != m_dmWidth)
1298 ImageStreamIO_closeIm(&m_flatImageStream);
1299 derivedT::template log<text_log>(
"width mismatch between " + m_shmimFlat +
" and configured DM",
logPrio::LOG_ERROR);
1303 if (m_flatImageStream.md[0].size[1] != m_dmHeight)
1305 ImageStreamIO_closeIm(&m_flatImageStream);
1306 derivedT::template log<text_log>(
"height mismatch between " + m_shmimFlat +
" and configured DM",
logPrio::LOG_ERROR);
1312 bool flatSet = m_flatSet;
1315 if (loadFlat(m_flatCurrent) < 0)
1317 derivedT::template log<text_log>(
"error loading flat " + m_flatCurrent,
logPrio::LOG_ERROR);
1319 m_flatSet = flatSet;
1324 ImageStreamIO_closeIm(&m_flatImageStream);
1329 if (m_flatCommand.rows() != m_dmWidth)
1331 ImageStreamIO_closeIm(&m_flatImageStream);
1332 derivedT::template log<text_log>(
"width mismatch between flat file and configured DM",
logPrio::LOG_ERROR);
1336 if (m_flatCommand.cols() != m_dmHeight)
1338 ImageStreamIO_closeIm(&m_flatImageStream);
1339 derivedT::template log<text_log>(
"height mismatch between flat file and configured DM",
logPrio::LOG_ERROR);
1343 m_flatImageStream.md->write = 1;
1347 memcpy(m_flatImageStream.array.raw, m_flatCommand.data(), m_dmWidth * m_dmHeight *
sizeof(realT));
1350 clock_gettime(CLOCK_REALTIME, &m_flatImageStream.md->writetime);
1353 m_flatImageStream.md->atime = m_flatImageStream.md->writetime;
1355 m_flatImageStream.md->cnt0++;
1356 m_flatImageStream.md->write = 0;
1357 ImageStreamIO_sempost(&m_flatImageStream, -1);
1362 ImageStreamIO_closeIm(&m_flatImageStream);
1366 derived().updateSwitchIfChanged(m_indiP_setFlat,
"toggle", pcf::IndiElement::On, pcf::IndiProperty::Busy);
1368 derivedT::template log<text_log>(
"flat set");
1374 template <
class derivedT,
typename realT>
1377 if (m_shmimFlat ==
"")
1380 if (ImageStreamIO_openIm(&m_flatImageStream, m_shmimFlat.c_str()) != 0)
1382 derivedT::template log<text_log>(
"could not connect to flat channel " + m_shmimFlat,
logPrio::LOG_WARNING);
1386 if (m_flatImageStream.md[0].size[0] != m_dmWidth)
1388 ImageStreamIO_closeIm(&m_flatImageStream);
1389 derivedT::template log<text_log>(
"width mismatch between " + m_shmimFlat +
" and configured DM",
logPrio::LOG_ERROR);
1393 if (m_flatImageStream.md[0].size[1] != m_dmHeight)
1395 ImageStreamIO_closeIm(&m_flatImageStream);
1396 derivedT::template log<text_log>(
"height mismatch between " + m_shmimFlat +
" and configured DM",
logPrio::LOG_ERROR);
1400 m_flatImageStream.md->write = 1;
1404 memset(m_flatImageStream.array.raw, 0, m_dmWidth * m_dmHeight *
sizeof(realT));
1407 clock_gettime(CLOCK_REALTIME, &m_flatImageStream.md->writetime);
1410 m_flatImageStream.md->atime = m_flatImageStream.md->writetime;
1412 m_flatImageStream.md->cnt0++;
1413 m_flatImageStream.md->write = 0;
1414 ImageStreamIO_sempost(&m_flatImageStream, -1);
1419 ImageStreamIO_closeIm(&m_flatImageStream);
1421 derived().updateSwitchIfChanged(m_indiP_setFlat,
"toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle);
1423 derivedT::template log<text_log>(
"flat zeroed");
1428 template <
class derivedT,
typename realT>
1431 std::vector<std::string> tfs = mx::ioutils::getFileNames(m_testPath,
"",
"",
".fits");
1433 for (
auto it = m_testCommands.begin();
it != m_testCommands.end(); ++
it)
1438 bool changed =
false;
1439 for (
size_t n = 0; n < tfs.size(); ++n)
1441 auto ir = m_testCommands.insert(std::pair<std::string, std::string>(mx::ioutils::pathStem(tfs[n]), tfs[n]));
1442 if (ir.second ==
true)
1445 ir.first->second = tfs[n];
1448 for (
auto it = m_testCommands.begin();
it != m_testCommands.end(); ++
it)
1450 if (
it->second ==
"")
1456 m_testCommands.erase(itdel);
1463 if (derived().m_indiDriver)
1465 derived().m_indiDriver->sendDelProperty(m_indiP_tests);
1466 derived().m_indiNewCallBacks.erase(m_indiP_tests.createUniqueKey());
1469 m_indiP_tests = pcf::IndiProperty(pcf::IndiProperty::Switch);
1470 m_indiP_tests.setDevice(derived().configName());
1471 m_indiP_tests.setName(
"test");
1472 m_indiP_tests.setPerm(pcf::IndiProperty::ReadWrite);
1473 m_indiP_tests.setState(pcf::IndiProperty::Idle);
1474 m_indiP_tests.setRule(pcf::IndiProperty::OneOfMany);
1477 for (
auto it = m_testCommands.begin();
it != m_testCommands.end(); ++
it)
1479 if (
it->first == m_testCurrent || m_testCurrent ==
"")
1481 m_indiP_tests.add(pcf::IndiElement(
it->first, pcf::IndiElement::On));
1482 m_testCurrent =
it->first;
1486 m_indiP_tests.add(pcf::IndiElement(
it->first, pcf::IndiElement::Off));
1490 if (m_testDefault !=
"")
1492 if (m_testCurrent ==
"default")
1494 m_indiP_tests.add(pcf::IndiElement(
"default", pcf::IndiElement::On));
1498 m_indiP_tests.add(pcf::IndiElement(
"default", pcf::IndiElement::Off));
1502 if (derived().registerIndiPropertyNew(m_indiP_tests, st_newCallBack_tests) < 0)
1504 #ifndef DM_TEST_NOLOG
1505 derivedT::template log<software_error>({__FILE__, __LINE__});
1510 if (derived().m_indiDriver)
1512 derived().m_indiDriver->sendDefProperty(m_indiP_tests);
1519 template <
class derivedT,
typename realT>
1522 std::string target = intarget;
1524 if (target ==
"default")
1526 target = m_testDefault;
1529 std::string targetPath;
1533 targetPath = m_testCommands.at(target);
1537 derivedT::template log<text_log>(
"test file " + target +
" not found",
logPrio::LOG_ERROR);
1541 m_testLoaded =
false;
1543 mx::fits::fitsFile<realT> ff;
1544 if (ff.read(m_testCommand, targetPath) < 0)
1546 derivedT::template log<text_log>(
"test file " + targetPath +
" not found",
logPrio::LOG_ERROR);
1550 derivedT::template log<text_log>(
"loaded test file " + targetPath);
1551 m_testLoaded =
true;
1553 m_testCurrent = intarget;
1555 if (m_indiP_tests.find(
"default"))
1557 if (m_testCurrent ==
"default")
1559 m_indiP_tests[
"default"] = pcf::IndiElement::On;
1563 m_indiP_tests[
"default"] = pcf::IndiElement::Off;
1567 for (
auto i = m_testCommands.begin(); i != m_testCommands.end(); ++i)
1569 if (!m_indiP_tests.find(i->first))
1574 if (i->first == m_testCurrent)
1576 m_indiP_tests[i->first] = pcf::IndiElement::On;
1580 m_indiP_tests[i->first] = pcf::IndiElement::Off;
1584 if (derived().m_indiDriver)
1585 derived().m_indiDriver->sendSetProperty(m_indiP_tests);
1593 template <
class derivedT,
typename realT>
1597 if (m_shmimTest ==
"")
1600 if (ImageStreamIO_openIm(&m_testImageStream, m_shmimTest.c_str()) != 0)
1602 derivedT::template log<text_log>(
"could not connect to test channel " + m_shmimTest,
logPrio::LOG_WARNING);
1606 if (m_testImageStream.md->size[0] != m_dmWidth)
1608 ImageStreamIO_closeIm(&m_testImageStream);
1609 derivedT::template log<text_log>(
"width mismatch between " + m_shmimTest +
" and configured DM",
logPrio::LOG_ERROR);
1613 if (m_testImageStream.md->size[1] != m_dmHeight)
1615 ImageStreamIO_closeIm(&m_testImageStream);
1616 derivedT::template log<text_log>(
"height mismatch between " + m_shmimTest +
" and configured DM",
logPrio::LOG_ERROR);
1622 bool testSet = m_testSet;
1625 if (loadTest(m_testCurrent) < 0)
1627 derivedT::template log<text_log>(
"error loading test " + m_testCurrent,
logPrio::LOG_ERROR);
1629 m_testSet = testSet;
1634 ImageStreamIO_closeIm(&m_testImageStream);
1639 if (m_testCommand.rows() != m_dmWidth)
1641 ImageStreamIO_closeIm(&m_testImageStream);
1642 derivedT::template log<text_log>(
"width mismatch between test file and configured DM",
logPrio::LOG_ERROR);
1646 if (m_testCommand.cols() != m_dmHeight)
1648 ImageStreamIO_closeIm(&m_testImageStream);
1649 derivedT::template log<text_log>(
"height mismatch between test file and configured DM",
logPrio::LOG_ERROR);
1653 m_testImageStream.md->write = 1;
1657 memcpy(m_testImageStream.array.raw, m_testCommand.data(), m_dmWidth * m_dmHeight *
sizeof(realT));
1660 clock_gettime(CLOCK_REALTIME, &m_testImageStream.md->writetime);
1663 m_testImageStream.md->atime = m_testImageStream.md->writetime;
1665 m_testImageStream.md->cnt0++;
1666 m_testImageStream.md->write = 0;
1667 ImageStreamIO_sempost(&m_testImageStream, -1);
1672 ImageStreamIO_closeIm(&m_testImageStream);
1674 derived().updateSwitchIfChanged(m_indiP_setTest,
"toggle", pcf::IndiElement::On, pcf::IndiProperty::Busy);
1676 derivedT::template log<text_log>(
"test set");
1681 template <
class derivedT,
typename realT>
1684 if (m_shmimTest ==
"")
1687 if (ImageStreamIO_openIm(&m_testImageStream, m_shmimTest.c_str()) != 0)
1689 derivedT::template log<text_log>(
"could not connect to test channel " + m_shmimTest,
logPrio::LOG_WARNING);
1693 if (m_testImageStream.md[0].size[0] != m_dmWidth)
1695 ImageStreamIO_closeIm(&m_testImageStream);
1696 derivedT::template log<text_log>(
"width mismatch between " + m_shmimTest +
" and configured DM",
logPrio::LOG_ERROR);
1700 if (m_testImageStream.md[0].size[1] != m_dmHeight)
1702 ImageStreamIO_closeIm(&m_testImageStream);
1703 derivedT::template log<text_log>(
"height mismatch between " + m_shmimTest +
" and configured DM",
logPrio::LOG_ERROR);
1707 m_testImageStream.md->write = 1;
1711 memset(m_testImageStream.array.raw, 0, m_dmWidth * m_dmHeight *
sizeof(realT));
1714 clock_gettime(CLOCK_REALTIME, &m_testImageStream.md->writetime);
1717 m_testImageStream.md->atime = m_testImageStream.md->writetime;
1719 m_testImageStream.md->cnt0++;
1720 m_testImageStream.md->write = 0;
1723 ImageStreamIO_sempost(&m_testImageStream, -1);
1727 ImageStreamIO_closeIm(&m_testImageStream);
1729 derived().updateSwitchIfChanged(m_indiP_setTest,
"toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle);
1731 derivedT::template log<text_log>(
"test zeroed");
1736 template <
class derivedT,
typename realT>
1739 if (derived().m_shmimName ==
"")
1746 for (
int n = 0; n < m_channels; ++n)
1749 snprintf(nstr,
sizeof(nstr),
"%02d", n);
1750 std::string shmimN = derived().m_shmimName + nstr;
1752 if (ImageStreamIO_openIm(&imageStream, shmimN.c_str()) != 0)
1754 derivedT::template log<text_log>(
"could not connect to channel " + shmimN,
logPrio::LOG_WARNING);
1758 if (imageStream.md->size[0] != m_dmWidth)
1760 ImageStreamIO_closeIm(&imageStream);
1761 derivedT::template log<text_log>(
"width mismatch between " + shmimN +
" and configured DM",
logPrio::LOG_ERROR);
1762 derived().updateSwitchIfChanged(m_indiP_zeroAll,
"request", pcf::IndiElement::Off,
INDI_IDLE);
1766 if (imageStream.md->size[1] != m_dmHeight)
1768 ImageStreamIO_closeIm(&imageStream);
1769 derivedT::template log<text_log>(
"height mismatch between " + shmimN +
" and configured DM",
logPrio::LOG_ERROR);
1770 derived().updateSwitchIfChanged(m_indiP_zeroAll,
"request", pcf::IndiElement::Off,
INDI_IDLE);
1774 imageStream.md->write = 1;
1775 memset(imageStream.array.raw, 0, m_dmWidth * m_dmHeight *
sizeof(realT));
1777 clock_gettime(CLOCK_REALTIME, &imageStream.md->writetime);
1780 imageStream.md->atime = imageStream.md->writetime;
1782 imageStream.md->cnt0++;
1783 imageStream.md->write = 0;
1786 if (n == m_channels - 1 && !nosem)
1787 ImageStreamIO_sempost(&imageStream, -1);
1789 ImageStreamIO_closeIm(&imageStream);
1794 derived().updateSwitchIfChanged(m_indiP_zeroAll,
"request", pcf::IndiElement::Off,
INDI_IDLE);
1798 derived().updateSwitchIfChanged(m_indiP_setFlat,
"toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle);
1802 derived().updateSwitchIfChanged(m_indiP_setTest,
"toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle);
1805 if ((rv = clearSat()) < 0)
1807 derivedT::template log<software_error>({__FILE__, __LINE__, errno, rv,
"Error from clearSat"});
1814 template <
class derivedT,
typename realT>
1817 if(m_shmimSat ==
"" || m_dmWidth == 0 || m_dmHeight == 0)
1824 std::vector<std::string> sats = {m_shmimSat, m_shmimSatPerc};
1826 for (
size_t n = 0; n < sats.size(); ++n)
1828 std::string shmimN = sats[n];
1830 if (ImageStreamIO_openIm(&imageStream, shmimN.c_str()) != 0)
1832 derivedT::template log<text_log>(
"could not connect to sat map " + shmimN,
logPrio::LOG_WARNING);
1836 if (imageStream.md->size[0] != m_dmWidth)
1838 ImageStreamIO_closeIm(&imageStream);
1839 derivedT::template log<text_log>(
"width mismatch between " + shmimN +
" and configured DM",
logPrio::LOG_ERROR);
1840 derived().updateSwitchIfChanged(m_indiP_zeroAll,
"request", pcf::IndiElement::Off,
INDI_IDLE);
1844 if (imageStream.md->size[1] != m_dmHeight)
1846 ImageStreamIO_closeIm(&imageStream);
1847 derivedT::template log<text_log>(
"height mismatch between " + shmimN +
" and configured DM",
logPrio::LOG_ERROR);
1848 derived().updateSwitchIfChanged(m_indiP_zeroAll,
"request", pcf::IndiElement::Off,
INDI_IDLE);
1852 imageStream.md->write = 1;
1853 memset(imageStream.array.raw, 0, m_dmWidth * m_dmHeight * ImageStreamIO_typesize(imageStream.md->datatype));
1855 clock_gettime(CLOCK_REALTIME, &imageStream.md->writetime);
1858 imageStream.md->atime = imageStream.md->writetime;
1860 imageStream.md->cnt0++;
1861 imageStream.md->write = 0;
1863 ImageStreamIO_closeIm(&imageStream);
1866 m_accumSatMap.setZero();
1867 m_instSatMap.setZero();
1872 template <
class derivedT,
typename realT>
1878 template <
class derivedT,
typename realT>
1882 m_satThreadID = syscall(SYS_gettid);
1885 while (m_satThreadInit ==
true && derived().shutdown() == 0)
1889 if (derived().shutdown())
1892 uint32_t imsize[3] = {0, 0, 0};
1895 while ((m_shmimSat ==
"" || m_accumSatMap.rows() == 0 || m_accumSatMap.cols() == 0) && !derived().shutdown())
1900 if (derived().shutdown())
1905 imsize[0] = m_dmWidth;
1906 imsize[1] = m_dmHeight;
1909 ImageStreamIO_createIm_gpu(&m_satImageStream, m_shmimSat.c_str(), 3, imsize,
IMAGESTRUCT_UINT8, -1, 1, IMAGE_NB_SEMAPHORE, 0, CIRCULAR_BUFFER | ZAXIS_TEMPORAL, 0);
1910 ImageStreamIO_createIm_gpu(&m_satPercImageStream, m_shmimSatPerc.c_str(), 3, imsize,
IMAGESTRUCT_FLOAT, -1, 1, IMAGE_NB_SEMAPHORE, 0, CIRCULAR_BUFFER | ZAXIS_TEMPORAL, 0);
1914 m_satImageStream.md->cnt1 = 0;
1915 m_satPercImageStream.md->cnt1 = 0;
1918 mx::improc::eigenImage<uint8_t> satmap(m_dmWidth, m_dmHeight);
1921 double t_accumst = mx::sys::get_curr_time();
1924 while (!derived().shutdown())
1928 if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
1930 derivedT::template log<software_critical>({__FILE__, __LINE__, errno, 0,
"clock_gettime"});
1936 if (sem_timedwait(&m_satSemaphore, &ts) == 0)
1939 for (
int rr = 0; rr < m_instSatMap.rows(); ++rr)
1941 for (
int cc = 0; cc < m_instSatMap.cols(); ++cc)
1943 m_accumSatMap(rr, cc) += m_instSatMap(rr, cc);
1949 if (mx::sys::get_curr_time(ts) - t_accumst < m_satAvgInt / 1000.0)
1956 for (
int rr = 0; rr < m_instSatMap.rows(); ++rr)
1958 for (
int cc = 0; cc < m_instSatMap.cols(); ++cc)
1960 m_satPercMap(rr, cc) = m_accumSatMap(rr, cc) / naccum;
1961 if (m_satPercMap(rr, cc) >= m_percThreshold)
1965 satmap(rr, cc) = (m_accumSatMap(rr, cc) > 0);
1971 if (m_overSatAct / (m_satPercMap.rows() * m_satPercMap.cols() * 0.75) > m_intervalSatThreshold)
1973 ++m_intervalSatExceeds;
1977 m_intervalSatExceeds = 0;
1981 if (m_intervalSatExceeds >= m_intervalSatCountThreshold)
1983 m_intervalSatTrip =
true;
1986 m_satImageStream.md->write = 1;
1987 m_satPercImageStream.md->write = 1;
1989 memcpy(m_satImageStream.array.raw, satmap.data(), m_dmWidth * m_dmHeight *
sizeof(uint8_t));
1990 memcpy(m_satPercImageStream.array.raw, m_satPercMap.data(), m_dmWidth * m_dmHeight *
sizeof(
float));
1993 clock_gettime(CLOCK_REALTIME, &m_satImageStream.md->writetime);
1994 m_satPercImageStream.md->writetime = m_satImageStream.md->writetime;
1997 m_satImageStream.md->atime = m_satImageStream.md->writetime;
1998 m_satPercImageStream.md->atime = m_satPercImageStream.md->writetime;
2001 m_satImageStream.md->cnt1 = 0;
2002 m_satPercImageStream.md->cnt1 = 0;
2005 m_satImageStream.md->cnt0++;
2006 m_satPercImageStream.md->cnt0++;
2008 m_satImageStream.writetimearray[0] = m_satImageStream.md->writetime;
2009 m_satImageStream.atimearray[0] = m_satImageStream.md->atime;
2010 m_satImageStream.cntarray[0] = m_satImageStream.md->cnt0;
2012 m_satPercImageStream.writetimearray[0] = m_satPercImageStream.md->writetime;
2013 m_satPercImageStream.atimearray[0] = m_satPercImageStream.md->atime;
2014 m_satPercImageStream.cntarray[0] = m_satPercImageStream.md->cnt0;
2017 m_satImageStream.md->write = 0;
2018 ImageStreamIO_sempost(&m_satImageStream, -1);
2020 m_satPercImageStream.md->write = 0;
2021 ImageStreamIO_sempost(&m_satPercImageStream, -1);
2023 m_accumSatMap.setZero();
2025 t_accumst = mx::sys::get_curr_time(ts);
2037 if (errno != ETIMEDOUT)
2039 derivedT::template log<software_error>({__FILE__, __LINE__, errno,
"sem_timedwait"});
2047 ImageStreamIO_destroyIm(&m_satImageStream);
2049 ImageStreamIO_destroyIm(&m_satPercImageStream);
2053 template <
class derivedT,
typename realT>
2056 if (!derived().m_indiDriver)
2062 template <
class derivedT,
typename realT>
2064 const pcf::IndiProperty &
ipRecv)
2066 return static_cast<derivedT *
>(app)->newCallBack_init(
ipRecv);
2069 template <
class derivedT,
typename realT>
2072 if (
ipRecv.createUniqueKey() != m_indiP_init.createUniqueKey())
2074 return derivedT::template
log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2077 if (!
ipRecv.find(
"request"))
2080 if (
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
2082 return derived().initDM();
2087 template <
class derivedT,
typename realT>
2089 const pcf::IndiProperty &
ipRecv)
2091 return static_cast<derivedT *
>(app)->newCallBack_zero(
ipRecv);
2094 template <
class derivedT,
typename realT>
2097 if (
ipRecv.createUniqueKey() != m_indiP_zero.createUniqueKey())
2099 return derivedT::template
log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2102 if (!
ipRecv.find(
"request"))
2105 if (
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
2107 return derived().zeroDM();
2112 template <
class derivedT,
typename realT>
2114 const pcf::IndiProperty &
ipRecv)
2116 return static_cast<derivedT *
>(app)->newCallBack_release(
ipRecv);
2119 template <
class derivedT,
typename realT>
2122 if (
ipRecv.createUniqueKey() != m_indiP_release.createUniqueKey())
2124 return derivedT::template
log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2127 if (!
ipRecv.find(
"request"))
2130 if (
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
2137 template <
class derivedT,
typename realT>
2139 const pcf::IndiProperty &
ipRecv)
2141 return static_cast<derivedT *
>(app)->newCallBack_flats(
ipRecv);
2144 template <
class derivedT,
typename realT>
2147 if (
ipRecv.createUniqueKey() != m_indiP_flats.createUniqueKey())
2149 derivedT::template log<software_error>({__FILE__, __LINE__,
"invalid indi property received"});
2153 std::string newFlat;
2155 if (
ipRecv.find(
"default"))
2157 if (
ipRecv[
"default"].getSwitchState() == pcf::IndiElement::On)
2159 newFlat =
"default";
2164 for (
auto i = m_flatCommands.begin(); i != m_flatCommands.end(); ++i)
2166 if (!
ipRecv.find(i->first))
2169 if (
ipRecv[i->first].getSwitchState() == pcf::IndiElement::On)
2184 return loadFlat(newFlat);
2187 template <
class derivedT,
typename realT>
2189 const pcf::IndiProperty &
ipRecv)
2191 return static_cast<derivedT *
>(app)->newCallBack_setFlat(
ipRecv);
2194 template <
class derivedT,
typename realT>
2197 if (
ipRecv.createUniqueKey() != m_indiP_setFlat.createUniqueKey())
2199 return derivedT::template
log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2202 if (!
ipRecv.find(
"toggle"))
2205 if (
ipRecv[
"toggle"] == pcf::IndiElement::On)
2215 template <
class derivedT,
typename realT>
2217 const pcf::IndiProperty &
ipRecv)
2219 return static_cast<derivedT *
>(app)->newCallBack_tests(
ipRecv);
2222 template <
class derivedT,
typename realT>
2225 if (
ipRecv.createUniqueKey() != m_indiP_tests.createUniqueKey())
2227 derivedT::template log<software_error>({__FILE__, __LINE__,
"invalid indi property received"});
2231 std::string newTest;
2233 if (
ipRecv.find(
"default"))
2235 if (
ipRecv[
"default"].getSwitchState() == pcf::IndiElement::On)
2237 newTest =
"default";
2242 for (
auto i = m_testCommands.begin(); i != m_testCommands.end(); ++i)
2244 if (!
ipRecv.find(i->first))
2247 if (
ipRecv[i->first].getSwitchState() == pcf::IndiElement::On)
2262 return loadTest(newTest);
2265 template <
class derivedT,
typename realT>
2267 const pcf::IndiProperty &
ipRecv)
2269 return static_cast<derivedT *
>(app)->newCallBack_setTest(
ipRecv);
2272 template <
class derivedT,
typename realT>
2275 if (
ipRecv.createUniqueKey() != m_indiP_setTest.createUniqueKey())
2277 return derivedT::template
log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2280 if (!
ipRecv.find(
"toggle"))
2283 if (
ipRecv[
"toggle"] == pcf::IndiElement::On)
2293 template <
class derivedT,
typename realT>
2295 const pcf::IndiProperty &
ipRecv)
2297 return static_cast<derivedT *
>(app)->newCallBack_zeroAll(
ipRecv);
2300 template <
class derivedT,
typename realT>
2303 if (
ipRecv.createUniqueKey() != m_indiP_zeroAll.createUniqueKey())
2305 return derivedT::template
log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2308 if (!
ipRecv.find(
"request"))
2311 if (
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
2315 std::lock_guard<std::mutex> guard(derived().m_indiMutex);
2325 #define DM_SETUP_CONFIG(cfig) \
2326 if (dmT::setupConfig(cfig) < 0) \
2328 log<software_error>({__FILE__, __LINE__, "Error from dmT::setupConfig"}); \
2329 m_shutdown = true; \
2337 #define DM_LOAD_CONFIG(cfig) \
2338 if (dmT::loadConfig(cfig) < 0) \
2340 return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::loadConfig"}); \
2344 #define DM_APP_STARTUP \
2345 if (dmT::appStartup() < 0) \
2347 return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::appStartup"}); \
2351 #define DM_APP_LOGIC \
2352 if (dmT::appLogic() < 0) \
2354 return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::appLogic"}); \
2358 #define DM_UPDATE_INDI \
2359 if (dmT::updateINDI() < 0) \
2361 return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::updateINDI"}); \
2365 #define DM_APP_SHUTDOWN \
2366 if (dmT::appShutdown() < 0) \
2368 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
constexpr static logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
constexpr static logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.