18#include <mx/improc/eigenImage.hpp>
19#include <mx/ioutils/fits/fitsFile.hpp>
21#include <boost/filesystem/operations.hpp>
25#include "../../ImageStreamIO/ImageStruct.hpp"
34template <
typename typeT>
43 return _DATATYPE_FLOAT;
49 return _DATATYPE_DOUBLE;
71template <
class derivedT,
typename realT>
363 pcf::IndiProperty ipFreq(pcf::IndiProperty::Switch);
367 ipFreq.add(pcf::IndiElement(
"toggle"));
368 ipFreq[
"toggle"] = pcf::IndiElement::Off;
369 derived().sendNewProperty(ipFreq);
371 derivedT::template log<text_log>(
"DM saturation threshold exceeded. Loop opened.", logPrio::LOG_WARNING);
413 const pcf::IndiProperty &
ipRecv
429 const pcf::IndiProperty &
ipRecv
445 const pcf::IndiProperty &
ipRecv
461 const pcf::IndiProperty &
ipRecv
477 const pcf::IndiProperty &
ipRecv
493 const pcf::IndiProperty &
ipRecv
509 const pcf::IndiProperty &
ipRecv
525 const pcf::IndiProperty &
ipRecv
549 typedef int32_t cbIndexT;
551 double m_t0 {0}, m_tf {0}, m_tsat0 {0}, m_tsatf {0};
552 double m_tact0 {0}, m_tact1 {0}, m_tact2 {0}, m_tact3 {0}, m_tact4 {0};
554 mx::sigproc::circularBufferIndex<double, cbIndexT> m_piTimes;
556 mx::sigproc::circularBufferIndex<double, cbIndexT> m_satSem;
558 mx::sigproc::circularBufferIndex<double, cbIndexT> m_actProc;
560 mx::sigproc::circularBufferIndex<double, cbIndexT> m_actCom;
562 mx::sigproc::circularBufferIndex<double, cbIndexT> m_satUp;
564 std::vector<double> m_piTimesD;
565 std::vector<double> m_satSemD;
566 std::vector<double> m_actProcD;
567 std::vector<double> m_actComD;
568 std::vector<double> m_satUpD;
575 return *
static_cast<derivedT *
>(
this);
579template <
class derivedT,
typename realT>
582 config.add(
"dm.calibPath",
"",
"dm.calibPath", argType::Required,
"dm",
"calibPath",
false,
"string",
"The path to calibration files, relative to the MagAO-X calibration path.");
584 config.add(
"dm.flatPath",
"",
"dm.flatPath", argType::Required,
"dm",
"flatPath",
false,
"string",
"The path to flat files. Default is the calibration path.");
585 config.add(
"dm.flatDefault",
"",
"dm.flatDefault", argType::Required,
"dm",
"flatDefault",
false,
"string",
"The default flat file (path and extension are not required).");
587 config.add(
"dm.testPath",
"",
"dm.testPath", argType::Required,
"dm",
"testPath",
false,
"string",
"The path to test files. Default is the calibration path plus /tests.");
588 config.add(
"dm.testDefault",
"",
"dm.testDefault", argType::Required,
"dm",
"testDefault",
false,
"string",
"The default test file (path and extension are not required).");
593 config.add(
"dm.threadPrio",
"",
"dm.threadPrio", argType::Required,
"dm",
"threadPrio",
false,
"int",
"The real-time priority of the dm control thread.");
594 config.add(
"dm.cpuset",
"",
"dm.cpuset", argType::Required,
"dm",
"cpuset",
false,
"int",
"The cpuset for the dm control thread.");
596 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.");
598 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). ");
600 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). ");
602 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.");
604 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.");
606 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.");
608 config.add(
"dm.width",
"",
"dm.width", argType::Required,
"dm",
"width",
false,
"string",
"The width of the DM in actuators.");
609 config.add(
"dm.height",
"",
"dm.height", argType::Required,
"dm",
"height",
false,
"string",
"The height of the DM in actuators.");
611 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.");
612 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.");
613 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.");
615 config.add(
"dm.satTriggerDevice",
"",
"dm.satTriggerDevice", argType::Required,
"dm",
"satTriggerDevice",
false,
"vector<string>",
"Device(s) with a toggle switch to toggle on saturation trigger.");
616 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.");
621template <
class derivedT,
typename realT>
625 m_calibPath = derived().m_calibDir +
"/" + m_calibRelDir;
626 config(m_calibPath,
"dm.calibPath");
629 m_flatPath = m_calibPath +
"/flats";
630 config(m_flatPath,
"dm.flatPath");
632 config(m_flatDefault,
"dm.flatDefault");
633 if (m_flatDefault !=
"")
635 m_flatDefault = mx::ioutils::pathStem(m_flatDefault);
636 m_flatCurrent =
"default";
640 m_testPath = m_calibPath +
"/tests";
641 config(m_testPath,
"dm.testPath");
643 config(m_testDefault,
"dm.testDefault");
644 if (m_testDefault !=
"")
646 m_testDefault = mx::ioutils::pathStem(m_testDefault);
647 m_testCurrent =
"default";
652 config(derived().m_smThreadPrio,
"dm.threadPrio");
653 config(derived().m_smCpuset,
"dm.cpuset");
655 config(derived().m_shmimName,
"dm.shmimName");
657 if (derived().m_shmimName !=
"")
659 m_shmimFlat = derived().m_shmimName +
"00";
660 config(m_shmimFlat,
"dm.shmimFlat");
662 m_shmimTest = derived().m_shmimName +
"02";
663 config(m_shmimTest,
"dm.shmimTest");
665 m_shmimSat = derived().m_shmimName +
"ST";
666 config(m_shmimSat,
"dm.shmimSat");
668 m_shmimSatPerc = derived().m_shmimName +
"SP";
669 config(m_shmimSatPerc,
"dm.shmimSatPerc");
671 config(m_satAvgInt,
"dm.satAvgInt");
675 config.isSet(
"dm.shmimFlat");
676 config.isSet(
"dm.shmimTest");
677 config.isSet(
"dm.shmimSat");
678 config.isSet(
"dm.shmimSatPerc");
679 config.isSet(
"dm.satAvgInt");
682 config(m_dmWidth,
"dm.width");
683 config(m_dmHeight,
"dm.height");
685 config(m_percThreshold,
"dm.percThreshold");
686 config(m_intervalSatThreshold,
"dm.intervalSatThreshold");
687 config(m_intervalSatCountThreshold,
"dm.intervalSatCountThreshold");
688 config(m_satTriggerDevice,
"dm.satTriggerDevice");
689 config(m_satTriggerProperty,
"dm.satTriggerProperty");
694template <
class derivedT,
typename realT>
697 if (m_dmDataType == 0)
699 derivedT::template log<software_error>({__FILE__, __LINE__,
"unsupported DM data type"});
708 m_indiP_flatShmim = pcf::IndiProperty(pcf::IndiProperty::Text);
709 m_indiP_flatShmim.setDevice(derived().configName());
710 m_indiP_flatShmim.setName(
"flat_shmim");
711 m_indiP_flatShmim.setPerm(pcf::IndiProperty::ReadOnly);
712 m_indiP_flatShmim.setState(pcf::IndiProperty::Idle);
713 m_indiP_flatShmim.add(pcf::IndiElement(
"channel"));
714 m_indiP_flatShmim[
"channel"] = m_shmimFlat;
716 if (derived().registerIndiPropertyReadOnly(m_indiP_flatShmim) < 0)
719 derivedT::template log<software_error>({__FILE__, __LINE__});
725 derived().createStandardIndiToggleSw(m_indiP_setFlat,
"flat_set");
726 if (derived().registerIndiPropertyNew(m_indiP_setFlat, st_newCallBack_setFlat) < 0)
729 derivedT::template log<software_error>({__FILE__, __LINE__});
739 m_indiP_testShmim = pcf::IndiProperty(pcf::IndiProperty::Text);
740 m_indiP_testShmim.setDevice(derived().configName());
741 m_indiP_testShmim.setName(
"test_shmim");
742 m_indiP_testShmim.setPerm(pcf::IndiProperty::ReadOnly);
743 m_indiP_testShmim.setState(pcf::IndiProperty::Idle);
744 m_indiP_testShmim.add(pcf::IndiElement(
"channel"));
745 m_indiP_testShmim[
"channel"] = m_shmimTest;
746 derived().createStandardIndiToggleSw(m_indiP_setTest,
"test_shmim");
747 if (derived().registerIndiPropertyReadOnly(m_indiP_testShmim) < 0)
750 derivedT::template log<software_error>({__FILE__, __LINE__});
756 derived().createStandardIndiToggleSw(m_indiP_setTest,
"test_set");
757 if (derived().registerIndiPropertyNew(m_indiP_setTest, st_newCallBack_setTest) < 0)
760 derivedT::template log<software_error>({__FILE__, __LINE__});
766 derived().createStandardIndiRequestSw(m_indiP_init,
"initDM");
767 if (derived().registerIndiPropertyNew(m_indiP_init, st_newCallBack_init) < 0)
770 derivedT::template log<software_error>({__FILE__, __LINE__});
776 derived().createStandardIndiRequestSw(m_indiP_zero,
"zeroDM");
777 if (derived().registerIndiPropertyNew(m_indiP_zero, st_newCallBack_zero) < 0)
780 derivedT::template log<software_error>({__FILE__, __LINE__});
786 derived().createStandardIndiRequestSw(m_indiP_release,
"releaseDM");
787 if (derived().registerIndiPropertyNew(m_indiP_release, st_newCallBack_release) < 0)
790 derivedT::template log<software_error>({__FILE__, __LINE__});
795 derived().createStandardIndiRequestSw(m_indiP_zeroAll,
"zeroAll");
796 if (derived().registerIndiPropertyNew(m_indiP_zeroAll, st_newCallBack_zeroAll) < 0)
799 derivedT::template log<software_error>({__FILE__, __LINE__});
804 if (m_flatDefault !=
"")
809 if (m_testDefault !=
"")
814 if (sem_init(&m_satSemaphore, 0, 0) < 0)
816 return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno, 0,
"Initializing sat semaphore"});
819 if (derived().threadStart(m_satThread, m_satThreadInit, m_satThreadID, m_satThreadProp, m_satThreadPrio,
"",
"saturation",
this, satThreadStart) < 0)
821 derivedT::template log<software_error, -1>({__FILE__, __LINE__});
828template <
class derivedT,
typename realT>
832 if (pthread_tryjoin_np(m_satThread.native_handle(), 0) == 0)
834 derivedT::template log<software_error>({__FILE__, __LINE__,
"saturation thread has exited"});
843 if (m_intervalSatTrip)
846 m_intervalSatTrip =
false;
850 static uint64_t lastMono = 0;
852 if(m_piTimes.size() >= m_piTimes.maxEntries() && m_piTimes.maxEntries() > 0 && m_piTimes.mono() != lastMono)
854 cbIndexT refEntry = m_piTimes.earliest();
856 m_piTimesD.resize(m_piTimes.maxEntries());
857 m_satSemD.resize(m_satSem.maxEntries());
858 m_actProcD.resize(m_actProc.maxEntries());
859 m_actComD.resize(m_actCom.maxEntries());
860 m_satUpD.resize(m_satUp.maxEntries());
862 for(
size_t n=0; n < m_piTimesD.size(); ++n)
864 m_piTimesD[n] = m_piTimes.at(refEntry,n);
865 m_satSemD[n] = m_satSem.at(refEntry,n);
866 m_actProcD[n] = m_actProc.at(refEntry,n);
867 m_actComD[n] = m_actCom.at(refEntry,n);
868 m_satUpD[n] = m_satUp.at(refEntry,n);
871 std::cerr <<
"Act. Process: " << mx::math::vectorMean(m_actProcD) <<
" +/- " << sqrt(mx::math::vectorVariance(m_actProcD)) <<
"\n";
872 std::cerr <<
"Act. Command: " << mx::math::vectorMean(m_actComD) <<
" +/- " << sqrt(mx::math::vectorVariance(m_actComD)) <<
"\n";
873 std::cerr <<
"Sat. Update: " << mx::math::vectorMean(m_satUpD) <<
" +/- " << sqrt(mx::math::vectorVariance(m_satUpD)) <<
"\n";
874 std::cerr <<
"Tot. CommandDM: " << mx::math::vectorMean(m_piTimesD) <<
" +/- " << sqrt(mx::math::vectorVariance(m_piTimesD)) <<
"\n";
875 std::cerr <<
"Sat. Semaphore: " << mx::math::vectorMean(m_satSemD) <<
" +/- " << sqrt(mx::math::vectorVariance(m_satSemD)) <<
"\n";
878 lastMono = m_piTimes.mono();
885template <
class derivedT,
typename realT>
888 if (m_satThread.joinable())
890 pthread_kill(m_satThread.native_handle(), SIGUSR1);
903template <
class derivedT,
typename realT>
911template <
class derivedT,
typename realT>
920template <
class derivedT,
typename realT>
923 std::vector<std::string> dmlist;
924 mx::error_t errc = mx::ioutils::getFileNames(dmlist,
"/milk/shm/", derived().m_shmimName,
".im",
".shm");
926 mx_error_check_rv(errc, -1);
928 if (dmlist.size() == 0)
930 derivedT::template log<software_error>({__FILE__, __LINE__,
"no dm channels found for " + derived().m_shmimName});
935 for (
size_t n = 0; n < dmlist.size(); ++n)
938 snprintf(nstr,
sizeof(nstr),
"%02d.im.shm", (
int)n);
939 std::string tgt = derived().m_shmimName;
942 for (
size_t m = 0; m < dmlist.size(); ++m)
944 if (dmlist[m].find(tgt) != std::string::npos)
946 if ((
int)n > m_channels)
954 derivedT::template log<text_log>({std::string(
"Found ") + std::to_string(m_channels) +
" channels for " + derived().m_shmimName});
959template <
class derivedT,
typename realT>
962 static_cast<void>(sp);
966 if (derived().m_width != m_dmWidth)
968 derivedT::template log<software_critical>({__FILE__, __LINE__,
"shmim width does not match configured DM width"});
972 if (derived().m_height != m_dmHeight)
974 derivedT::template log<software_critical>({__FILE__, __LINE__,
"shmim height does not match configured DM height"});
978 if (derived().m_dataType != m_dmDataType)
980 derivedT::template log<software_critical>({__FILE__, __LINE__,
"shmim data type does not match configured DM data type"});
987 m_instSatMap.resize(m_dmWidth, m_dmHeight);
988 m_instSatMap.setZero();
990 m_accumSatMap.resize(m_dmWidth, m_dmHeight);
991 m_accumSatMap.setZero();
993 m_satPercMap.resize(m_dmWidth, m_dmHeight);
994 m_satPercMap.setZero();
996 if (findDMChannels() < 0)
998 derivedT::template log<software_critical>({__FILE__, __LINE__,
"error finding DM channels"});
1003 #ifdef XWC_DMTIMINGS
1004 m_piTimes.maxEntries(2000);
1005 m_satSem.maxEntries(2000);
1006 m_actProc.maxEntries(2000);
1007 m_actCom.maxEntries(2000);
1008 m_satUp.maxEntries(2000);
1014template <
class derivedT,
typename realT>
1018 static_cast<void>(sp);
1020 #ifdef XWC_DMTIMINGS
1021 m_t0 = mx::sys::get_curr_time();
1024 int rv = derived().commandDM(curr_src);
1026 #ifdef XWC_DMTIMINGS
1027 m_tf = mx::sys::get_curr_time();
1032 derivedT::template log<software_critical>({__FILE__, __LINE__, errno, rv,
"Error from commandDM"});
1036 #ifdef XWC_DMTIMINGS
1037 m_tsat0 = mx::sys::get_curr_time();
1041 if (sem_post(&m_satSemaphore) < 0)
1043 derivedT::template log<software_critical>({__FILE__, __LINE__, errno, 0,
"Error posting to semaphore"});
1047 #ifdef XWC_DMTIMINGS
1048 m_tsatf = mx::sys::get_curr_time();
1051 #ifdef XWC_DMTIMINGS
1053 if(m_piTimes.maxEntries() > 0)
1055 m_piTimes.nextEntry(m_tf-m_t0);
1056 m_satSem.nextEntry(m_tsatf - m_tsat0);
1057 m_actProc.nextEntry(m_tact1 - m_tact0);
1058 m_actCom.nextEntry(m_tact2 - m_tact1);
1059 m_satUp.nextEntry(m_tact4 - m_tact3);
1065template <
class derivedT,
typename realT>
1069 if ((rv = derived().releaseDM()) < 0)
1071 derivedT::template log<software_critical>({__FILE__, __LINE__, errno, rv,
"Error from releaseDM"});
1075 if ((rv = zeroAll(
true)) < 0)
1077 derivedT::template log<software_error>({__FILE__, __LINE__, errno, rv,
"Error from zeroAll"});
1084template <
class derivedT,
typename realT>
1087 std::vector<std::string> tfs;
1088 mx::error_t errc = mx::ioutils::getFileNames(tfs, m_flatPath,
"",
"",
".fits");
1090 mx_error_check_rv(errc, -1);
1093 for (
size_t n = 0; n < tfs.size(); ++n)
1095 if (mx::ioutils::pathStem(tfs[n]) ==
"default")
1097 tfs.erase(tfs.begin() + n);
1102 unsigned m_nFlatFiles = 5;
1105 if (tfs.size() >= m_nFlatFiles)
1107 std::vector<std::time_t> wtimes(tfs.size());
1109 for (
size_t n = 0; n < wtimes.size(); ++n)
1111 wtimes[n] = boost::filesystem::last_write_time(tfs[n]);
1114 std::sort(wtimes.begin(), wtimes.end());
1116 std::time_t tn = wtimes[wtimes.size() - m_nFlatFiles];
1118 for (
size_t n = 0; n < tfs.size(); ++n)
1120 std::time_t lmt = boost::filesystem::last_write_time(tfs[n]);
1123 tfs.erase(tfs.begin() + n);
1129 for (
auto it = m_flatCommands.begin();
it != m_flatCommands.end(); ++
it)
1134 bool changed =
false;
1135 for (
size_t n = 0; n < tfs.size(); ++n)
1137 auto ir = m_flatCommands.insert(std::pair<std::string, std::string>(mx::ioutils::pathStem(tfs[n]), tfs[n]));
1138 if (ir.second ==
true)
1141 ir.first->second = tfs[n];
1144 for (
auto it = m_flatCommands.begin();
it != m_flatCommands.end(); ++
it)
1146 if (
it->second ==
"")
1152 m_flatCommands.erase(itdel);
1159 if (derived().m_indiDriver)
1161 derived().m_indiDriver->sendDelProperty(m_indiP_flats);
1162 derived().m_indiNewCallBacks.erase(m_indiP_flats.createUniqueKey());
1165 m_indiP_flats = pcf::IndiProperty(pcf::IndiProperty::Switch);
1166 m_indiP_flats.setDevice(derived().configName());
1167 m_indiP_flats.setName(
"flat");
1168 m_indiP_flats.setPerm(pcf::IndiProperty::ReadWrite);
1169 m_indiP_flats.setState(pcf::IndiProperty::Idle);
1170 m_indiP_flats.setRule(pcf::IndiProperty::OneOfMany);
1173 for (
auto it = m_flatCommands.begin();
it != m_flatCommands.end(); ++
it)
1175 if (
it->first == m_flatCurrent || m_flatCurrent ==
"")
1177 m_indiP_flats.add(pcf::IndiElement(
it->first, pcf::IndiElement::On));
1178 m_flatCurrent =
it->first;
1182 m_indiP_flats.add(pcf::IndiElement(
it->first, pcf::IndiElement::Off));
1186 if (m_flatDefault !=
"")
1188 if (m_flatCurrent ==
"default")
1190 m_indiP_flats.add(pcf::IndiElement(
"default", pcf::IndiElement::On));
1194 m_indiP_flats.add(pcf::IndiElement(
"default", pcf::IndiElement::Off));
1198 if (derived().registerIndiPropertyNew(m_indiP_flats, st_newCallBack_flats) < 0)
1200#ifndef DM_TEST_NOLOG
1201 derivedT::template log<software_error>({__FILE__, __LINE__});
1206 if (derived().m_indiDriver)
1208 derived().m_indiDriver->sendDefProperty(m_indiP_flats);
1215template <
class derivedT,
typename realT>
1218 std::string target = intarget;
1220 std::string targetPath;
1222 if (target ==
"default")
1224 target = m_flatDefault;
1225 targetPath = m_flatPath +
"/" + m_flatDefault +
".fits";
1231 targetPath = m_flatCommands.at(target);
1235 derivedT::template log<text_log>(
"flat file " + target +
" not found", logPrio::LOG_ERROR);
1240 m_flatLoaded =
false;
1242 mx::fits::fitsFile<realT> ff;
1243 mx::error_t errc = ff.read(m_flatCommand, targetPath);
1244 if (errc != mx::error_t::noerror)
1246 derivedT::template log<text_log>(std::format(
"error reading flat file {}: "
1247 "{} ({})", targetPath, mx::errorMessage(errc),
1248 mx::errorName(errc)), logPrio::LOG_ERROR);
1252 derivedT::template log<text_log>(
"loaded flat file " + targetPath);
1253 m_flatLoaded =
true;
1255 m_flatCurrent = intarget;
1257 if (m_indiP_flats.find(
"default"))
1259 if (m_flatCurrent ==
"default")
1261 m_indiP_flats[
"default"] = pcf::IndiElement::On;
1265 m_indiP_flats[
"default"] = pcf::IndiElement::Off;
1269 for (
auto i = m_flatCommands.begin(); i != m_flatCommands.end(); ++i)
1271 if (!m_indiP_flats.find(i->first))
1276 if (i->first == m_flatCurrent)
1278 m_indiP_flats[i->first] = pcf::IndiElement::On;
1282 m_indiP_flats[i->first] = pcf::IndiElement::Off;
1286 if (derived().m_indiDriver)
1288 derived().m_indiDriver->sendSetProperty(m_indiP_flats);
1299template <
class derivedT,
typename realT>
1302 if (m_shmimFlat ==
"")
1305 if (ImageStreamIO_openIm(&m_flatImageStream, m_shmimFlat.c_str()) != 0)
1307 derivedT::template log<text_log>(
"could not connect to flat channel " + m_shmimFlat, logPrio::LOG_WARNING);
1311 if (m_flatImageStream.md[0].size[0] != m_dmWidth)
1313 ImageStreamIO_closeIm(&m_flatImageStream);
1314 derivedT::template log<text_log>(
"width mismatch between " + m_shmimFlat +
" and configured DM", logPrio::LOG_ERROR);
1318 if (m_flatImageStream.md[0].size[1] != m_dmHeight)
1320 ImageStreamIO_closeIm(&m_flatImageStream);
1321 derivedT::template log<text_log>(
"height mismatch between " + m_shmimFlat +
" and configured DM", logPrio::LOG_ERROR);
1327 bool flatSet = m_flatSet;
1330 if (loadFlat(m_flatCurrent) < 0)
1332 derivedT::template log<text_log>(
"error loading flat " + m_flatCurrent, logPrio::LOG_ERROR);
1334 m_flatSet = flatSet;
1339 ImageStreamIO_closeIm(&m_flatImageStream);
1340 derivedT::template log<text_log>(
"no flat loaded", logPrio::LOG_ERROR);
1344 if (m_flatCommand.rows() != m_dmWidth)
1346 ImageStreamIO_closeIm(&m_flatImageStream);
1347 derivedT::template log<text_log>(
"width mismatch between flat file and configured DM", logPrio::LOG_ERROR);
1351 if (m_flatCommand.cols() != m_dmHeight)
1353 ImageStreamIO_closeIm(&m_flatImageStream);
1354 derivedT::template log<text_log>(
"height mismatch between flat file and configured DM", logPrio::LOG_ERROR);
1358 m_flatImageStream.md->write = 1;
1362 memcpy(m_flatImageStream.array.raw, m_flatCommand.data(), m_dmWidth * m_dmHeight *
sizeof(realT));
1365 clock_gettime(CLOCK_REALTIME, &m_flatImageStream.md->writetime);
1368 m_flatImageStream.md->atime = m_flatImageStream.md->writetime;
1370 m_flatImageStream.md->cnt0++;
1371 m_flatImageStream.md->write = 0;
1372 ImageStreamIO_sempost(&m_flatImageStream, -1);
1377 ImageStreamIO_closeIm(&m_flatImageStream);
1381 derived().updateSwitchIfChanged(m_indiP_setFlat,
"toggle", pcf::IndiElement::On, pcf::IndiProperty::Busy);
1383 derivedT::template log<text_log>(
"flat set");
1389template <
class derivedT,
typename realT>
1392 if (m_shmimFlat ==
"")
1395 if (ImageStreamIO_openIm(&m_flatImageStream, m_shmimFlat.c_str()) != 0)
1397 derivedT::template log<text_log>(
"could not connect to flat channel " + m_shmimFlat, logPrio::LOG_WARNING);
1401 if (m_flatImageStream.md[0].size[0] != m_dmWidth)
1403 ImageStreamIO_closeIm(&m_flatImageStream);
1404 derivedT::template log<text_log>(
"width mismatch between " + m_shmimFlat +
" and configured DM", logPrio::LOG_ERROR);
1408 if (m_flatImageStream.md[0].size[1] != m_dmHeight)
1410 ImageStreamIO_closeIm(&m_flatImageStream);
1411 derivedT::template log<text_log>(
"height mismatch between " + m_shmimFlat +
" and configured DM", logPrio::LOG_ERROR);
1415 m_flatImageStream.md->write = 1;
1419 memset(m_flatImageStream.array.raw, 0, m_dmWidth * m_dmHeight *
sizeof(realT));
1422 clock_gettime(CLOCK_REALTIME, &m_flatImageStream.md->writetime);
1425 m_flatImageStream.md->atime = m_flatImageStream.md->writetime;
1427 m_flatImageStream.md->cnt0++;
1428 m_flatImageStream.md->write = 0;
1429 ImageStreamIO_sempost(&m_flatImageStream, -1);
1434 ImageStreamIO_closeIm(&m_flatImageStream);
1436 derived().updateSwitchIfChanged(m_indiP_setFlat,
"toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle);
1438 derivedT::template log<text_log>(
"flat zeroed");
1443template <
class derivedT,
typename realT>
1446 std::vector<std::string> tfs;
1447 mx::error_t errc = mx::ioutils::getFileNames(tfs, m_testPath,
"",
"",
".fits");
1449 mx_error_check_rv(errc, -1);
1451 for (
auto it = m_testCommands.begin();
it != m_testCommands.end(); ++
it)
1456 bool changed =
false;
1457 for (
size_t n = 0; n < tfs.size(); ++n)
1459 auto ir = m_testCommands.insert(std::pair<std::string, std::string>(mx::ioutils::pathStem(tfs[n]), tfs[n]));
1460 if (ir.second ==
true)
1463 ir.first->second = tfs[n];
1466 for (
auto it = m_testCommands.begin();
it != m_testCommands.end(); ++
it)
1468 if (
it->second ==
"")
1474 m_testCommands.erase(itdel);
1481 if (derived().m_indiDriver)
1483 derived().m_indiDriver->sendDelProperty(m_indiP_tests);
1484 derived().m_indiNewCallBacks.erase(m_indiP_tests.createUniqueKey());
1487 m_indiP_tests = pcf::IndiProperty(pcf::IndiProperty::Switch);
1488 m_indiP_tests.setDevice(derived().configName());
1489 m_indiP_tests.setName(
"test");
1490 m_indiP_tests.setPerm(pcf::IndiProperty::ReadWrite);
1491 m_indiP_tests.setState(pcf::IndiProperty::Idle);
1492 m_indiP_tests.setRule(pcf::IndiProperty::OneOfMany);
1495 for (
auto it = m_testCommands.begin();
it != m_testCommands.end(); ++
it)
1497 if (
it->first == m_testCurrent || m_testCurrent ==
"")
1499 m_indiP_tests.add(pcf::IndiElement(
it->first, pcf::IndiElement::On));
1500 m_testCurrent =
it->first;
1504 m_indiP_tests.add(pcf::IndiElement(
it->first, pcf::IndiElement::Off));
1508 if (m_testDefault !=
"")
1510 if (m_testCurrent ==
"default")
1512 m_indiP_tests.add(pcf::IndiElement(
"default", pcf::IndiElement::On));
1516 m_indiP_tests.add(pcf::IndiElement(
"default", pcf::IndiElement::Off));
1520 if (derived().registerIndiPropertyNew(m_indiP_tests, st_newCallBack_tests) < 0)
1522#ifndef DM_TEST_NOLOG
1523 derivedT::template log<software_error>({__FILE__, __LINE__});
1528 if (derived().m_indiDriver)
1530 derived().m_indiDriver->sendDefProperty(m_indiP_tests);
1537template <
class derivedT,
typename realT>
1540 std::string target = intarget;
1542 if (target ==
"default")
1544 target = m_testDefault;
1547 std::string targetPath;
1551 targetPath = m_testCommands.at(target);
1555 derivedT::template log<text_log>(
"test file " + target +
" not found", logPrio::LOG_ERROR);
1559 m_testLoaded =
false;
1561 mx::fits::fitsFile<realT> ff;
1562 mx::error_t errc = ff.read(m_testCommand, targetPath);
1563 if ( errc != mx::error_t::noerror)
1565 derivedT::template log<text_log>(std::format(
"error reading test file {}: "
1566 "{} ({})", targetPath, mx::errorMessage(errc),
1567 mx::errorName(errc)), logPrio::LOG_ERROR);
1571 derivedT::template log<text_log>(
"loaded test file " + targetPath);
1572 m_testLoaded =
true;
1574 m_testCurrent = intarget;
1576 if (m_indiP_tests.find(
"default"))
1578 if (m_testCurrent ==
"default")
1580 m_indiP_tests[
"default"] = pcf::IndiElement::On;
1584 m_indiP_tests[
"default"] = pcf::IndiElement::Off;
1588 for (
auto i = m_testCommands.begin(); i != m_testCommands.end(); ++i)
1590 if (!m_indiP_tests.find(i->first))
1595 if (i->first == m_testCurrent)
1597 m_indiP_tests[i->first] = pcf::IndiElement::On;
1601 m_indiP_tests[i->first] = pcf::IndiElement::Off;
1605 if (derived().m_indiDriver)
1606 derived().m_indiDriver->sendSetProperty(m_indiP_tests);
1614template <
class derivedT,
typename realT>
1618 if (m_shmimTest ==
"")
1621 if (ImageStreamIO_openIm(&m_testImageStream, m_shmimTest.c_str()) != 0)
1623 derivedT::template log<text_log>(
"could not connect to test channel " + m_shmimTest, logPrio::LOG_WARNING);
1627 if (m_testImageStream.md->size[0] != m_dmWidth)
1629 ImageStreamIO_closeIm(&m_testImageStream);
1630 derivedT::template log<text_log>(
"width mismatch between " + m_shmimTest +
" and configured DM", logPrio::LOG_ERROR);
1634 if (m_testImageStream.md->size[1] != m_dmHeight)
1636 ImageStreamIO_closeIm(&m_testImageStream);
1637 derivedT::template log<text_log>(
"height mismatch between " + m_shmimTest +
" and configured DM", logPrio::LOG_ERROR);
1643 bool testSet = m_testSet;
1646 if (loadTest(m_testCurrent) < 0)
1648 derivedT::template log<text_log>(
"error loading test " + m_testCurrent, logPrio::LOG_ERROR);
1650 m_testSet = testSet;
1655 ImageStreamIO_closeIm(&m_testImageStream);
1656 derivedT::template log<text_log>(
"no test loaded", logPrio::LOG_ERROR);
1660 if (m_testCommand.rows() != m_dmWidth)
1662 ImageStreamIO_closeIm(&m_testImageStream);
1663 derivedT::template log<text_log>(
"width mismatch between test file and configured DM", logPrio::LOG_ERROR);
1667 if (m_testCommand.cols() != m_dmHeight)
1669 ImageStreamIO_closeIm(&m_testImageStream);
1670 derivedT::template log<text_log>(
"height mismatch between test file and configured DM", logPrio::LOG_ERROR);
1674 m_testImageStream.md->write = 1;
1678 memcpy(m_testImageStream.array.raw, m_testCommand.data(), m_dmWidth * m_dmHeight *
sizeof(realT));
1681 clock_gettime(CLOCK_REALTIME, &m_testImageStream.md->writetime);
1684 m_testImageStream.md->atime = m_testImageStream.md->writetime;
1686 m_testImageStream.md->cnt0++;
1687 m_testImageStream.md->write = 0;
1688 ImageStreamIO_sempost(&m_testImageStream, -1);
1693 ImageStreamIO_closeIm(&m_testImageStream);
1695 derived().updateSwitchIfChanged(m_indiP_setTest,
"toggle", pcf::IndiElement::On, pcf::IndiProperty::Busy);
1697 derivedT::template log<text_log>(
"test set");
1702template <
class derivedT,
typename realT>
1705 if (m_shmimTest ==
"")
1708 if (ImageStreamIO_openIm(&m_testImageStream, m_shmimTest.c_str()) != 0)
1710 derivedT::template log<text_log>(
"could not connect to test channel " + m_shmimTest, logPrio::LOG_WARNING);
1714 if (m_testImageStream.md[0].size[0] != m_dmWidth)
1716 ImageStreamIO_closeIm(&m_testImageStream);
1717 derivedT::template log<text_log>(
"width mismatch between " + m_shmimTest +
" and configured DM", logPrio::LOG_ERROR);
1721 if (m_testImageStream.md[0].size[1] != m_dmHeight)
1723 ImageStreamIO_closeIm(&m_testImageStream);
1724 derivedT::template log<text_log>(
"height mismatch between " + m_shmimTest +
" and configured DM", logPrio::LOG_ERROR);
1728 m_testImageStream.md->write = 1;
1732 memset(m_testImageStream.array.raw, 0, m_dmWidth * m_dmHeight *
sizeof(realT));
1735 clock_gettime(CLOCK_REALTIME, &m_testImageStream.md->writetime);
1738 m_testImageStream.md->atime = m_testImageStream.md->writetime;
1740 m_testImageStream.md->cnt0++;
1741 m_testImageStream.md->write = 0;
1744 ImageStreamIO_sempost(&m_testImageStream, -1);
1748 ImageStreamIO_closeIm(&m_testImageStream);
1750 derived().updateSwitchIfChanged(m_indiP_setTest,
"toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle);
1752 derivedT::template log<text_log>(
"test zeroed");
1757template <
class derivedT,
typename realT>
1760 if (derived().m_shmimName ==
"")
1767 for (
int n = 0; n < m_channels; ++n)
1770 snprintf(nstr,
sizeof(nstr),
"%02d", n);
1771 std::string shmimN = derived().m_shmimName + nstr;
1773 if (ImageStreamIO_openIm(&imageStream, shmimN.c_str()) != 0)
1775 derivedT::template log<text_log>(
"could not connect to channel " + shmimN, logPrio::LOG_WARNING);
1779 if (imageStream.md->size[0] != m_dmWidth)
1781 ImageStreamIO_closeIm(&imageStream);
1782 derivedT::template log<text_log>(
"width mismatch between " + shmimN +
" and configured DM", logPrio::LOG_ERROR);
1783 derived().updateSwitchIfChanged(m_indiP_zeroAll,
"request", pcf::IndiElement::Off,
INDI_IDLE);
1787 if (imageStream.md->size[1] != m_dmHeight)
1789 ImageStreamIO_closeIm(&imageStream);
1790 derivedT::template log<text_log>(
"height mismatch between " + shmimN +
" and configured DM", logPrio::LOG_ERROR);
1791 derived().updateSwitchIfChanged(m_indiP_zeroAll,
"request", pcf::IndiElement::Off,
INDI_IDLE);
1795 imageStream.md->write = 1;
1796 memset(imageStream.array.raw, 0, m_dmWidth * m_dmHeight *
sizeof(realT));
1798 clock_gettime(CLOCK_REALTIME, &imageStream.md->writetime);
1801 imageStream.md->atime = imageStream.md->writetime;
1803 imageStream.md->cnt0++;
1804 imageStream.md->write = 0;
1807 if (n == m_channels - 1 && !nosem)
1808 ImageStreamIO_sempost(&imageStream, -1);
1810 ImageStreamIO_closeIm(&imageStream);
1813 derivedT::template log<text_log>(
"all channels zeroed", logPrio::LOG_NOTICE);
1815 derived().updateSwitchIfChanged(m_indiP_zeroAll,
"request", pcf::IndiElement::Off,
INDI_IDLE);
1819 derived().updateSwitchIfChanged(m_indiP_setFlat,
"toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle);
1823 derived().updateSwitchIfChanged(m_indiP_setTest,
"toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle);
1826 if ((rv = clearSat()) < 0)
1828 derivedT::template log<software_error>({__FILE__, __LINE__, errno, rv,
"Error from clearSat"});
1835template <
class derivedT,
typename realT>
1838 if(m_shmimSat ==
"" || m_dmWidth == 0 || m_dmHeight == 0)
1845 std::vector<std::string> sats = {m_shmimSat, m_shmimSatPerc};
1847 for (
size_t n = 0; n < sats.size(); ++n)
1849 std::string shmimN = sats[n];
1851 if (ImageStreamIO_openIm(&imageStream, shmimN.c_str()) != 0)
1853 derivedT::template log<text_log>(
"could not connect to sat map " + shmimN, logPrio::LOG_WARNING);
1857 if (imageStream.md->size[0] != m_dmWidth)
1859 ImageStreamIO_closeIm(&imageStream);
1860 derivedT::template log<text_log>(
"width mismatch between " + shmimN +
" and configured DM", logPrio::LOG_ERROR);
1861 derived().updateSwitchIfChanged(m_indiP_zeroAll,
"request", pcf::IndiElement::Off,
INDI_IDLE);
1865 if (imageStream.md->size[1] != m_dmHeight)
1867 ImageStreamIO_closeIm(&imageStream);
1868 derivedT::template log<text_log>(
"height mismatch between " + shmimN +
" and configured DM", logPrio::LOG_ERROR);
1869 derived().updateSwitchIfChanged(m_indiP_zeroAll,
"request", pcf::IndiElement::Off,
INDI_IDLE);
1873 imageStream.md->write = 1;
1874 memset(imageStream.array.raw, 0, m_dmWidth * m_dmHeight * ImageStreamIO_typesize(imageStream.md->datatype));
1876 clock_gettime(CLOCK_REALTIME, &imageStream.md->writetime);
1879 imageStream.md->atime = imageStream.md->writetime;
1881 imageStream.md->cnt0++;
1882 imageStream.md->write = 0;
1884 ImageStreamIO_closeIm(&imageStream);
1887 m_accumSatMap.setZero();
1888 m_instSatMap.setZero();
1893template <
class derivedT,
typename realT>
1899template <
class derivedT,
typename realT>
1903 m_satThreadID = syscall(SYS_gettid);
1906 while (m_satThreadInit ==
true && derived().shutdown() == 0)
1910 if (derived().shutdown())
1913 uint32_t imsize[3] = {0, 0, 0};
1916 while ((m_shmimSat ==
"" || m_accumSatMap.rows() == 0 || m_accumSatMap.cols() == 0) && !derived().shutdown())
1921 if (derived().shutdown())
1926 imsize[0] = m_dmWidth;
1927 imsize[1] = m_dmHeight;
1930 ImageStreamIO_createIm_gpu(&m_satImageStream, m_shmimSat.c_str(), 3, imsize,
IMAGESTRUCT_UINT8, -1, 1, IMAGE_NB_SEMAPHORE, 0, CIRCULAR_BUFFER | ZAXIS_TEMPORAL, 0);
1931 ImageStreamIO_createIm_gpu(&m_satPercImageStream, m_shmimSatPerc.c_str(), 3, imsize,
IMAGESTRUCT_FLOAT, -1, 1, IMAGE_NB_SEMAPHORE, 0, CIRCULAR_BUFFER | ZAXIS_TEMPORAL, 0);
1935 m_satImageStream.md->cnt1 = 0;
1936 m_satPercImageStream.md->cnt1 = 0;
1939 mx::improc::eigenImage<uint8_t> satmap(m_dmWidth, m_dmHeight);
1942 double t_accumst = mx::sys::get_curr_time();
1945 while (!derived().shutdown())
1949 if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
1951 derivedT::template log<software_critical>({__FILE__, __LINE__, errno, 0,
"clock_gettime"});
1957 if (sem_timedwait(&m_satSemaphore, &ts) == 0)
1960 for (
int rr = 0; rr < m_instSatMap.rows(); ++rr)
1962 for (
int cc = 0; cc < m_instSatMap.cols(); ++cc)
1964 m_accumSatMap(rr, cc) += m_instSatMap(rr, cc);
1970 if (mx::sys::get_curr_time(ts) - t_accumst < m_satAvgInt / 1000.0)
1977 for (
int rr = 0; rr < m_instSatMap.rows(); ++rr)
1979 for (
int cc = 0; cc < m_instSatMap.cols(); ++cc)
1981 m_satPercMap(rr, cc) = m_accumSatMap(rr, cc) / naccum;
1982 if (m_satPercMap(rr, cc) >= m_percThreshold)
1986 satmap(rr, cc) = (m_accumSatMap(rr, cc) > 0);
1992 if (m_overSatAct / (m_satPercMap.rows() * m_satPercMap.cols() * 0.75) > m_intervalSatThreshold)
1994 ++m_intervalSatExceeds;
1998 m_intervalSatExceeds = 0;
2002 if (m_intervalSatExceeds >= m_intervalSatCountThreshold)
2004 m_intervalSatTrip =
true;
2007 m_satImageStream.md->write = 1;
2008 m_satPercImageStream.md->write = 1;
2010 memcpy(m_satImageStream.array.raw, satmap.data(), m_dmWidth * m_dmHeight *
sizeof(uint8_t));
2011 memcpy(m_satPercImageStream.array.raw, m_satPercMap.data(), m_dmWidth * m_dmHeight *
sizeof(
float));
2014 clock_gettime(CLOCK_REALTIME, &m_satImageStream.md->writetime);
2015 m_satPercImageStream.md->writetime = m_satImageStream.md->writetime;
2018 m_satImageStream.md->atime = m_satImageStream.md->writetime;
2019 m_satPercImageStream.md->atime = m_satPercImageStream.md->writetime;
2022 m_satImageStream.md->cnt1 = 0;
2023 m_satPercImageStream.md->cnt1 = 0;
2026 m_satImageStream.md->cnt0++;
2027 m_satPercImageStream.md->cnt0++;
2029 m_satImageStream.writetimearray[0] = m_satImageStream.md->writetime;
2030 m_satImageStream.atimearray[0] = m_satImageStream.md->atime;
2031 m_satImageStream.cntarray[0] = m_satImageStream.md->cnt0;
2033 m_satPercImageStream.writetimearray[0] = m_satPercImageStream.md->writetime;
2034 m_satPercImageStream.atimearray[0] = m_satPercImageStream.md->atime;
2035 m_satPercImageStream.cntarray[0] = m_satPercImageStream.md->cnt0;
2038 m_satImageStream.md->write = 0;
2039 ImageStreamIO_sempost(&m_satImageStream, -1);
2041 m_satPercImageStream.md->write = 0;
2042 ImageStreamIO_sempost(&m_satPercImageStream, -1);
2044 m_accumSatMap.setZero();
2046 t_accumst = mx::sys::get_curr_time(ts);
2058 if (errno != ETIMEDOUT)
2060 derivedT::template log<software_error>({__FILE__, __LINE__, errno,
"sem_timedwait"});
2068 ImageStreamIO_destroyIm(&m_satImageStream);
2070 ImageStreamIO_destroyIm(&m_satPercImageStream);
2074template <
class derivedT,
typename realT>
2077 if (!derived().m_indiDriver)
2083template <
class derivedT,
typename realT>
2085 const pcf::IndiProperty &
ipRecv)
2087 return static_cast<derivedT *
>(app)->newCallBack_init(
ipRecv);
2090template <
class derivedT,
typename realT>
2093 if (
ipRecv.createUniqueKey() != m_indiP_init.createUniqueKey())
2095 return derivedT::template log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2098 if (!
ipRecv.find(
"request"))
2101 if (
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
2103 return derived().initDM();
2108template <
class derivedT,
typename realT>
2110 const pcf::IndiProperty &
ipRecv)
2112 return static_cast<derivedT *
>(app)->newCallBack_zero(
ipRecv);
2115template <
class derivedT,
typename realT>
2118 if (
ipRecv.createUniqueKey() != m_indiP_zero.createUniqueKey())
2120 return derivedT::template log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2123 if (!
ipRecv.find(
"request"))
2126 if (
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
2128 return derived().zeroDM();
2133template <
class derivedT,
typename realT>
2135 const pcf::IndiProperty &
ipRecv)
2137 return static_cast<derivedT *
>(app)->newCallBack_release(
ipRecv);
2140template <
class derivedT,
typename realT>
2143 if (
ipRecv.createUniqueKey() != m_indiP_release.createUniqueKey())
2145 return derivedT::template log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2148 if (!
ipRecv.find(
"request"))
2151 if (
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
2158template <
class derivedT,
typename realT>
2160 const pcf::IndiProperty &
ipRecv)
2162 return static_cast<derivedT *
>(app)->newCallBack_flats(
ipRecv);
2165template <
class derivedT,
typename realT>
2168 if (
ipRecv.createUniqueKey() != m_indiP_flats.createUniqueKey())
2170 derivedT::template log<software_error>({__FILE__, __LINE__,
"invalid indi property received"});
2174 std::string newFlat;
2176 if (
ipRecv.find(
"default"))
2178 if (
ipRecv[
"default"].getSwitchState() == pcf::IndiElement::On)
2180 newFlat =
"default";
2185 for (
auto i = m_flatCommands.begin(); i != m_flatCommands.end(); ++i)
2187 if (!
ipRecv.find(i->first))
2190 if (
ipRecv[i->first].getSwitchState() == pcf::IndiElement::On)
2194 derivedT::template log<text_log>(
"More than one flat selected", logPrio::LOG_ERROR);
2205 return loadFlat(newFlat);
2208template <
class derivedT,
typename realT>
2210 const pcf::IndiProperty &
ipRecv)
2212 return static_cast<derivedT *
>(app)->newCallBack_setFlat(
ipRecv);
2215template <
class derivedT,
typename realT>
2218 if (
ipRecv.createUniqueKey() != m_indiP_setFlat.createUniqueKey())
2220 return derivedT::template log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2223 if (!
ipRecv.find(
"toggle"))
2226 if (
ipRecv[
"toggle"] == pcf::IndiElement::On)
2236template <
class derivedT,
typename realT>
2238 const pcf::IndiProperty &
ipRecv)
2240 return static_cast<derivedT *
>(app)->newCallBack_tests(
ipRecv);
2243template <
class derivedT,
typename realT>
2246 if (
ipRecv.createUniqueKey() != m_indiP_tests.createUniqueKey())
2248 derivedT::template log<software_error>({__FILE__, __LINE__,
"invalid indi property received"});
2252 std::string newTest;
2254 if (
ipRecv.find(
"default"))
2256 if (
ipRecv[
"default"].getSwitchState() == pcf::IndiElement::On)
2258 newTest =
"default";
2263 for (
auto i = m_testCommands.begin(); i != m_testCommands.end(); ++i)
2265 if (!
ipRecv.find(i->first))
2268 if (
ipRecv[i->first].getSwitchState() == pcf::IndiElement::On)
2272 derivedT::template log<text_log>(
"More than one test selected", logPrio::LOG_ERROR);
2283 return loadTest(newTest);
2286template <
class derivedT,
typename realT>
2288 const pcf::IndiProperty &
ipRecv)
2290 return static_cast<derivedT *
>(app)->newCallBack_setTest(
ipRecv);
2293template <
class derivedT,
typename realT>
2296 if (
ipRecv.createUniqueKey() != m_indiP_setTest.createUniqueKey())
2298 return derivedT::template log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2301 if (!
ipRecv.find(
"toggle"))
2304 if (
ipRecv[
"toggle"] == pcf::IndiElement::On)
2314template <
class derivedT,
typename realT>
2316 const pcf::IndiProperty &
ipRecv)
2318 return static_cast<derivedT *
>(app)->newCallBack_zeroAll(
ipRecv);
2321template <
class derivedT,
typename realT>
2324 if (
ipRecv.createUniqueKey() != m_indiP_zeroAll.createUniqueKey())
2326 return derivedT::template log<software_error, -1>({__FILE__, __LINE__,
"wrong INDI-P in callback"});
2329 if (!
ipRecv.find(
"request"))
2332 if (
ipRecv[
"request"].getSwitchState() == pcf::IndiElement::On)
2336 std::lock_guard<std::mutex> guard(derived().m_indiMutex);
2346#define DM_SETUP_CONFIG(cfig) \
2347 if (dmT::setupConfig(cfig) < 0) \
2349 log<software_error>({__FILE__, __LINE__, "Error from dmT::setupConfig"}); \
2350 m_shutdown = true; \
2358#define DM_LOAD_CONFIG(cfig) \
2359 if (dmT::loadConfig(cfig) < 0) \
2361 return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::loadConfig"}); \
2365#define DM_APP_STARTUP \
2366 if (dmT::appStartup() < 0) \
2368 return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::appStartup"}); \
2372#define DM_APP_LOGIC \
2373 if (dmT::appLogic() < 0) \
2375 return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::appLogic"}); \
2379#define DM_UPDATE_INDI \
2380 if (dmT::updateINDI() < 0) \
2382 return log<software_error, -1>({__FILE__, __LINE__, "Error from dmT::updateINDI"}); \
2386#define DM_APP_SHUTDOWN \
2387 if (dmT::appShutdown() < 0) \
2389 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.
mx::verbose::vvv verboseT
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
The MagAO-X generic shared memory monitor.