API
 
Loading...
Searching...
No Matches
dm.hpp
Go to the documentation of this file.
1/** \file dm.hpp
2 * \brief The MagAO-X generic deformable mirror controller.
3 *
4 * \author Jared R. Males (jaredmales@gmail.com)
5 *
6 * \ingroup app_files
7 */
8
9#ifndef dm_hpp
10#define dm_hpp
11
12#include <mx/improc/eigenImage.hpp>
13#include <mx/improc/milkImage.hpp>
14#include <mx/ioutils/fits/fitsFile.hpp>
15
16#include "shmimMonitor.hpp"
17
18namespace MagAOX
19{
20namespace app
21{
22namespace dev
23{
24
25template <typename typeT>
26constexpr uint8_t ImageStreamTypeCode()
27{
28 return 0;
29}
30
31template <>
32constexpr uint8_t ImageStreamTypeCode<float>()
33{
34 return _DATATYPE_FLOAT;
35}
36
37template <>
38constexpr uint8_t ImageStreamTypeCode<double>()
39{
40 return _DATATYPE_DOUBLE;
41}
42
43/** MagAO-X generic deformable mirror controller
44 *
45 *
46 * The derived class `derivedT` must meet the following requirements:
47 * - Must be a MagAOXApp<true>
48 *
49 * - Must be a dev::shmimMonitor<derivedT>
50 *
51 * - Must NOT call dev::shmimMonitor<derivedT>::setupConfog and dev::shmimMonitor<derivedT>::loadConfig. These are
52 * handled by this class.
53 *
54 * - Must expose the following interface
55 * \code
56 * int initDM();
57 * int commandDM(void * cmd);
58 * int zeroDM();
59 * int releaseDM();
60 * \endcode
61 * Each of the above functions should return 0 on success, and -1 on an error.
62 *
63 * - This class must be declared a friend in the derived class, like so:
64 * \code
65 * friend class dev::dm<derivedT,realT>;
66 * \endcode
67 *
68 * - Must contain the following typedef:
69 * \code
70 * typedef dev::dm<derivedT, realT> dmT;
71 * \endcode
72 *
73 * - Calls to this class's `setupConfig`, `loadConfig`, `appStartup`, `appLogic`, `appShutdown`, and `udpdateINDI`
74 * functions must be placed in the derived class's functions of the same name. For convenience the
75 * following macros are defined to provide error checking:
76 * \code
77 * DM_SETUP_CONFIG( cfig )
78 * DM_LOAD_CONFIG( cfig )
79 * DM_APP_STARTUP
80 * DM_APP_LOGIC
81 * DM_UPDATE_INDI
82 * DM_APP_SHUTDOWN
83 * \endcode
84 *
85 *
86 * \ingroup appdev
87 */
88template <class derivedT, typename realT>
89class dm
90{
91
92 typedef mx::verbose::vvv verboseT;
93
94 protected:
95 /** \name Configurable Parameters
96 * @{
97 */
98
99 std::string m_calibPath; ///< The path to this DM's calibration files.
100 std::string m_flatPath; ///< The path to this DM's flat files (usually the same as calibPath)
101 std::string m_testPath; ///< The path to this DM's test files (default is calibPath/tests;
102
103 std::string m_actMaskPath; ///< The file name of the actuator mask for this DM
104
105 std::string m_flatDefault; ///< The file name of the this DM's default flat command. Path and extension will be
106 ///< ignored and can be omitted.
107 std::string m_testDefault; ///< The file name of the this DM's default test command. Path and extension will be
108 ///< ignored and can be omitted.
109
110 std::string m_shmimFlat; ///< The name of the shmim stream to write the flat to.
111 std::string m_shmimTest; ///< The name of the shmim stream to write the test to.
112 std::string m_shmimSat; ///< The name of the shmim stream to write the saturation map to.
113 std::string m_shmimSatPerc; ///< The name of the shmim stream to write the saturation percentage map to.
114
115 int m_satAvgInt{ 100 }; ///< The time in milliseconds to accumulate saturation over.
116
117 int m_satThreadPrio{ 0 }; ///< Priority of the saturation thread. Usually ok to be 0.
118
119 std::string m_shmimShape; ///< The name of the shmim stream to write the desaturated true shape to.
120 std::string m_shmimDelta; ///< The name of the shmim stream to write the desaturated delta command to.
121 std::string m_shmimDiff; ///< The name of the shmim stream to write the difference to.
122
123 uint32_t m_dmWidth{ 0 }; ///< The width of the images in the stream
124 uint32_t m_dmHeight{ 0 }; ///< The height of the images in the stream
125
126 static constexpr uint8_t m_dmDataType = ImageStreamTypeCode<realT>(); ///< The ImageStreamIO type code.
127
128 float m_percThreshold{ 0.98 }; ///< Threshold on percentage of frames an actuator is saturated over an interval.
129
130 float m_intervalSatThreshold{ 0.50 }; /**< Threshold on percentage of actuators which exceed
131 percThreshold in an interval.*/
132
133 int m_intervalSatCountThreshold{ 10 }; /**< Threshold on number of consecutive intervals
134 the intervalSatThreshold is exceeded. */
135
136 std::vector<std::string> m_satTriggerDevice; ///< Device(s) with a toggle switch to toggle on saturation trigger.
137
138 std::vector<std::string> m_satTriggerProperty; /**< Property with a toggle switch to toggle on saturation trigger,
139 one per entry in satTriggerDevice.*/
140
141 ///@}
142
143 std::string m_calibRelDir; ///< The directory relative to the calibPath. Set this before calling
144 ///< dm<derivedT,realT>::loadConfig().
145
146 int m_numChannels{ 0 }; ///< The number of dmcomb channels found as part of allocation.
147
148 std::vector<mx::improc::milkImage<realT> *> m_channels;
149
150 std::map<std::string, std::string> m_flatCommands; ///< Map of flat file name to full path
151 std::string m_flatCurrent; ///< The name of the current flat command
152
153 mx::improc::eigenImage<realT> m_flatCommand; ///< Data storage for the flat command
154 bool m_flatLoaded{ false }; ///< Flag indicating whether a flat is loaded in memory
155
156 IMAGE m_flatImageStream; ///< The ImageStreamIO shared memory buffer for the flat.
157 bool m_flatSet{ false }; ///< Flag indicating whether the flat command has been set.
158
159 mx::improc::milkImage<realT> m_actMask;
160
161 std::map<std::string, std::string> m_testCommands; ///< Map of test file name to full path
162 std::string m_testCurrent;
163
164 mx::improc::eigenImage<realT> m_testCommand; ///< Data storage for the test command
165 bool m_testLoaded{ false }; ///< Flag indicating whether a test command is loaded in memory
166
167 IMAGE m_testImageStream; ///< The ImageStreamIO shared memory buffer for the test.
168 bool m_testSet{ false }; ///< Flag indicating whether the test command has been set.
169
170 mx::improc::eigenImage<uint8_t> m_instSatMap; /**< The instantaneous saturation map, 0/1, set by the commandDM()
171 function of the derived class.*/
172
173 mx::improc::eigenImage<uint16_t> m_accumSatMap; /**< The accumulated saturation map, which acccumulates for
174 m_satAvgInt then is publised as a 0/1 image. */
175
176 mx::improc::eigenImage<float> m_satPercMap; /**< Map of the percentage of time each actuator was
177 saturated during the avg. interval.*/
178
179 IMAGE m_satImageStream; ///< The ImageStreamIO shared memory buffer for the sat map.
180 IMAGE m_satPercImageStream; ///< The ImageStreamIO shared memory buffer for the sat percentage map.
181
182 int m_overSatAct{ 0 }; // counter
183 int m_intervalSatExceeds{ 0 }; // counter
184 bool m_intervalSatTrip{ 0 }; // flag to trip the loop opening
185
186 mx::improc::milkImage<realT> m_outputShape; ///< The true output shape after saturation.
187
188 std::vector<std::string> m_deltaChannels; ///< The names of channels which are treated as delta commands
189
190 std::vector<size_t> m_deltas; ///< Indices of the channels which are delta commands
191 std::vector<size_t> m_notDeltas; ///< Indices of the channels which are not delta commands
192
193 mx::improc::eigenImage<realT> m_totalFlat; ///< the total of all non-delta channels
194 mx::improc::eigenImage<realT> m_totalDelta; ///< the total of all delta channels
195
196 mx::improc::milkImage<realT> m_outputDelta; ///< The true output delta command after saturation.
197 mx::improc::milkImage<realT>
198 m_outputDiff; ///< The difference between command and true delta command after saturation.
199
200 /** \name Saturation Thread Data
201 * This thread processes the saturation maps
202 * @{
203 */
204
205 sem_t m_satSemaphore; ///< Semaphore used to tell the saturation thread to run.
206
207 bool m_satThreadInit{ true }; ///< Synchronizer for thread startup, to allow priority setting to finish.
208
209 pid_t m_satThreadID{ 0 }; ///< The ID of the saturation thread.
210
211 pcf::IndiProperty m_satThreadProp; ///< The property to hold the saturation thread details.
212
213 std::thread m_satThread; ///< A separate thread for the actual saturation processing
214
215 ///@}
216
217 public:
218 /// Destructor
219 /** deallocates the m_channels vector
220 *
221 */
223
224 /// Get the
225 /**
226 * \returns the current value of
227 */
228 const std::string &calibPath() const;
229
230 /// Get the
231 /**
232 * \returns the current value of
233 */
234 const std::string &flatPath() const;
235
236 /// Get the
237 /**
238 * \returns the current value of
239 */
240 const std::string &testPath() const;
241
242 /// Get the
243 /**
244 * \returns the current value of
245 */
246 const std::string &flatDefault() const;
247
248 /// Get the
249 /**
250 * \returns the current value of
251 */
252 const std::string &testDefault() const;
253
254 /// Get the
255 /**
256 * \returns the current value of
257 */
258 const std::string &shmimFlat() const;
259
260 /// Get the
261 /**
262 * \returns the current value of
263 */
264 const std::string &shmimTest() const;
265
266 /// Get the
267 /**
268 * \returns the current value of
269 */
270 const std::string &shmimSat() const;
271
272 /// Get the stream name for saturation percentage
273 /**
274 * \returns the current value of m_shmimSatPerc
275 */
276 const std::string &shmimSatPerc() const;
277
278 /// Get the saturation accumulation interval
279 /**
280 * \returns the current value of m_satAvgInt
281 */
282 int satAvgInt() const;
283
284 /// Get the saturation thread priority
285 /**
286 * \returns the current value of m_satThreadPrio
287 */
288 int satThreadPrio() const;
289
290 /// Get the
291 /**
292 * \returns the current value of
293 */
294 const std::string &shmimShape() const;
295
296 /// Get the
297 /**
298 * \returns the current value of
299 */
300 const std::string &shmimDelta() const;
301
302 /// Get the DM Width
303 /**
304 * \returns the current value of m_dmWidth
305 */
306 uint32_t dmWidth() const;
307
308 /// Get the DM Height
309 /**
310 * \returns the current value of m_dmHeight
311 */
312 uint32_t dmHeight() const;
313
314 /// Get the DM data type
315 /**
316 * \returns the current value of m_dmDataType
317 */
318 uint8_t dmDataType() const;
319
320 /// Get the saturation percentage threshold
321 /**
322 * \returns the current value of m_percThreshold
323 */
324 float percThreshold() const;
325
326 /// Get the interval saturation threshold
327 /**
328 * \returns the current value of m_intervalSatThreshold
329 */
330 float intervalSatThreshold() const;
331
332 /// Get the interval saturation count threshold
333 /**
334 * \returns the current value of m_intervalSatCountThreshold
335 */
337
338 /// Get the saturation trigger device(s)
339 /**
340 * \returns the current value of m_satTriggerDevice
341 */
342 const std::vector<std::string> &satTriggerDevice() const;
343
344 /// Get the saturation trigger property(ies)
345 /**
346 * \returns the current value of m_satTriggerProperty
347 */
348 const std::vector<std::string> &satTriggerProperty() const;
349
350 const std::string &calibRelDir() const;
351
352 int numChannels() const;
353
354 const mx::improc::eigenImage<uint8_t> &instSatMap() const;
355
356 const mx::improc::eigenImage<uint16_t> &accumSatMap() const;
357
358 const mx::improc::eigenImage<float> &satPercMap() const;
359
360 const std::vector<std::string> &deltaChannels() const;
361
362 const std::vector<size_t> &notDeltas() const;
363
364 const mx::improc::eigenImage<float> &totalFlat() const;
365
366 /// Setup the configuration system
367 /**
368 * This should be called in `derivedT::setupConfig` as
369 * \code
370 dm<derivedT,realT>::setupConfig(config);
371 \endcode
372 * with appropriate error checking.
373 */
374 int setupConfig( mx::app::appConfigurator &config /**< [out] the derived classes configurator*/ );
375
376 /// load the configuration system results
377 /**
378 * This should be called in `derivedT::loadConfig` as
379 * \code
380 dm<derivedT,realT>::loadConfig(config);
381 \endcode
382 * with appropriate error checking.
383 */
384 int loadConfig( mx::app::appConfigurator &config /**< [in] the derived classes configurator*/ );
385
386 /// Startup function
387 /**
388 * This should be called in `derivedT::appStartup` as
389 * \code
390 dm<derivedT,realT>::appStartup();
391 \endcode
392 * with appropriate error checking.
393 *
394 * \returns 0 on success
395 * \returns -1 on error, which is logged.
396 */
398
399 /// DM application logic
400 /** This should be called in `derivedT::appLogic` as
401 * \code
402 dm<derivedT,realT>::appLogic();
403 \endcode
404 * with appropriate error checking.
405 *
406 * \returns 0 on success
407 * \returns -1 on error, which is logged.
408 */
409 int appLogic();
410
411 /// DM shutdown
412 /** This should be called in `derivedT::appShutdown` as
413 * \code
414 dm<derivedT,realT>::appShutdown();
415 \endcode
416 * with appropriate error checking.
417 *
418 * \returns 0 on success
419 * \returns -1 on error, which is logged.
420 */
422
423 /// DM Poweroff
424 /** This should be called in `derivedT::onPowerOff` as
425 * \code
426 dm<derivedT,realT>::onPowerOff();
427 \endcode
428 * with appropriate error checking.
429 *
430 * \returns 0 on success
431 * \returns -1 on error, which is logged.
432 */
434
435 /// DM Poweroff Updates
436 /** This should be called in `derivedT::whilePowerOff` as
437 * \code
438 dm<derivedT,realT>::whilePowerOff();
439 \endcode
440 * with appropriate error checking.
441 *
442 * \returns 0 on success
443 * \returns -1 on error, which is logged.
444 */
446
447 /// Find the DM comb channels
448 /** Introspectively finds all dmXXdispYY channels, zeroes them, and raises the semapahore
449 * on the last to cause dmcomb to update.
450 */
452
453 /// Called after shmimMonitor connects to the dmXXdisp stream. Checks for proper size.
454 /**
455 * \returns 0 on success
456 * \returns -1 if incorrect size or data type in stream.
457 */
458 int allocate( const dev::shmimT &sp );
459
460 /// Called by shmimMonitor when a new DM command is available. This is just a pass-through to
461 /// derivedT::commandDM(char*).
462 int processImage( void *curr_src, const dev::shmimT &sp );
463
464 /// Calls derived()->initDM()
465 /**
466 * \returns 0 on success
467 * \returns -1 on error from derived()->initDM()
468 */
470
471 /// Calls derived()->releaseDM() and then 0s all channels and the sat map.
472 /** This is called by the relevant INDI callback
473 *
474 * \returns 0 on success
475 * \returns -1 on error
476 */
478
479 /// Check the flats directory and update the list of flats if anything changes
480 /** This is called once per appLogic and whilePowerOff loops.
481 *
482 * \returns 0 on success
483 * \returns -1 on error
484 */
486
487 /// Load a flat file
488 /** Uses the target argument for lookup in m_flatCommands to find the path
489 * and loads the command in the local memory. Calls setFlat if the flat
490 * is currently set.
491 *
492 * \returns 0 on success
493 * \returns -1 on error
494 */
495 int loadFlat( const std::string &target /**< [in] the name of the flat to load */ );
496
497 /// Send the current flat command to the DM
498 /** Writes the command to the designated shmim.
499 *
500 * \returns 0 on success
501 * \returns -1 on error
502 */
503 int setFlat( bool update = false /**< [in] If true, this is an update rather than a new set*/ );
504
505 /// Zero the flat command on the DM
506 /** Writes a 0 array the designated shmim.
507 *
508 * \returns 0 on success
509 * \returns -1 on error
510 */
511 int zeroFlat();
512
513 /// Check the tests directory and update the list of tests if anything changes
514 /** This is called once per appLogic and whilePowerOff loops.
515 *
516 * \returns 0 on success
517 * \returns -1 on error
518 */
520
521 /// Load a test file
522 /** Uses the target argument for lookup in m_testCommands to find the path
523 * and loads the command in the local memory. Calls setTest if the test
524 * is currently set.
525 */
526 int loadTest( const std::string &target );
527
528 /// Send the current test command to the DM
529 /** Writes the command to the designated shmim.
530 *
531 * \returns 0 on success
532 * \returns -1 on error
533 */
534 int setTest();
535
536 /// Zero the test command on the DM
537 /** Writes a 0 array the designated shmim.
538 *
539 * \returns 0 on success
540 * \returns -1 on error
541 */
542 int zeroTest();
543
544 /// Zero all channels
545 /**
546 * \returns 0 on sucess
547 * \returns <0 on an error
548 */
549 int zeroAll( bool nosem = false /**< [in] [optional] if true then the semaphore
550 is not raised after zeroing all channels*/
551 );
552
553 /// Calculate the delta command from the output shape.
555
556 /// Clear the saturation maps and zero the shared memory.
557 /**
558 * \returns 0 on success
559 * \returns -1 on error
560 */
561 int clearSat();
562
563 protected:
564 /** \name Saturation Thread Functions
565 * This thread processes the saturation maps
566 * @{
567 */
568
569 /// Thread starter, called by MagAOXApp::threadStart on thread construction. Calls satThreadExec.
570 static void satThreadStart( dm *d /**< [in] a pointer to a dm instance (normally this) */ );
571
572 /// Execute saturation processing
574
575 /// Trigger loop openings because of excessive saturation
577
578 ///@}
579
580 protected:
581 /** \name INDI
582 *
583 *@{
584 */
585 protected:
586 // declare our properties
587
588 pcf::IndiProperty m_indiP_flat; ///< Property used to set and report the current flat
589
590 pcf::IndiProperty m_indiP_init;
591 pcf::IndiProperty m_indiP_zero;
592 pcf::IndiProperty m_indiP_release;
593
594 pcf::IndiProperty m_indiP_flats; ///< INDI Selection switch containing the flat files.
595 pcf::IndiProperty m_indiP_flatShmim; ///< Publish the shmim being used for the flat
596 pcf::IndiProperty m_indiP_setFlat; ///< INDI toggle switch to set the current flat.
597
598 pcf::IndiProperty m_indiP_tests; ///< INDI Selection switch containing the test pattern files.
599 pcf::IndiProperty m_indiP_testShmim; ///< Publish the shmim being used for the test command
600 pcf::IndiProperty m_indiP_setTest; ///< INDI toggle switch to set the current test pattern.
601
602 pcf::IndiProperty m_indiP_zeroAll;
603
604 public:
605 /// The static callback function to be registered for initializing the DM.
606 /**
607 * \returns 0 on success.
608 * \returns -1 on error.
609 */
610 static int st_newCallBack_init( void *app, /**< [in] a pointer to this, will be
611 static_cast-ed to derivedT.*/
612 const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the
613 the new property request.*/
614 );
615
616 /// The callback called by the static version, to actually process the new request.
617 /**
618 * \returns 0 on success.
619 * \returns -1 on error.
620 */
621 int newCallBack_init( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with
622 the the new property request.*/
623 );
624
625 /// The static callback function to be registered for initializing the DM.
626 /**
627 * \returns 0 on success.
628 * \returns -1 on error.
629 */
630 static int st_newCallBack_zero( void *app, /**< [in] a pointer to this, will be
631 static_cast-ed to derivedT.*/
632 const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with
633 the the new property request.*/
634 );
635
636 /// The callback called by the static version, to actually process the new request.
637 /**
638 * \returns 0 on success.
639 * \returns -1 on error.
640 */
642 const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the
643 the new property request.*/ );
644
645 /// The static callback function to be registered for initializing the DM.
646 /**
647 * \returns 0 on success.
648 * \returns -1 on error.
649 */
650 static int st_newCallBack_release( void *app, /**< [in] a pointer to this, will be
651 static_cast-ed to derivedT.*/
652 const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with
653 the the new property request.*/
654 );
655
656 /// The callback called by the static version, to actually process the new request.
657 /**
658 * \returns 0 on success.
659 * \returns -1 on error.
660 */
661 int newCallBack_release( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with
662 the new property request.*/
663 );
664
665 /// The static callback function to be registered for selecting the flat file
666 /**
667 * \returns 0 on success.
668 * \returns -1 on error.
669 */
670 static int st_newCallBack_flats( void *app, /**< [in] a pointer to this, will be
671 static_cast-ed to derivedT.*/
672 const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with
673 the new property request.*/
674 );
675
676 /// The callback called by the static version, to actually process the new request.
677 /**
678 * \returns 0 on success.
679 * \returns -1 on error.
680 */
682 const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
683
684 /// The static callback function to be registered for setting the flat
685 /**
686 * \returns 0 on success.
687 * \returns -1 on error.
688 */
690 void *app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
691 const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
692 );
693
694 /// The callback called by the static version, to actually process the new request.
695 /**
696 * \returns 0 on success.
697 * \returns -1 on error.
698 */
700 const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
701
702 /// The static callback function to be registered for selecting the test file
703 /**
704 * \returns 0 on success.
705 * \returns -1 on error.
706 */
708 void *app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
709 const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
710 );
711
712 /// The callback called by the static version, to actually process the new request.
713 /**
714 * \returns 0 on success.
715 * \returns -1 on error.
716 */
718 const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
719
720 /// The static callback function to be registered for setting the test shape
721 /**
722 * \returns 0 on success.
723 * \returns -1 on error.
724 */
725 static int st_newCallBack_setTest( void *app, /**< [in] a pointer to this, will be
726 static_cast-ed to derivedT.*/
727 const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with
728 the the new property request.*/
729 );
730
731 /// The callback called by the static version, to actually process the new request.
732 /**
733 * \returns 0 on success.
734 * \returns -1 on error.
735 */
736 int newCallBack_setTest( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the
737 the new property request.*/
738 );
739
740 /// The static callback function to be registered for zeroing all channels
741 /**
742 * \returns 0 on success.
743 * \returns -1 on error.
744 */
746 void *app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
747 const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
748 );
749
750 /// The callback for the zeroAll toggle switch, called by the static version
751 /**
752 * \returns 0 on success.
753 * \returns -1 on error.
754 */
756 const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/ );
757
758 /// Update the INDI properties for this device controller
759 /** You should call this once per main loop.
760 * It is not called automatically.
761 *
762 * \returns 0 on success.
763 * \returns -1 on error.
764 */
766
767 ///@}
768
769 public:
770 // clang-format off
771 #ifdef XWC_DMTIMINGS //clang-format on
772
773 typedef int32_t cbIndexT;
774
775 double m_t0{ 0 }, m_tf{ 0 }, m_tsat0{ 0 }, m_tsatf{ 0 };
776 double m_tact0{ 0 }, m_tact1{ 0 }, m_tact2{ 0 }, m_tact3{ 0 }, m_tact4{ 0 };
777 double m_tdelta0 {0}, m_tdeltaf {0};
778
779 mx::sigproc::circularBufferIndex<double, cbIndexT> m_piTimes;
780
781 mx::sigproc::circularBufferIndex<double, cbIndexT> m_satSem;
782
783 mx::sigproc::circularBufferIndex<double, cbIndexT> m_actProc;
784
785 mx::sigproc::circularBufferIndex<double, cbIndexT> m_actCom;
786
787 mx::sigproc::circularBufferIndex<double, cbIndexT> m_satUp;
788
789 mx::sigproc::circularBufferIndex<double, cbIndexT> m_deltaUp;
790
791
792 std::vector<double> m_piTimesD;
793 std::vector<double> m_satSemD;
794 std::vector<double> m_actProcD;
795 std::vector<double> m_actComD;
796 std::vector<double> m_satUpD;
797 std::vector<double> m_deltaUpD;
798
799 // clang-format off
800 #endif // clang-format on
801
802 private:
803 derivedT &derived()
804 {
805 return *static_cast<derivedT *>( this );
806 }
807};
808
809template <class derivedT, typename realT>
811{
812 for( auto &mi : m_channels )
813 {
814 if( mi != nullptr )
815 {
816 delete mi;
817 }
818 }
819}
820
821template <class derivedT, typename realT>
822const std::string &dm<derivedT, realT>::calibPath() const
823{
824 return m_calibPath;
825}
826
827template <class derivedT, typename realT>
828const std::string &dm<derivedT, realT>::flatPath() const
829{
830 return m_flatPath;
831}
832
833template <class derivedT, typename realT>
834const std::string &dm<derivedT, realT>::testPath() const
835{
836 return m_testPath;
837}
838
839template <class derivedT, typename realT>
840const std::string &dm<derivedT, realT>::flatDefault() const
841{
842 return m_flatDefault;
843}
844
845template <class derivedT, typename realT>
846const std::string &dm<derivedT, realT>::testDefault() const
847{
848 return m_testDefault;
849}
850
851template <class derivedT, typename realT>
852const std::string &dm<derivedT, realT>::shmimFlat() const
853{
854 return m_shmimFlat;
855}
856
857template <class derivedT, typename realT>
858const std::string &dm<derivedT, realT>::shmimTest() const
859{
860 return m_shmimTest;
861}
862
863template <class derivedT, typename realT>
864const std::string &dm<derivedT, realT>::shmimSat() const
865{
866 return m_shmimSat;
867}
868
869template <class derivedT, typename realT>
870const std::string &dm<derivedT, realT>::shmimSatPerc() const
871{
872 return m_shmimSatPerc;
873}
874
875template <class derivedT, typename realT>
876const std::string &dm<derivedT, realT>::shmimShape() const
877{
878 return m_shmimShape;
879}
880
881template <class derivedT, typename realT>
882const std::string &dm<derivedT, realT>::shmimDelta() const
883{
884 return m_shmimDelta;
885}
886
887template <class derivedT, typename realT>
889{
890 return m_dmWidth;
891}
892
893template <class derivedT, typename realT>
895{
896 return m_dmHeight;
897}
898
899template <class derivedT, typename realT>
901{
902 return m_dmDataType;
903}
904
905template <class derivedT, typename realT>
907{
908 return m_percThreshold;
909}
910
911template <class derivedT, typename realT>
913{
914 return m_intervalSatThreshold;
915}
916
917template <class derivedT, typename realT>
919{
920 return m_intervalSatCountThreshold;
921}
922
923template <class derivedT, typename realT>
924const std::vector<std::string> &dm<derivedT, realT>::satTriggerDevice() const
925{
926 return m_satTriggerDevice;
927}
928
929template <class derivedT, typename realT>
930const std::vector<std::string> &dm<derivedT, realT>::satTriggerProperty() const
931{
932 return m_satTriggerProperty;
933}
934
935template <class derivedT, typename realT>
936const std::string &dm<derivedT, realT>::calibRelDir() const
937{
938 return m_calibRelDir;
939}
940
941template <class derivedT, typename realT>
943{
944 return m_numChannels;
945}
946
947template <class derivedT, typename realT>
948const mx::improc::eigenImage<uint8_t> &dm<derivedT, realT>::instSatMap() const
949{
950 return m_instSatMap;
951}
952
953template <class derivedT, typename realT>
954const mx::improc::eigenImage<uint16_t> &dm<derivedT, realT>::accumSatMap() const
955{
956 return m_accumSatMap;
957}
958
959template <class derivedT, typename realT>
960const mx::improc::eigenImage<float> &dm<derivedT, realT>::satPercMap() const
961{
962 return m_satPercMap;
963}
964
965template <class derivedT, typename realT>
966const std::vector<std::string> &dm<derivedT, realT>::deltaChannels() const
967{
968 return m_deltaChannels;
969}
970
971template <class derivedT, typename realT>
972const std::vector<size_t> &dm<derivedT, realT>::notDeltas() const
973{
974 return m_notDeltas;
975}
976
977template <class derivedT, typename realT>
978const mx::improc::eigenImage<float> &dm<derivedT, realT>::totalFlat() const
979{
980 return m_totalFlat;
981}
982
983template <class derivedT, typename realT>
984int dm<derivedT, realT>::setupConfig( mx::app::appConfigurator &config )
985{
986 config.add( "dm.calibPath",
987 "",
988 "dm.calibPath",
989 argType::Required,
990 "dm",
991 "calibPath",
992 false,
993 "string",
994 "The path to calibration files, relative to the MagAO-X calibration path." );
995
996 config.add( "dm.flatPath",
997 "",
998 "dm.flatPath",
999 argType::Required,
1000 "dm",
1001 "flatPath",
1002 false,
1003 "string",
1004 "The path to flat files. Default is the calibration path." );
1005
1006 config.add( "dm.flatDefault",
1007 "",
1008 "dm.flatDefault",
1009 argType::Required,
1010 "dm",
1011 "flatDefault",
1012 false,
1013 "string",
1014 "The default flat file (path and extension are not required)." );
1015
1016 config.add( "dm.testPath",
1017 "",
1018 "dm.testPath",
1019 argType::Required,
1020 "dm",
1021 "testPath",
1022 false,
1023 "string",
1024 "The path to test files. Default is the calibration path plus /tests." );
1025
1026 config.add( "dm.testDefault",
1027 "",
1028 "dm.testDefault",
1029 argType::Required,
1030 "dm",
1031 "testDefault",
1032 false,
1033 "string",
1034 "The default test file (path and extension are not required)." );
1035
1036 config.add( "dm.actMaskPath",
1037 "",
1038 "dm.actMaskPath",
1039 argType::Required,
1040 "dm",
1041 "actMaskPath",
1042 false,
1043 "string",
1044 "The path to the actuator mask for this DM, relative to the calib path." );
1045
1046 // Overriding the shmimMonitor setup so that these all go in the dm section
1047 // Otherwise, would call shmimMonitor<dm<derivedT,realT>>::setupConfig();
1048 ///\todo shmimMonitor now has configSection so this isn't necessary.
1049 config.add( "dm.threadPrio",
1050 "",
1051 "dm.threadPrio",
1052 argType::Required,
1053 "dm",
1054 "threadPrio",
1055 false,
1056 "int",
1057 "The real-time priority of the dm control thread." );
1058
1059 config.add( "dm.cpuset",
1060 "",
1061 "dm.cpuset",
1062 argType::Required,
1063 "dm",
1064 "cpuset",
1065 false,
1066 "int",
1067 "The cpuset for the dm control thread." );
1068
1069 config.add( "dm.shmimName",
1070 "",
1071 "dm.shmimName",
1072 argType::Required,
1073 "dm",
1074 "shmimName",
1075 false,
1076 "string",
1077 "The name of the ImageStreamIO shared memory image to monitor for DM comands. Will be used as "
1078 "/tmp/<shmimName>.im.shm." );
1079
1080 // end of shmimmonitor overrides
1081
1082 config.add( "dm.shmimFlat",
1083 "",
1084 "dm.shmimFlat",
1085 argType::Required,
1086 "dm",
1087 "shmimFlat",
1088 false,
1089 "string",
1090 "The name of the ImageStreamIO shared memory image to write the flat command to. Default is shmimName "
1091 "with 00 apended (i.e. dm00disp -> dm00disp00). " );
1092
1093 config.add( "dm.shmimTest",
1094 "",
1095 "dm.shmimTest",
1096 argType::Required,
1097 "dm",
1098 "shmimTest",
1099 false,
1100 "string",
1101 "The name of the ImageStreamIO shared memory image to write the test command to. Default is shmimName "
1102 "with 01 apended (i.e. dm00disp -> dm00disp01). " );
1103
1104 config.add( "dm.shmimSat",
1105 "",
1106 "dm.shmimSat",
1107 argType::Required,
1108 "dm",
1109 "shmimSat",
1110 false,
1111 "string",
1112 "The name of the ImageStreamIO shared memory image to write the saturation map to. Default is "
1113 "shmimName with SA apended (i.e. dm00disp -> dm00dispSA). This is created." );
1114
1115 config.add( "dm.shmimSatPerc",
1116 "",
1117 "dm.shmimSatPerc",
1118 argType::Required,
1119 "dm",
1120 "shmimSatPerc",
1121 false,
1122 "string",
1123 "The name of the ImageStreamIO shared memory image to write the saturation percentage map to. Default "
1124 "is shmimName with SP apended (i.e. dm00disp -> dm00dispSP). This is created." );
1125
1126 config.add( "dm.satAvgInt",
1127 "",
1128 "dm.satAvgInt",
1129 argType::Required,
1130 "dm",
1131 "satAvgInt",
1132 false,
1133 "int",
1134 "The interval in milliseconds over which saturation "
1135 "is accumulated before updating. Default is 100 ms." );
1136
1137 config.add( "dm.satThreadPrio",
1138 "",
1139 "dm.satThreadPrio",
1140 argType::Required,
1141 "dm",
1142 "satThreadPrio",
1143 false,
1144 "int",
1145 "The priority for the saturation thread. "
1146 "Usually ok to be 0." );
1147
1148 config.add( "dm.shmimShape",
1149 "",
1150 "dm.shmimShape",
1151 argType::Required,
1152 "dm",
1153 "shmimShape",
1154 false,
1155 "string",
1156 "The name of the ImageStreamIO shared memory image to write the desaturated shape to. Default is "
1157 "shmimName with _shape apended (i.e. dm00disp -> dm00disp_shape). This is created." );
1158
1159 config.add( "dm.shmimDelta",
1160 "",
1161 "dm.shmimDelta",
1162 argType::Required,
1163 "dm",
1164 "shmimDelta",
1165 false,
1166 "string",
1167 "The name of the ImageStreamIO shared memory image to write the "
1168 "desaturated delta-shape to. Default is "
1169 "shmimName with _delta apended (i.e. dm00disp -> dm00disp_delta). This is created." );
1170
1171 config.add( "dm.deltaChannels",
1172 "",
1173 "dm.deltaChannels",
1174 argType::Required,
1175 "dm",
1176 "deltaChannels",
1177 false,
1178 "vector<string>",
1179 "The names of the DM channels which are delta commands to be excluded from the total flat." );
1180
1181 config.add( "dm.width",
1182 "",
1183 "dm.width",
1184 argType::Required,
1185 "dm",
1186 "width",
1187 false,
1188 "string",
1189 "The width of the DM in actuators." );
1190
1191 config.add( "dm.height",
1192 "",
1193 "dm.height",
1194 argType::Required,
1195 "dm",
1196 "height",
1197 false,
1198 "string",
1199 "The height of the DM in actuators." );
1200
1201 config.add( "dm.percThreshold",
1202 "",
1203 "dm.percThreshold",
1204 argType::Required,
1205 "dm",
1206 "percThreshold",
1207 false,
1208 "float",
1209 "Threshold on percentage of frames an actuator is saturated over an interval. Default is 0.98." );
1210
1211 config.add( "dm.intervalSatThreshold",
1212 "",
1213 "dm.intervalSatThreshold",
1214 argType::Required,
1215 "dm",
1216 "intervalSatThreshold",
1217 false,
1218 "float",
1219 "Threshold on percentage of actuators which exceed percThreshold in an interval. Default is 0.5." );
1220
1221 config.add( "dm.intervalSatCountThreshold",
1222 "",
1223 "dm.intervalSatCountThreshold",
1224 argType::Required,
1225 "dm",
1226 "intervalSatCountThreshold",
1227 false,
1228 "float",
1229 "Threshold on number of consecutive intervals the intervalSatThreshold is exceeded. Default is 10." );
1230
1231 config.add( "dm.satTriggerDevice",
1232 "",
1233 "dm.satTriggerDevice",
1234 argType::Required,
1235 "dm",
1236 "satTriggerDevice",
1237 false,
1238 "vector<string>",
1239 "Device(s) with a toggle switch to toggle on saturation trigger." );
1240
1241 config.add( "dm.satTriggerProperty",
1242 "",
1243 "dm.satTriggerProperty",
1244 argType::Required,
1245 "dm",
1246 "satTriggerProperty",
1247 false,
1248 "vector<string>",
1249 "Property with a toggle switch to toggle on saturation trigger, one per entry in satTriggerDevice." );
1250
1251 return 0;
1252}
1253
1254template <class derivedT, typename realT>
1255int dm<derivedT, realT>::loadConfig( mx::app::appConfigurator &config )
1256{
1257
1258 m_calibPath = derived().m_calibDir + "/" + m_calibRelDir;
1259 config( m_calibPath, "dm.calibPath" );
1260
1261 // setup flats
1262 m_flatPath = m_calibPath + "/flats";
1263 config( m_flatPath, "dm.flatPath" );
1264
1265 config( m_flatDefault, "dm.flatDefault" );
1266 if( m_flatDefault != "" )
1267 {
1268 m_flatDefault = mx::ioutils::pathStem( m_flatDefault ); // strip off path and extension if provided.
1269 m_flatCurrent = "default";
1270 }
1271
1272 // setup tests
1273 m_testPath = m_calibPath + "/tests";
1274 config( m_testPath, "dm.testPath" );
1275
1276 config( m_testDefault, "dm.testDefault" );
1277 if( m_testDefault != "" )
1278 {
1279 m_testDefault = mx::ioutils::pathStem( m_testDefault ); // strip off path and extension if provided.
1280 m_testCurrent = "default";
1281 }
1282
1283 config( m_actMaskPath, "dm.actMaskPath" );
1284
1285 // Overriding the shmimMonitor setup so that these all go in the dm section
1286 // Otherwise, would call shmimMonitor<dm<derivedT,realT>>::loadConfig(config);
1287 config( derived().m_smThreadPrio, "dm.threadPrio" );
1288 config( derived().m_smCpuset, "dm.cpuset" );
1289
1290 config( derived().m_shmimName, "dm.shmimName" );
1291
1292 derived().m_getExistingFirst = true;
1293 // end of shmimmonitor overrides
1294
1295 if( derived().m_shmimName != "" )
1296 {
1297 m_shmimFlat = derived().m_shmimName + "00";
1298 config( m_shmimFlat, "dm.shmimFlat" );
1299
1300 m_shmimTest = derived().m_shmimName + "02";
1301 config( m_shmimTest, "dm.shmimTest" );
1302
1303 m_shmimSat = derived().m_shmimName + "ST";
1304 config( m_shmimSat, "dm.shmimSat" );
1305
1306 m_shmimSatPerc = derived().m_shmimName + "SP";
1307 config( m_shmimSatPerc, "dm.shmimSatPerc" );
1308
1309 config( m_satAvgInt, "dm.satAvgInt" );
1310
1311 config( m_satThreadPrio, "dm.satSatThreadPrio" );
1312
1313 m_shmimShape = derived().m_shmimName + "_shape";
1314 config( m_shmimShape, "dm.shmimShape" );
1315
1316 m_shmimDelta = derived().m_shmimName + "_delta";
1317 config( m_shmimDelta, "dm.shmimDelta" );
1318
1319 m_shmimDiff = derived().m_shmimName + "_diff";
1320 config( m_shmimDiff, "dm.shmimDiff" );
1321
1322 config( m_deltaChannels, "dm.deltaChannels" );
1323 }
1324 else
1325 {
1326 // Avoid unused error
1327 config.isSet( "dm.shmimFlat" );
1328 config.isSet( "dm.shmimTest" );
1329 config.isSet( "dm.shmimSat" );
1330 config.isSet( "dm.shmimSatPerc" );
1331 config.isSet( "dm.satAvgInt" );
1332 config.isSet( "dm.shmimShape" );
1333 config.isSet( "dm.shmimDelta" );
1334 config.isSet( "dm.deltaChannels" );
1335 }
1336
1337 config( m_dmWidth, "dm.width" );
1338 config( m_dmHeight, "dm.height" );
1339
1340 config( m_percThreshold, "dm.percThreshold" );
1341 config( m_intervalSatThreshold, "dm.intervalSatThreshold" );
1342 config( m_intervalSatCountThreshold, "dm.intervalSatCountThreshold" );
1343 config( m_satTriggerDevice, "dm.satTriggerDevice" );
1344 config( m_satTriggerProperty, "dm.satTriggerProperty" );
1345
1346 if( m_dmWidth > 0 && m_dmHeight > 0 )
1347 {
1348 try
1349 {
1350 m_actMask.create( derived().m_shmimName + "_actmask", m_dmWidth, m_dmHeight );
1351 }
1352 catch( const std::exception &e )
1353 {
1354 derivedT::template log<text_log>( std::format( "exception caught creating actuator mask: "
1355 "{}: {}",
1356 derived().m_shmimName + "_actmask",
1357 e.what(),
1358 logPrio::LOG_ERROR ) );
1359 return -1;
1360 }
1361
1362 if( m_actMaskPath != "" )
1363 {
1364 mx::improc::eigenImage<realT> actMask;
1365
1366 mx::fits::fitsFile<realT> ff;
1367
1368 mx::error_t errc = ff.read( actMask, m_calibPath + '/' + m_actMaskPath );
1369
1370 if( errc != mx::error_t::noerror )
1371 {
1372 derivedT::template log<text_log>( std::format( "error reading actuator mask file {}: "
1373 "{} ({})",
1374 m_calibPath + '/' + m_actMaskPath,
1375 mx::errorMessage( errc ),
1376 mx::errorName( errc ) ),
1377 logPrio::LOG_ERROR );
1378 return -1;
1379 }
1380
1381 if( actMask.rows() != m_dmWidth || actMask.cols() != m_dmHeight )
1382 {
1383 derivedT::template log<text_log>( std::format( "actuaor mask {}x{} is not same size as flag {}x{}",
1384 actMask.rows(),
1385 actMask.cols(),
1386 m_dmWidth,
1387 m_dmHeight ),
1388 logPrio::LOG_ERROR );
1389
1390 return -1;
1391 }
1392
1393 m_actMask = actMask;
1394 }
1395 else
1396 {
1397 m_actMask().setConstant( 1.0 );
1398 }
1399 }
1400
1401 return 0;
1402}
1403
1404template <class derivedT, typename realT>
1406{
1407 if( m_dmDataType == 0 )
1408 {
1409 derivedT::template log<software_error>( { "unsupported DM data type" } );
1410 return -1;
1411 }
1412
1413 //-----------------
1414 // Get the flats
1415 checkFlats();
1416
1417 // Register the test shmim INDI property
1418 m_indiP_flatShmim = pcf::IndiProperty( pcf::IndiProperty::Text );
1419 m_indiP_flatShmim.setDevice( derived().configName() );
1420 m_indiP_flatShmim.setName( "flat_shmim" );
1421 m_indiP_flatShmim.setPerm( pcf::IndiProperty::ReadOnly );
1422 m_indiP_flatShmim.setState( pcf::IndiProperty::Idle );
1423 m_indiP_flatShmim.add( pcf::IndiElement( "channel" ) );
1424 m_indiP_flatShmim["channel"] = m_shmimFlat;
1425
1426 if( derived().registerIndiPropertyReadOnly( m_indiP_flatShmim ) < 0 )
1427 {
1428#ifndef DM_TEST_NOLOG
1429 derivedT::template log<software_error>( { "" } );
1430#endif
1431 return -1;
1432 }
1433
1434 // Register the setFlat INDI property
1435 derived().createStandardIndiToggleSw( m_indiP_setFlat, "flat_set" );
1436 if( derived().registerIndiPropertyNew( m_indiP_setFlat, st_newCallBack_setFlat ) < 0 )
1437 {
1438#ifndef DM_TEST_NOLOG
1439 derivedT::template log<software_error>( { "" } );
1440#endif
1441 return -1;
1442 }
1443
1444 //-----------------
1445 // Get the tests
1446 checkTests();
1447
1448 // Register the test shmim INDI property
1449 m_indiP_testShmim = pcf::IndiProperty( pcf::IndiProperty::Text );
1450 m_indiP_testShmim.setDevice( derived().configName() );
1451 m_indiP_testShmim.setName( "test_shmim" );
1452 m_indiP_testShmim.setPerm( pcf::IndiProperty::ReadOnly );
1453 m_indiP_testShmim.setState( pcf::IndiProperty::Idle );
1454 m_indiP_testShmim.add( pcf::IndiElement( "channel" ) );
1455 m_indiP_testShmim["channel"] = m_shmimTest;
1456 derived().createStandardIndiToggleSw( m_indiP_setTest, "test_shmim" );
1457 if( derived().registerIndiPropertyReadOnly( m_indiP_testShmim ) < 0 )
1458 {
1459#ifndef DM_TEST_NOLOG
1460 derivedT::template log<software_error>( { "" } );
1461#endif
1462 return -1;
1463 }
1464
1465 // Register the setTest INDI property
1466 derived().createStandardIndiToggleSw( m_indiP_setTest, "test_set" );
1467 if( derived().registerIndiPropertyNew( m_indiP_setTest, st_newCallBack_setTest ) < 0 )
1468 {
1469#ifndef DM_TEST_NOLOG
1470 derivedT::template log<software_error>( { "" } );
1471#endif
1472 return -1;
1473 }
1474
1475 // Register the init INDI property
1476 derived().createStandardIndiRequestSw( m_indiP_init, "initDM" );
1477 if( derived().registerIndiPropertyNew( m_indiP_init, st_newCallBack_init ) < 0 )
1478 {
1479 // clang-format off
1480 #ifndef DM_TEST_NOLOG
1481 derivedT::template log<software_error>( {""} );
1482 #endif
1483 // clang-format on
1484
1485 return -1;
1486 }
1487
1488 // Register the zero INDI property
1489 derived().createStandardIndiRequestSw( m_indiP_zero, "zeroDM" );
1490
1491 if( derived().registerIndiPropertyNew( m_indiP_zero, st_newCallBack_zero ) < 0 )
1492 {
1493 // clang-format off
1494 #ifndef DM_TEST_NOLOG
1495 derivedT::template log<software_error>( {""} );
1496 #endif
1497 // clang-format on
1498
1499 return -1;
1500 }
1501
1502 // Register the release INDI property
1503 derived().createStandardIndiRequestSw( m_indiP_release, "releaseDM" );
1504 if( derived().registerIndiPropertyNew( m_indiP_release, st_newCallBack_release ) < 0 )
1505 {
1506 return derivedT::template log<software_error, -1>( { "" } );
1507 }
1508
1509 derived().createStandardIndiRequestSw( m_indiP_zeroAll, "zeroAll" );
1510 if( derived().registerIndiPropertyNew( m_indiP_zeroAll, st_newCallBack_zeroAll ) < 0 )
1511 {
1512#ifndef DM_TEST_NOLOG
1513 derivedT::template log<software_error>( { "" } );
1514#endif
1515 return -1;
1516 }
1517
1518 if( m_flatDefault != "" )
1519 {
1520 loadFlat( "default" );
1521 }
1522
1523 if( m_testDefault != "" )
1524 {
1525 loadTest( "default" );
1526 }
1527
1528 if( sem_init( &m_satSemaphore, 0, 0 ) < 0 )
1529 {
1530 return derivedT::template log<software_critical, -1>( { errno, 0, "Initializing sat semaphore" } );
1531 }
1532
1533 if( derived().threadStart( m_satThread,
1534 m_satThreadInit,
1535 m_satThreadID,
1536 m_satThreadProp,
1537 m_satThreadPrio,
1538 "",
1539 "saturation",
1540 this,
1541 satThreadStart ) < 0 )
1542 {
1543 derivedT::template log<software_error, -1>( { "" } );
1544 return -1;
1545 }
1546
1547 return 0;
1548}
1549
1550template <class derivedT, typename realT>
1552{
1553 // do a join check to see if other threads have exited.
1554 if( pthread_tryjoin_np( m_satThread.native_handle(), 0 ) == 0 )
1555 {
1556 derivedT::template log<software_error>( { "saturation thread has exited" } );
1557
1558 return -1;
1559 }
1560
1561 checkFlats();
1562
1563 checkTests();
1564
1565 if( m_intervalSatTrip )
1566 {
1567 intervalSatTrip();
1568 m_intervalSatTrip = false;
1569 }
1570
1571#ifdef XWC_DMTIMINGS
1572 static uint64_t lastMono = 0;
1573
1574 if( m_piTimes.size() >= m_piTimes.maxEntries() && m_piTimes.maxEntries() > 0 && m_piTimes.mono() != lastMono )
1575 {
1576 cbIndexT refEntry = m_piTimes.earliest();
1577
1578 m_piTimesD.resize( m_piTimes.maxEntries() );
1579 m_satSemD.resize( m_satSem.maxEntries() );
1580 m_actProcD.resize( m_actProc.maxEntries() );
1581 m_actComD.resize( m_actCom.maxEntries() );
1582 m_satUpD.resize( m_satUp.maxEntries() );
1583 m_deltaUpD.resize( m_deltaUp.maxEntries() );
1584
1585 for( size_t n = 0; n < m_piTimesD.size(); ++n )
1586 {
1587 m_piTimesD[n] = m_piTimes.at( refEntry, n );
1588 m_satSemD[n] = m_satSem.at( refEntry, n );
1589 m_actProcD[n] = m_actProc.at( refEntry, n );
1590 m_actComD[n] = m_actCom.at( refEntry, n );
1591 m_satUpD[n] = m_satUp.at( refEntry, n );
1592 m_deltaUpD[n] = m_deltaUp.at( refEntry, n );
1593 }
1594
1595 std::cerr << "Act. Process: " << mx::math::vectorMean( m_actProcD ) << " +/- "
1596 << sqrt( mx::math::vectorVariance( m_actProcD ) ) << "\n";
1597 std::cerr << "Act. Command: " << mx::math::vectorMean( m_actComD ) << " +/- "
1598 << sqrt( mx::math::vectorVariance( m_actComD ) ) << "\n";
1599 std::cerr << "Sat. Update: " << mx::math::vectorMean( m_satUpD ) << " +/- "
1600 << sqrt( mx::math::vectorVariance( m_satUpD ) ) << "\n";
1601 std::cerr << "Delta Update: " << mx::math::vectorMean( m_deltaUpD ) << " +/- "
1602 << sqrt( mx::math::vectorVariance( m_deltaUpD ) ) << "\n";
1603 std::cerr << "Tot. CommandDM: " << mx::math::vectorMean( m_piTimesD ) << " +/- "
1604 << sqrt( mx::math::vectorVariance( m_piTimesD ) ) << "\n";
1605 std::cerr << "Sat. Semaphore: " << mx::math::vectorMean( m_satSemD ) << " +/- "
1606 << sqrt( mx::math::vectorVariance( m_satSemD ) ) << "\n";
1607 std::cerr << "\n";
1608
1609 lastMono = m_piTimes.mono();
1610 }
1611#endif // XWC_DMTIMINGS
1612
1613 return 0;
1614}
1615
1616template <class derivedT, typename realT>
1618{
1619 if( m_satThread.joinable() )
1620 {
1621 pthread_kill( m_satThread.native_handle(), SIGUSR1 );
1622 try
1623 {
1624 m_satThread.join(); // this will throw if it was already joined
1625 }
1626 catch( ... )
1627 {
1628 }
1629 }
1630
1631 return 0;
1632}
1633
1634template <class derivedT, typename realT>
1636{
1637 baseReleaseDM();
1638
1639 return 0;
1640}
1641
1642template <class derivedT, typename realT>
1644{
1645 checkFlats();
1646 checkTests();
1647
1648 return 0;
1649}
1650
1651template <class derivedT, typename realT>
1653{
1654 std::string milkShmimDir = mx::sys::getEnv( "MILK_SHM_DIR" );
1655 if( milkShmimDir == "" )
1656 {
1657 milkShmimDir = "/milk/shm";
1658 }
1659
1660 std::vector<std::string> dmlist;
1661 mx::error_t errc = mx::ioutils::getFileNames( dmlist, milkShmimDir, derived().m_shmimName, ".im", ".shm" );
1662
1663 mx_error_check_rv( errc, -1 );
1664
1665 if( dmlist.size() == 0 )
1666 {
1667 derivedT::template log<software_error>( { "no dm channels found for " + derived().m_shmimName } );
1668
1669 return -1;
1670 }
1671
1672 m_numChannels = -1;
1673 for( size_t n = 0; n < dmlist.size(); ++n )
1674 {
1675 char nstr[16];
1676 snprintf( nstr, sizeof( nstr ), "%02d.im.shm", (int)n );
1677 std::string tgt = derived().m_shmimName;
1678 tgt += nstr;
1679
1680 for( size_t m = 0; m < dmlist.size(); ++m )
1681 {
1682 if( dmlist[m].find( tgt ) != std::string::npos )
1683 {
1684 if( (int)n > m_numChannels )
1685 {
1686 m_numChannels = n;
1687 }
1688 }
1689 }
1690 }
1691
1692 ++m_numChannels;
1693
1694 derivedT::template log<text_log>( {std::format("Found {} chanels for {} ", m_numChannels, derived().m_shmimName )} );
1695
1696 m_channels.resize( m_numChannels, nullptr );
1697
1698 m_notDeltas.clear();
1699 m_deltas.clear();
1700
1701 for( size_t n = 0; n < m_channels.size(); ++n )
1702 {
1703 std::string sname = std::format( "{}{:02}", derived().m_shmimName, n );
1704
1705 try
1706 {
1707 m_channels[n] = new mx::improc::milkImage<realT>( sname ); // this opens the channel stream
1708 }
1709 catch( const std::exception &e )
1710 {
1711 derivedT::template log<software_error>( { "exception opening " + sname + ": " + e.what() } );
1712 }
1713
1714 std::cerr << "looking for " << sname << '\n';
1715 auto res = std::find( m_deltaChannels.begin(), m_deltaChannels.end(), sname );
1716 if( res == m_deltaChannels.end() )
1717 {
1718 std::cerr << " not a delta\n";
1719 m_notDeltas.push_back( n );
1720 }
1721 else
1722 {
1723 std::cerr << " is a delta\n";
1724 m_deltas.push_back( n );
1725 }
1726 }
1727
1728 std::cerr << "not deltas: ";
1729 for( size_t n = 0; n < m_notDeltas.size(); ++n )
1730 {
1731 std::cerr << m_notDeltas[n] << ' ';
1732 }
1733 std::cerr << '\n';
1734
1735 std::cerr << "deltas: ";
1736 for( size_t n = 0; n < m_deltas.size(); ++n )
1737 {
1738 std::cerr << m_deltas[n] << ' ';
1739 }
1740 std::cerr << '\n';
1741
1742 return 0;
1743}
1744
1745template <class derivedT, typename realT>
1747{
1748 static_cast<void>( sp ); // be unused
1749
1750 int err = 0;
1751
1752 if( derived().m_width != m_dmWidth )
1753 {
1754 derivedT::template log<software_critical>( { "shmim width does not match configured DM width" } );
1755 ++err;
1756 }
1757
1758 if( derived().m_height != m_dmHeight )
1759 {
1760 derivedT::template log<software_critical>( { "shmim height does not match configured DM height" } );
1761 ++err;
1762 }
1763
1764 if( derived().m_dataType != m_dmDataType )
1765 {
1766 derivedT::template log<software_critical>( { "shmim data type does not match configured DM data type" } );
1767 ++err;
1768 }
1769
1770 if( err )
1771 {
1772 return -1;
1773 }
1774
1775 m_instSatMap.resize( m_dmWidth, m_dmHeight );
1776 m_instSatMap.setZero();
1777
1778 m_accumSatMap.resize( m_dmWidth, m_dmHeight );
1779 m_accumSatMap.setZero();
1780
1781 m_satPercMap.resize( m_dmWidth, m_dmHeight );
1782 m_satPercMap.setZero();
1783
1784 if( findDMChannels() < 0 )
1785 {
1786 derivedT::template log<software_critical>( { "error finding DM channels" } );
1787
1788 return -1;
1789 }
1790
1791 try
1792 {
1793 m_outputShape.create( m_shmimShape, m_dmWidth, m_dmHeight );
1794 m_outputShape().setZero();
1795 }
1796 catch( const std::exception &e )
1797 {
1798 return derivedT::template log<software_error, -1>(
1799 { std::string( "creating output shape shmim: " ) + e.what() } );
1800 }
1801
1802 try
1803 {
1804 m_outputDelta.create( m_shmimDelta, m_dmWidth, m_dmHeight );
1805 m_outputDelta().setZero();
1806 }
1807 catch( const std::exception &e )
1808 {
1809 return derivedT::template log<software_error, -1>(
1810 { std::string( "creating output delta shmim: " ) + e.what() } );
1811 }
1812
1813 try
1814 {
1815 m_outputDiff.create( m_shmimDiff, m_dmWidth, m_dmHeight );
1816 m_outputDiff().setZero();
1817 }
1818 catch( const std::exception &e )
1819 {
1820 return derivedT::template log<software_error, -1>(
1821 { std::string( "creating output diff shmim: " ) + e.what() } );
1822 }
1823
1824 m_totalFlat.resize( m_dmWidth, m_dmHeight );
1825 m_totalFlat.setZero();
1826
1827 m_totalDelta.resize( m_dmWidth, m_dmHeight );
1828 m_totalDelta.setZero();
1829
1830 // clang-format off
1831 #ifdef XWC_DMTIMINGS
1832 m_piTimes.maxEntries( 2000 );
1833 m_satSem.maxEntries( 2000 );
1834 m_actProc.maxEntries( 2000 );
1835 m_actCom.maxEntries( 2000 );
1836 m_satUp.maxEntries( 2000 );
1837 m_deltaUp.maxEntries( 2000 );
1838 #endif // clang-format on
1839
1840 return 0;
1841}
1842
1843template <class derivedT, typename realT>
1844int dm<derivedT, realT>::processImage( void *curr_src, const dev::shmimT &sp )
1845{
1846 static_cast<void>( sp ); // be unused
1847
1848 // clang-format off
1849 #ifdef XWC_DMTIMINGS
1850 m_t0 = mx::sys::get_curr_time();
1851 #endif // clang-format on
1852
1853 int rv = derived().commandDM( curr_src );
1854
1855 if( rv < 0 )
1856 {
1857 derivedT::template log<software_critical>( { errno, rv, "Error from commandDM" } );
1858 return rv;
1859 }
1860
1861 // clang-format off
1862 #ifdef XWC_DMTIMINGS
1863 m_tdelta0 = mx::sys::get_curr_time();
1864 #endif // clang-format on
1865
1866 if( m_deltaChannels.size() > 0 )
1867 {
1868 rv = makeDelta();
1869
1870 if( rv < 0 )
1871 {
1872 derivedT::template log<software_critical>( { errno, rv, "Error from makeDelta" } );
1873 return rv;
1874 }
1875 }
1876
1877 // clang-format off
1878 #ifdef XWC_DMTIMINGS
1879 m_tdeltaf = mx::sys::get_curr_time();
1880
1881 m_tf = m_tdeltaf;
1882 #endif // clang-format on
1883
1884 // clang-format off
1885 #ifdef XWC_DMTIMINGS
1886 m_tsat0 = mx::sys::get_curr_time();
1887 #endif // clang-format on
1888
1889 // Tell the sat thread to get going
1890 if( sem_post( &m_satSemaphore ) < 0 )
1891 {
1892 derivedT::template log<software_critical>( { errno, 0, "Error posting to semaphore" } );
1893 return -1;
1894 }
1895
1896 // clang-format off
1897 #ifdef XWC_DMTIMINGS // clang-format on
1898
1899 m_tsatf = mx::sys::get_curr_time();
1900
1901 // Update the latency circ. buffs
1902 if( m_piTimes.maxEntries() > 0 )
1903 {
1904 m_piTimes.nextEntry( m_tf - m_t0 );
1905 m_satSem.nextEntry( m_tsatf - m_tsat0 );
1906 m_actProc.nextEntry( m_tact1 - m_tact0 );
1907 m_actCom.nextEntry( m_tact2 - m_tact1 );
1908 m_satUp.nextEntry( m_tact4 - m_tact3 );
1909 m_deltaUp.nextEntry( m_tdeltaf - m_tdelta0 );
1910 }
1911
1912 // clang-format off
1913 #endif // clang-format on
1914
1915 return rv;
1916}
1917
1918template <class derivedT, typename realT>
1920{
1921 if( derived().state() != stateCodes::NOTHOMED )
1922 {
1923 derivedT::template log<software_error>( { errno, "DM is not ready to be initialized" } );
1924 derived().state( stateCodes::ERROR );
1925 return -1;
1926 }
1927
1928 derived().state( stateCodes::HOMING );
1929
1930 int rv;
1931 if( ( rv = derived().initDM() ) < 0 )
1932 {
1933 derivedT::template log<software_critical>( { errno, rv, "Error from initDM" } );
1934 derived().state( stateCodes::ERROR );
1935 return rv;
1936 }
1937
1938 return 0;
1939}
1940
1941template <class derivedT, typename realT>
1943{
1944 if( derived().state() != stateCodes::POWEROFF )
1945 {
1946 derived().state( stateCodes::NOTHOMED );
1947 }
1948
1949 int rv;
1950 if( ( rv = derived().releaseDM() ) < 0 )
1951 {
1952 derivedT::template log<software_critical>( { errno, rv, "Error from releaseDM" } );
1953 derived().state( stateCodes::ERROR );
1954 return rv;
1955 }
1956
1957 if( ( rv = zeroAll( true ) ) < 0 )
1958 {
1959 derivedT::template log<software_error>( { errno, rv, "Error from zeroAll" } );
1960 derived().state( stateCodes::ERROR );
1961 return rv;
1962 }
1963
1964 return 0;
1965}
1966
1967template <class derivedT, typename realT>
1969{
1970 std::vector<std::string> tfs;
1971 mx::error_t errc = mx::ioutils::getFileNames( tfs, m_flatPath, "", "", ".fits" );
1972
1973 mx_error_check_rv( errc, -1 );
1974
1975 // First remove default, b/c we always add it and don't want to include it in timestamp selected ones
1976 for( size_t n = 0; n < tfs.size(); ++n )
1977 {
1978 if( mx::ioutils::pathStem( tfs[n] ) == "default" )
1979 {
1980 tfs.erase( tfs.begin() + n );
1981 --n;
1982 }
1983 }
1984
1985 unsigned m_nFlatFiles = 5;
1986
1987 // Here we keep only the m_nFlatFiles most recent files
1988 if( tfs.size() >= m_nFlatFiles )
1989 {
1990 std::vector<std::filesystem::file_time_type> wtimes( tfs.size() );
1991
1992 for( size_t n = 0; n < wtimes.size(); ++n )
1993 {
1994 wtimes[n] = std::filesystem::last_write_time( tfs[n] );
1995 }
1996
1997 std::sort( wtimes.begin(), wtimes.end() );
1998
1999 std::filesystem::file_time_type tn = wtimes[wtimes.size() - m_nFlatFiles];
2000
2001 for( size_t n = 0; n < tfs.size(); ++n )
2002 {
2003 std::filesystem::file_time_type lmt = std::filesystem::last_write_time( tfs[n] );
2004 if( lmt < tn )
2005 {
2006 tfs.erase( tfs.begin() + n );
2007 --n;
2008 }
2009 }
2010 }
2011
2012 for( auto it = m_flatCommands.begin(); it != m_flatCommands.end(); ++it )
2013 {
2014 it->second = "";
2015 }
2016
2017 bool changed = false;
2018 for( size_t n = 0; n < tfs.size(); ++n )
2019 {
2020 auto ir =
2021 m_flatCommands.insert( std::pair<std::string, std::string>( mx::ioutils::pathStem( tfs[n] ), tfs[n] ) );
2022 if( ir.second == true )
2023 changed = true;
2024 else
2025 ir.first->second = tfs[n];
2026 }
2027
2028 for( auto it = m_flatCommands.begin(); it != m_flatCommands.end(); ++it )
2029 {
2030 if( it->second == "" )
2031 {
2032 changed = true;
2033 // Erase the current iterator safely, even if the first one.
2034 auto itdel = it;
2035 ++it;
2036 m_flatCommands.erase( itdel );
2037 --it;
2038 };
2039 }
2040
2041 if( changed )
2042 {
2043 if( derived().m_indiDriver )
2044 {
2045 derived().m_indiDriver->sendDelProperty( m_indiP_flats );
2046 derived().m_indiNewCallBacks.erase( m_indiP_flats.createUniqueKey() );
2047 }
2048
2049 m_indiP_flats = pcf::IndiProperty( pcf::IndiProperty::Switch );
2050 m_indiP_flats.setDevice( derived().configName() );
2051 m_indiP_flats.setName( "flat" );
2052 m_indiP_flats.setPerm( pcf::IndiProperty::ReadWrite );
2053 m_indiP_flats.setState( pcf::IndiProperty::Idle );
2054 m_indiP_flats.setRule( pcf::IndiProperty::OneOfMany );
2055
2056 // Add the toggle element initialized to Off
2057 for( auto it = m_flatCommands.begin(); it != m_flatCommands.end(); ++it )
2058 {
2059 if( it->first == m_flatCurrent || m_flatCurrent == "" )
2060 {
2061 m_indiP_flats.add( pcf::IndiElement( it->first, pcf::IndiElement::On ) );
2062 m_flatCurrent = it->first; // handles the case m_flatCurrent == "" b/c it was not set in config
2063 }
2064 else
2065 {
2066 m_indiP_flats.add( pcf::IndiElement( it->first, pcf::IndiElement::Off ) );
2067 }
2068 }
2069
2070 if( m_flatDefault != "" )
2071 {
2072 if( m_flatCurrent == "default" )
2073 {
2074 m_indiP_flats.add( pcf::IndiElement( "default", pcf::IndiElement::On ) );
2075 }
2076 else
2077 {
2078 m_indiP_flats.add( pcf::IndiElement( "default", pcf::IndiElement::Off ) );
2079 }
2080 }
2081
2082 if( derived().registerIndiPropertyNew( m_indiP_flats, st_newCallBack_flats ) < 0 )
2083 {
2084 // clang-format off
2085 #ifndef DM_TEST_NOLOG
2086 derivedT::template log<software_error>( {""} );
2087 #endif
2088 // clang-format on
2089
2090 return -1;
2091 }
2092
2093 if( derived().m_indiDriver )
2094 {
2095 derived().m_indiDriver->sendDefProperty( m_indiP_flats );
2096 }
2097 }
2098
2099 return 0;
2100}
2101
2102template <class derivedT, typename realT>
2103int dm<derivedT, realT>::loadFlat( const std::string &intarget )
2104{
2105 std::string target = intarget;
2106
2107 std::string targetPath;
2108
2109 if( target == "default" )
2110 {
2111 target = m_flatDefault;
2112 targetPath = m_flatPath + "/" + m_flatDefault + ".fits";
2113 }
2114 else
2115 {
2116 try
2117 {
2118 targetPath = m_flatCommands.at( target );
2119 }
2120 catch( ... )
2121 {
2122 derivedT::template log<text_log>( "flat file " + target + " not found", logPrio::LOG_ERROR );
2123 return -1;
2124 }
2125 }
2126
2127 m_flatLoaded = false;
2128
2129 // load into memory.
2130 mx::fits::fitsFile<realT> ff;
2131
2132 mx::error_t errc = ff.read( m_flatCommand, targetPath );
2133
2134 if( errc != mx::error_t::noerror )
2135 {
2136 derivedT::template log<text_log>( std::format( "error reading flat file {}: "
2137 "{} ({})",
2138 targetPath,
2139 mx::errorMessage( errc ),
2140 mx::errorName( errc ) ),
2141 logPrio::LOG_ERROR );
2142 return -1;
2143 }
2144
2145 if( m_actMask.rows() != m_flatCommand.rows() || m_actMask.cols() != m_flatCommand.cols() )
2146 {
2147 derivedT::template log<text_log>( std::format( "actuaor mask {}x{} is not same size as flag {}x{}",
2148 m_actMask.rows(),
2149 m_actMask.cols(),
2150 m_flatCommand.rows(),
2151 m_flatCommand.cols() ),
2152 logPrio::LOG_ERROR );
2153
2154 return -1;
2155 }
2156
2157 m_flatCommand *= m_actMask();
2158
2159 derivedT::template log<text_log>( "loaded flat file " + targetPath );
2160 m_flatLoaded = true;
2161
2162 m_flatCurrent = intarget;
2163
2164 if( m_indiP_flats.find( "default" ) )
2165 {
2166 if( m_flatCurrent == "default" )
2167 {
2168 m_indiP_flats["default"] = pcf::IndiElement::On;
2169 }
2170 else
2171 {
2172 m_indiP_flats["default"] = pcf::IndiElement::Off;
2173 }
2174 }
2175
2176 for( auto i = m_flatCommands.begin(); i != m_flatCommands.end(); ++i )
2177 {
2178 if( !m_indiP_flats.find( i->first ) )
2179 {
2180 continue;
2181 }
2182
2183 if( i->first == m_flatCurrent )
2184 {
2185 m_indiP_flats[i->first] = pcf::IndiElement::On;
2186 }
2187 else
2188 {
2189 m_indiP_flats[i->first] = pcf::IndiElement::Off;
2190 }
2191 }
2192
2193 if( derived().m_indiDriver )
2194 {
2195 derived().m_indiDriver->sendSetProperty( m_indiP_flats );
2196 }
2197
2198 if( m_flatSet )
2199 {
2200 setFlat();
2201 }
2202
2203 return 0;
2204}
2205
2206template <class derivedT, typename realT>
2208{
2209 if( m_shmimFlat == "" )
2210 {
2211 return 0;
2212 }
2213
2214 if( !( derived().state() == stateCodes::READY || derived().state() == stateCodes::OPERATING ) )
2215 {
2216 derivedT::template log<text_log>( "can not set flat unless DM is READY or OPERATING", logPrio::LOG_WARNING );
2217 return -1;
2218 }
2219
2220 if( ImageStreamIO_openIm( &m_flatImageStream, m_shmimFlat.c_str() ) != 0 )
2221 {
2222 derivedT::template log<text_log>( "could not connect to flat channel " + m_shmimFlat, logPrio::LOG_WARNING );
2223 return -1;
2224 }
2225
2226 if( m_flatImageStream.md[0].size[0] != m_dmWidth )
2227 {
2228 ImageStreamIO_closeIm( &m_flatImageStream );
2229 derivedT::template log<text_log>( "width mismatch between " + m_shmimFlat + " and configured DM",
2230 logPrio::LOG_ERROR );
2231 return -1;
2232 }
2233
2234 if( m_flatImageStream.md[0].size[1] != m_dmHeight )
2235 {
2236 ImageStreamIO_closeIm( &m_flatImageStream );
2237 derivedT::template log<text_log>( "height mismatch between " + m_shmimFlat + " and configured DM",
2238 logPrio::LOG_ERROR );
2239 return -1;
2240 }
2241
2242 if( !m_flatLoaded )
2243 {
2244 bool flatSet = m_flatSet;
2245 m_flatSet = false; // make sure we don't loop
2246
2247 if( loadFlat( m_flatCurrent ) < 0 )
2248 {
2249 derivedT::template log<text_log>( "error loading flat " + m_flatCurrent, logPrio::LOG_ERROR );
2250 }
2251 m_flatSet = flatSet;
2252 }
2253
2254 if( !m_flatLoaded )
2255 {
2256 ImageStreamIO_closeIm( &m_flatImageStream );
2257 derivedT::template log<text_log>( "no flat loaded", logPrio::LOG_ERROR );
2258 return -1;
2259 }
2260
2261 if( m_flatCommand.rows() != m_dmWidth )
2262 {
2263 ImageStreamIO_closeIm( &m_flatImageStream );
2264 derivedT::template log<text_log>( "width mismatch between flat file and configured DM", logPrio::LOG_ERROR );
2265 return -1;
2266 }
2267
2268 if( m_flatCommand.cols() != m_dmHeight )
2269 {
2270 ImageStreamIO_closeIm( &m_flatImageStream );
2271 derivedT::template log<text_log>( "height mismatch between flat file and configured DM", logPrio::LOG_ERROR );
2272 return -1;
2273 }
2274
2275 m_flatImageStream.md->write = 1;
2276
2277 ///\todo we are assuming that dmXXcomYY is not a cube. This might be true, but we should add cnt1 handling here
2278 /// anyway. With bounds checks b/c not everyone handles cnt1 properly.
2279 // Copy
2280 memcpy( m_flatImageStream.array.raw, m_flatCommand.data(), m_dmWidth * m_dmHeight * sizeof( realT ) );
2281
2282 // Set the time of last write
2283 clock_gettime( CLOCK_REALTIME, &m_flatImageStream.md->writetime );
2284
2285 // Set the image acquisition timestamp
2286 m_flatImageStream.md->atime = m_flatImageStream.md->writetime;
2287
2288 m_flatImageStream.md->cnt0++;
2289 m_flatImageStream.md->write = 0;
2290
2291 // Post the semaphores
2292 ImageStreamIO_sempost( &m_flatImageStream, -1 );
2293
2294 m_flatSet = true;
2295
2296 ImageStreamIO_closeIm( &m_flatImageStream );
2297
2298 derived().state( stateCodes::OPERATING );
2299
2300 if( !update )
2301 {
2302 derived().updateSwitchIfChanged( m_indiP_setFlat, "toggle", pcf::IndiElement::On, pcf::IndiProperty::Busy );
2303
2304 derivedT::template log<text_log>( "flat set" );
2305 }
2306
2307 return 0;
2308}
2309
2310template <class derivedT, typename realT>
2312{
2313 if( m_shmimFlat == "" )
2314 {
2315 return 0;
2316 }
2317
2318 if( !( derived().state() == stateCodes::READY || derived().state() == stateCodes::OPERATING ) )
2319 {
2320 derivedT::template log<text_log>( "can not zero flat unless DM is READY or OPERATING", logPrio::LOG_WARNING );
2321 return -1;
2322 }
2323
2324 if( ImageStreamIO_openIm( &m_flatImageStream, m_shmimFlat.c_str() ) != 0 )
2325 {
2326 derivedT::template log<text_log>( "could not connect to flat channel " + m_shmimFlat, logPrio::LOG_WARNING );
2327 return -1;
2328 }
2329
2330 if( m_flatImageStream.md[0].size[0] != m_dmWidth )
2331 {
2332 ImageStreamIO_closeIm( &m_flatImageStream );
2333 derivedT::template log<text_log>( "width mismatch between " + m_shmimFlat + " and configured DM",
2334 logPrio::LOG_ERROR );
2335 return -1;
2336 }
2337
2338 if( m_flatImageStream.md[0].size[1] != m_dmHeight )
2339 {
2340 ImageStreamIO_closeIm( &m_flatImageStream );
2341 derivedT::template log<text_log>( "height mismatch between " + m_shmimFlat + " and configured DM",
2342 logPrio::LOG_ERROR );
2343 return -1;
2344 }
2345
2346 m_flatImageStream.md->write = 1;
2347
2348 ///\todo we are assuming that dmXXcomYY is not a cube. This might be true, but we should add cnt1 handling here
2349 /// anyway. With bounds checks b/c not everyone handles cnt1 properly.
2350 // Zero
2351 memset( m_flatImageStream.array.raw, 0, m_dmWidth * m_dmHeight * sizeof( realT ) );
2352
2353 // Set the time of last write
2354 clock_gettime( CLOCK_REALTIME, &m_flatImageStream.md->writetime );
2355
2356 // Set the image acquisition timestamp
2357 m_flatImageStream.md->atime = m_flatImageStream.md->writetime;
2358
2359 m_flatImageStream.md->cnt0++;
2360 m_flatImageStream.md->write = 0;
2361 ImageStreamIO_sempost( &m_flatImageStream, -1 );
2362
2363 m_flatSet = false;
2364
2365 // Post the semaphore
2366 ImageStreamIO_closeIm( &m_flatImageStream );
2367
2368 derived().updateSwitchIfChanged( m_indiP_setFlat, "toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle );
2369
2370 derivedT::template log<text_log>( "flat zeroed" );
2371
2372 if( derived().zeroDM() < 0 )
2373 {
2374 derivedT::template log<software_error>( { "error from zeroDM" } );
2375 }
2376
2377 if( clearSat() < 0 )
2378 {
2379 derivedT::template log<software_error>( { "error from clearSat" } );
2380 }
2381 derived().state( stateCodes::READY );
2382
2383 return 0;
2384}
2385
2386template <class derivedT, typename realT>
2388{
2389 std::vector<std::string> tfs;
2390 mx::error_t errc = mx::ioutils::getFileNames( tfs, m_testPath, "", "", ".fits" );
2391
2392 mx_error_check_rv( errc, -1 );
2393
2394 for( auto it = m_testCommands.begin(); it != m_testCommands.end(); ++it )
2395 {
2396 it->second = "";
2397 }
2398
2399 bool changed = false;
2400 for( size_t n = 0; n < tfs.size(); ++n )
2401 {
2402 auto ir =
2403 m_testCommands.insert( std::pair<std::string, std::string>( mx::ioutils::pathStem( tfs[n] ), tfs[n] ) );
2404 if( ir.second == true )
2405 changed = true;
2406 else
2407 ir.first->second = tfs[n];
2408 }
2409
2410 for( auto it = m_testCommands.begin(); it != m_testCommands.end(); ++it )
2411 {
2412 if( it->second == "" )
2413 {
2414 changed = true;
2415 // Erase the current iterator safely, even if the first one.
2416 auto itdel = it;
2417 ++it;
2418 m_testCommands.erase( itdel );
2419 --it;
2420 };
2421 }
2422
2423 if( changed )
2424 {
2425 if( derived().m_indiDriver )
2426 {
2427 derived().m_indiDriver->sendDelProperty( m_indiP_tests );
2428 derived().m_indiNewCallBacks.erase( m_indiP_tests.createUniqueKey() );
2429 }
2430
2431 m_indiP_tests = pcf::IndiProperty( pcf::IndiProperty::Switch );
2432 m_indiP_tests.setDevice( derived().configName() );
2433 m_indiP_tests.setName( "test" );
2434 m_indiP_tests.setPerm( pcf::IndiProperty::ReadWrite );
2435 m_indiP_tests.setState( pcf::IndiProperty::Idle );
2436 m_indiP_tests.setRule( pcf::IndiProperty::OneOfMany );
2437
2438 // Add the toggle element initialized to Off
2439 for( auto it = m_testCommands.begin(); it != m_testCommands.end(); ++it )
2440 {
2441 if( it->first == m_testCurrent || m_testCurrent == "" )
2442 {
2443 m_indiP_tests.add( pcf::IndiElement( it->first, pcf::IndiElement::On ) );
2444 m_testCurrent = it->first; // Handles the case when m_testCurrent=="" b/c it was not set in config
2445 }
2446 else
2447 {
2448 m_indiP_tests.add( pcf::IndiElement( it->first, pcf::IndiElement::Off ) );
2449 }
2450 }
2451
2452 if( m_testDefault != "" )
2453 {
2454 if( m_testCurrent == "default" )
2455 {
2456 m_indiP_tests.add( pcf::IndiElement( "default", pcf::IndiElement::On ) );
2457 }
2458 else
2459 {
2460 m_indiP_tests.add( pcf::IndiElement( "default", pcf::IndiElement::Off ) );
2461 }
2462 }
2463
2464 if( derived().registerIndiPropertyNew( m_indiP_tests, st_newCallBack_tests ) < 0 )
2465 {
2466#ifndef DM_TEST_NOLOG
2467 derivedT::template log<software_error>( { "" } );
2468#endif
2469 return -1;
2470 }
2471
2472 if( derived().m_indiDriver )
2473 {
2474 derived().m_indiDriver->sendDefProperty( m_indiP_tests );
2475 }
2476 }
2477
2478 return 0;
2479}
2480
2481template <class derivedT, typename realT>
2482int dm<derivedT, realT>::loadTest( const std::string &intarget )
2483{
2484 std::string target = intarget; // store this for later to resolve default next:
2485
2486 if( target == "default" )
2487 {
2488 target = m_testDefault;
2489 }
2490
2491 std::string targetPath;
2492
2493 try
2494 {
2495 targetPath = m_testCommands.at( target );
2496 }
2497 catch( ... )
2498 {
2499 derivedT::template log<text_log>( "test file " + target + " not found", logPrio::LOG_ERROR );
2500 return -1;
2501 }
2502
2503 m_testLoaded = false;
2504 // load into memory.
2505 mx::fits::fitsFile<realT> ff;
2506 mx::error_t errc = ff.read( m_testCommand, targetPath );
2507 if( errc != mx::error_t::noerror )
2508 {
2509 derivedT::template log<text_log>( std::format( "error reading test file {}: "
2510 "{} ({})",
2511 targetPath,
2512 mx::errorMessage( errc ),
2513 mx::errorName( errc ) ),
2514 logPrio::LOG_ERROR );
2515 return -1;
2516 }
2517
2518 derivedT::template log<text_log>( "loaded test file " + targetPath );
2519 m_testLoaded = true;
2520
2521 m_testCurrent = intarget;
2522
2523 if( m_indiP_tests.find( "default" ) )
2524 {
2525 if( m_testCurrent == "default" )
2526 {
2527 m_indiP_tests["default"] = pcf::IndiElement::On;
2528 }
2529 else
2530 {
2531 m_indiP_tests["default"] = pcf::IndiElement::Off;
2532 }
2533 }
2534
2535 for( auto i = m_testCommands.begin(); i != m_testCommands.end(); ++i )
2536 {
2537 if( !m_indiP_tests.find( i->first ) )
2538 {
2539 continue;
2540 }
2541
2542 if( i->first == m_testCurrent )
2543 {
2544 m_indiP_tests[i->first] = pcf::IndiElement::On;
2545 }
2546 else
2547 {
2548 m_indiP_tests[i->first] = pcf::IndiElement::Off;
2549 }
2550 }
2551
2552 if( derived().m_indiDriver )
2553 derived().m_indiDriver->sendSetProperty( m_indiP_tests );
2554
2555 if( m_testSet )
2556 setTest();
2557
2558 return 0;
2559}
2560
2561template <class derivedT, typename realT>
2563{
2564
2565 if( m_shmimTest == "" )
2566 return 0;
2567
2568 if( ImageStreamIO_openIm( &m_testImageStream, m_shmimTest.c_str() ) != 0 )
2569 {
2570 derivedT::template log<text_log>( "could not connect to test channel " + m_shmimTest, logPrio::LOG_WARNING );
2571 return -1;
2572 }
2573
2574 if( m_testImageStream.md->size[0] != m_dmWidth )
2575 {
2576 ImageStreamIO_closeIm( &m_testImageStream );
2577 derivedT::template log<text_log>( "width mismatch between " + m_shmimTest + " and configured DM",
2578 logPrio::LOG_ERROR );
2579 return -1;
2580 }
2581
2582 if( m_testImageStream.md->size[1] != m_dmHeight )
2583 {
2584 ImageStreamIO_closeIm( &m_testImageStream );
2585 derivedT::template log<text_log>( "height mismatch between " + m_shmimTest + " and configured DM",
2586 logPrio::LOG_ERROR );
2587 return -1;
2588 }
2589
2590 if( !m_testLoaded )
2591 {
2592 bool testSet = m_testSet;
2593 m_testSet = false; // make sure we don't loop
2594
2595 if( loadTest( m_testCurrent ) < 0 )
2596 {
2597 derivedT::template log<text_log>( "error loading test " + m_testCurrent, logPrio::LOG_ERROR );
2598 }
2599 m_testSet = testSet;
2600 }
2601
2602 if( !m_testLoaded )
2603 {
2604 ImageStreamIO_closeIm( &m_testImageStream );
2605 derivedT::template log<text_log>( "no test loaded", logPrio::LOG_ERROR );
2606 return -1;
2607 }
2608
2609 if( m_testCommand.rows() != m_dmWidth )
2610 {
2611 ImageStreamIO_closeIm( &m_testImageStream );
2612 derivedT::template log<text_log>( "width mismatch between test file and configured DM", logPrio::LOG_ERROR );
2613 return -1;
2614 }
2615
2616 if( m_testCommand.cols() != m_dmHeight )
2617 {
2618 ImageStreamIO_closeIm( &m_testImageStream );
2619 derivedT::template log<text_log>( "height mismatch between test file and configured DM", logPrio::LOG_ERROR );
2620 return -1;
2621 }
2622
2623 m_testImageStream.md->write = 1;
2624
2625 ///\todo we are assuming that dmXXcomYY is not a cube. This might be true, but we should add cnt1 handling here
2626 /// anyway. With bounds checks b/c not everyone handles cnt1 properly.
2627 // Copy
2628 memcpy( m_testImageStream.array.raw, m_testCommand.data(), m_dmWidth * m_dmHeight * sizeof( realT ) );
2629
2630 // Set the time of last write
2631 clock_gettime( CLOCK_REALTIME, &m_testImageStream.md->writetime );
2632
2633 // Set the image acquisition timestamp
2634 m_testImageStream.md->atime = m_testImageStream.md->writetime;
2635
2636 m_testImageStream.md->cnt0++;
2637 m_testImageStream.md->write = 0;
2638 ImageStreamIO_sempost( &m_testImageStream, -1 );
2639
2640 m_testSet = true;
2641
2642 // Post the semaphore
2643 ImageStreamIO_closeIm( &m_testImageStream );
2644
2645 derived().updateSwitchIfChanged( m_indiP_setTest, "toggle", pcf::IndiElement::On, pcf::IndiProperty::Busy );
2646
2647 derivedT::template log<text_log>( "test set" );
2648
2649 return 0;
2650}
2651
2652template <class derivedT, typename realT>
2654{
2655 if( m_shmimTest == "" )
2656 return 0;
2657
2658 if( ImageStreamIO_openIm( &m_testImageStream, m_shmimTest.c_str() ) != 0 )
2659 {
2660 derivedT::template log<text_log>( "could not connect to test channel " + m_shmimTest, logPrio::LOG_WARNING );
2661 return -1;
2662 }
2663
2664 if( m_testImageStream.md[0].size[0] != m_dmWidth )
2665 {
2666 ImageStreamIO_closeIm( &m_testImageStream );
2667 derivedT::template log<text_log>( "width mismatch between " + m_shmimTest + " and configured DM",
2668 logPrio::LOG_ERROR );
2669 return -1;
2670 }
2671
2672 if( m_testImageStream.md[0].size[1] != m_dmHeight )
2673 {
2674 ImageStreamIO_closeIm( &m_testImageStream );
2675 derivedT::template log<text_log>( "height mismatch between " + m_shmimTest + " and configured DM",
2676 logPrio::LOG_ERROR );
2677 return -1;
2678 }
2679
2680 m_testImageStream.md->write = 1;
2681
2682 ///\todo we are assuming that dmXXcomYY is not a cube. This might be true, but we should add cnt1 handling here
2683 /// anyway. With bounds checks b/c not everyone handles cnt1 properly.
2684 // Zero
2685 memset( m_testImageStream.array.raw, 0, m_dmWidth * m_dmHeight * sizeof( realT ) );
2686
2687 // Set the time of last write
2688 clock_gettime( CLOCK_REALTIME, &m_testImageStream.md->writetime );
2689
2690 // Set the image acquisition timestamp
2691 m_testImageStream.md->atime = m_testImageStream.md->writetime;
2692
2693 m_testImageStream.md->cnt0++;
2694 m_testImageStream.md->write = 0;
2695
2696 // Post the semaphore
2697 ImageStreamIO_sempost( &m_testImageStream, -1 );
2698
2699 m_testSet = false;
2700
2701 ImageStreamIO_closeIm( &m_testImageStream );
2702
2703 derived().updateSwitchIfChanged( m_indiP_setTest, "toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle );
2704
2705 derivedT::template log<text_log>( "test zeroed" );
2706
2707 return 0;
2708}
2709
2710template <class derivedT, typename realT>
2712{
2713 if( derived().m_shmimName == "" )
2714 {
2715 return 0;
2716 }
2717
2718 IMAGE imageStream;
2719
2720 for( int n = 0; n < m_numChannels; ++n )
2721 {
2722 char nstr[16];
2723 snprintf( nstr, sizeof( nstr ), "%02d", n );
2724 std::string shmimN = derived().m_shmimName + nstr;
2725
2726 if( ImageStreamIO_openIm( &imageStream, shmimN.c_str() ) != 0 )
2727 {
2728 derivedT::template log<text_log>( "could not connect to channel " + shmimN, logPrio::LOG_WARNING );
2729 continue;
2730 }
2731
2732 if( imageStream.md->size[0] != m_dmWidth )
2733 {
2734 ImageStreamIO_closeIm( &imageStream );
2735 derivedT::template log<text_log>( "width mismatch between " + shmimN + " and configured DM",
2736 logPrio::LOG_ERROR );
2737 derived().updateSwitchIfChanged( m_indiP_zeroAll, "request", pcf::IndiElement::Off, INDI_IDLE );
2738 return -1;
2739 }
2740
2741 if( imageStream.md->size[1] != m_dmHeight )
2742 {
2743 ImageStreamIO_closeIm( &imageStream );
2744 derivedT::template log<text_log>( "height mismatch between " + shmimN + " and configured DM",
2745 logPrio::LOG_ERROR );
2746 derived().updateSwitchIfChanged( m_indiP_zeroAll, "request", pcf::IndiElement::Off, INDI_IDLE );
2747 return -1;
2748 }
2749
2750 imageStream.md->write = 1;
2751 memset( imageStream.array.raw, 0, m_dmWidth * m_dmHeight * sizeof( realT ) );
2752
2753 clock_gettime( CLOCK_REALTIME, &imageStream.md->writetime );
2754
2755 // Set the image acquisition timestamp
2756 imageStream.md->atime = imageStream.md->writetime;
2757
2758 imageStream.md->cnt0++;
2759 imageStream.md->write = 0;
2760
2761 // Raise the semaphore on last one.
2762 if( n == m_numChannels - 1 && !nosem )
2763 {
2764 ImageStreamIO_sempost( &imageStream, -1 );
2765 }
2766
2767 ImageStreamIO_closeIm( &imageStream );
2768 }
2769
2770 derivedT::template log<text_log>( "all channels zeroed", logPrio::LOG_NOTICE );
2771
2772 derived().updateSwitchIfChanged( m_indiP_zeroAll, "request", pcf::IndiElement::Off, INDI_IDLE );
2773
2774 // Also cleanup flat and test
2775 m_flatSet = false;
2776 derived().updateSwitchIfChanged( m_indiP_setFlat, "toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle );
2777 if( derived().state() == stateCodes::OPERATING )
2778 {
2779 derived().state( stateCodes::READY );
2780 }
2781
2782 // Also cleanup flat and test
2783 m_testSet = false;
2784 derived().updateSwitchIfChanged( m_indiP_setTest, "toggle", pcf::IndiElement::Off, pcf::IndiProperty::Idle );
2785
2786 int rv;
2787 if( ( rv = clearSat() ) < 0 )
2788 {
2789 derivedT::template log<software_error>( { errno, rv, "Error from clearSat" } );
2790 return rv;
2791 }
2792
2793 return 0;
2794}
2795
2796template <class derivedT, typename realT>
2798{
2799 if( m_notDeltas.size() == 0 )
2800 {
2801 return 0;
2802 }
2803
2804 m_totalFlat = ( *m_channels[m_notDeltas[0]] )();
2805
2806 for( size_t n = 1; n < m_notDeltas.size(); ++n )
2807 {
2808 m_totalFlat += ( *m_channels[m_notDeltas[n]] )();
2809 }
2810
2811 m_outputDelta = m_outputShape() - m_totalFlat; // this posts and everything
2812
2813 m_totalDelta = ( *m_channels[m_deltas[0]] )();
2814
2815 for( size_t n = 1; n < m_deltas.size(); ++n )
2816 {
2817 m_totalDelta += ( *m_channels[m_deltas[n]] )();
2818 }
2819
2820 m_outputDiff = m_totalDelta - m_outputDelta();
2821
2822 return 0;
2823}
2824
2825template <class derivedT, typename realT>
2827{
2828 if( m_shmimSat == "" || m_dmWidth == 0 || m_dmHeight == 0 )
2829 {
2830 return 0;
2831 }
2832
2833 IMAGE imageStream;
2834
2835 std::vector<std::string> sats = { m_shmimSat, m_shmimSatPerc };
2836
2837 for( size_t n = 0; n < sats.size(); ++n )
2838 {
2839 std::string shmimN = sats[n];
2840
2841 if( ImageStreamIO_openIm( &imageStream, shmimN.c_str() ) != 0 )
2842 {
2843 derivedT::template log<text_log>( "could not connect to sat map " + shmimN, logPrio::LOG_WARNING );
2844 return 0;
2845 }
2846
2847 if( imageStream.md->size[0] != m_dmWidth )
2848 {
2849 ImageStreamIO_closeIm( &imageStream );
2850 derivedT::template log<text_log>( "width mismatch between " + shmimN + " and configured DM",
2851 logPrio::LOG_ERROR );
2852 derived().updateSwitchIfChanged( m_indiP_zeroAll, "request", pcf::IndiElement::Off, INDI_IDLE );
2853 return -1;
2854 }
2855
2856 if( imageStream.md->size[1] != m_dmHeight )
2857 {
2858 ImageStreamIO_closeIm( &imageStream );
2859 derivedT::template log<text_log>( "height mismatch between " + shmimN + " and configured DM",
2860 logPrio::LOG_ERROR );
2861 derived().updateSwitchIfChanged( m_indiP_zeroAll, "request", pcf::IndiElement::Off, INDI_IDLE );
2862 return -1;
2863 }
2864
2865 imageStream.md->write = 1;
2866 memset( imageStream.array.raw, 0, m_dmWidth * m_dmHeight * ImageStreamIO_typesize( imageStream.md->datatype ) );
2867
2868 clock_gettime( CLOCK_REALTIME, &imageStream.md->writetime );
2869
2870 // Set the image acquisition timestamp
2871 imageStream.md->atime = imageStream.md->writetime;
2872
2873 imageStream.md->cnt0++;
2874 imageStream.md->write = 0;
2875 ImageStreamIO_sempost( &imageStream, -1 );
2876
2877 ImageStreamIO_closeIm( &imageStream );
2878 }
2879
2880 m_accumSatMap.setZero();
2881 m_instSatMap.setZero();
2882
2883 return 0;
2884}
2885
2886template <class derivedT, typename realT>
2888{
2889 d->satThreadExec();
2890}
2891
2892template <class derivedT, typename realT>
2894{
2895 // Get the thread PID immediately so the caller can return.
2896 m_satThreadID = syscall( SYS_gettid );
2897
2898 // Wait for the thread starter to finish initializing this thread.
2899 while( m_satThreadInit == true && derived().shutdown() == 0 )
2900 {
2901 sleep( 1 );
2902 }
2903
2904 if( derived().shutdown() )
2905 {
2906 return;
2907 }
2908
2909 uint32_t imsize[3] = { 0, 0, 0 };
2910
2911 // Check for allocation to have happened.
2912 while( ( m_shmimSat == "" || m_accumSatMap.rows() == 0 || m_accumSatMap.cols() == 0 ) && !derived().shutdown() )
2913 {
2914 sleep( 1 );
2915 }
2916
2917 if( derived().shutdown() )
2918 {
2919 return;
2920 }
2921
2922 imsize[0] = m_dmWidth;
2923 imsize[1] = m_dmHeight;
2924 imsize[2] = 1;
2925
2926 ImageStreamIO_createIm_gpu( &m_satImageStream,
2927 m_shmimSat.c_str(),
2928 3,
2929 imsize,
2931 -1,
2932 1,
2933 IMAGE_NB_SEMAPHORE,
2934 0,
2935 CIRCULAR_BUFFER | ZAXIS_TEMPORAL,
2936 0 );
2937 ImageStreamIO_createIm_gpu( &m_satPercImageStream,
2938 m_shmimSatPerc.c_str(),
2939 3,
2940 imsize,
2942 -1,
2943 1,
2944 IMAGE_NB_SEMAPHORE,
2945 0,
2946 CIRCULAR_BUFFER | ZAXIS_TEMPORAL,
2947 0 );
2948
2949 bool opened = true;
2950
2951 m_satImageStream.md->cnt1 = 0;
2952 m_satPercImageStream.md->cnt1 = 0;
2953
2954 // This is the working memory for making the 1/0 mask out of m_accumSatMap
2955 mx::improc::eigenImage<uint8_t> satmap( m_dmWidth, m_dmHeight );
2956
2957 int naccum = 0;
2958 double t_accumst = mx::sys::get_curr_time();
2959
2960 // This is the main image grabbing loop.
2961 while( !derived().shutdown() )
2962 {
2963 // Get timespec for sem_timedwait
2964 timespec ts;
2965 if( clock_gettime( CLOCK_REALTIME, &ts ) < 0 )
2966 {
2967 derivedT::template log<software_critical>( { errno, 0, "clock_gettime" } );
2968 return;
2969 }
2970 ts.tv_sec += 1;
2971
2972 // Wait on semaphore
2973 if( sem_timedwait( &m_satSemaphore, &ts ) == 0 )
2974 {
2975 // not a timeout -->accumulate
2976 for( int rr = 0; rr < m_instSatMap.rows(); ++rr )
2977 {
2978 for( int cc = 0; cc < m_instSatMap.cols(); ++cc )
2979 {
2980 m_accumSatMap( rr, cc ) += m_instSatMap( rr, cc );
2981 }
2982 }
2983 ++naccum;
2984
2985 // If less than avg int --> go back and wait again
2986 if( mx::sys::get_curr_time( ts ) - t_accumst < m_satAvgInt / 1000.0 )
2987 {
2988 continue;
2989 }
2990
2991 // If greater than avg int --> calc stats, write to streams.
2992 m_overSatAct = 0;
2993 for( int rr = 0; rr < m_instSatMap.rows(); ++rr )
2994 {
2995 for( int cc = 0; cc < m_instSatMap.cols(); ++cc )
2996 {
2997 m_satPercMap( rr, cc ) = m_accumSatMap( rr, cc ) / naccum;
2998 if( m_satPercMap( rr, cc ) >= m_percThreshold )
2999 {
3000 ++m_overSatAct;
3001 }
3002 satmap( rr, cc ) = ( m_accumSatMap( rr, cc ) > 0 ); // it's 1/0 map
3003 }
3004 }
3005
3006 // Check of the number of actuators saturated above the percent threshold is greater than the number
3007 // threshold if it is, increment the counter
3008 if( m_overSatAct / ( m_satPercMap.rows() * m_satPercMap.cols() * 0.75 ) > m_intervalSatThreshold )
3009 {
3010 ++m_intervalSatExceeds;
3011 }
3012 else
3013 {
3014 m_intervalSatExceeds = 0;
3015 }
3016
3017 // If enough consecutive intervals exceed the count threshold, we trigger
3018 if( m_intervalSatExceeds >= m_intervalSatCountThreshold )
3019 {
3020 m_intervalSatTrip = true;
3021 }
3022
3023 m_satImageStream.md->write = 1;
3024 m_satPercImageStream.md->write = 1;
3025
3026 memcpy( m_satImageStream.array.raw, satmap.data(), m_dmWidth * m_dmHeight * sizeof( uint8_t ) );
3027 memcpy( m_satPercImageStream.array.raw, m_satPercMap.data(), m_dmWidth * m_dmHeight * sizeof( float ) );
3028
3029 // Set the time of last write
3030 clock_gettime( CLOCK_REALTIME, &m_satImageStream.md->writetime );
3031 m_satPercImageStream.md->writetime = m_satImageStream.md->writetime;
3032
3033 // Set the image acquisition timestamp
3034 m_satImageStream.md->atime = m_satImageStream.md->writetime;
3035 m_satPercImageStream.md->atime = m_satPercImageStream.md->writetime;
3036
3037 // Update cnt1
3038 m_satImageStream.md->cnt1 = 0;
3039 m_satPercImageStream.md->cnt1 = 0;
3040
3041 // Update cnt0
3042 m_satImageStream.md->cnt0++;
3043 m_satPercImageStream.md->cnt0++;
3044
3045 m_satImageStream.writetimearray[0] = m_satImageStream.md->writetime;
3046 m_satImageStream.atimearray[0] = m_satImageStream.md->atime;
3047 m_satImageStream.cntarray[0] = m_satImageStream.md->cnt0;
3048
3049 m_satPercImageStream.writetimearray[0] = m_satPercImageStream.md->writetime;
3050 m_satPercImageStream.atimearray[0] = m_satPercImageStream.md->atime;
3051 m_satPercImageStream.cntarray[0] = m_satPercImageStream.md->cnt0;
3052
3053 // And post
3054 m_satImageStream.md->write = 0;
3055 ImageStreamIO_sempost( &m_satImageStream, -1 );
3056
3057 m_satPercImageStream.md->write = 0;
3058 ImageStreamIO_sempost( &m_satPercImageStream, -1 );
3059
3060 m_accumSatMap.setZero();
3061 naccum = 0;
3062 t_accumst = mx::sys::get_curr_time( ts );
3063 }
3064 else
3065 {
3066 // Check for why we timed out
3067 if( errno == EINTR )
3068 {
3069 break; // This indicates signal interrupted us, time to restart or shutdown, loop will exit normally if
3070 // flags set.
3071 }
3072
3073 // ETIMEDOUT just means we should wait more.
3074 // Otherwise, report an error.
3075 if( errno != ETIMEDOUT )
3076 {
3077 derivedT::template log<software_error>( { errno, "sem_timedwait" } );
3078 break;
3079 }
3080 }
3081 }
3082
3083 if( opened )
3084 {
3085 ImageStreamIO_destroyIm( &m_satImageStream );
3086
3087 ImageStreamIO_destroyIm( &m_satPercImageStream );
3088 }
3089}
3090
3091template <class derivedT, typename realT>
3093{
3094 if( m_satTriggerDevice.size() > 0 && m_satTriggerProperty.size() == m_satTriggerDevice.size() )
3095 {
3096 for( size_t n = 0; n < m_satTriggerDevice.size(); ++n )
3097 {
3098 // We just silently fail
3099 try
3100 {
3101 pcf::IndiProperty ipFreq( pcf::IndiProperty::Switch );
3102
3103 ipFreq.setDevice( m_satTriggerDevice[n] );
3104 ipFreq.setName( m_satTriggerProperty[n] );
3105 ipFreq.add( pcf::IndiElement( "toggle" ) );
3106 ipFreq["toggle"] = pcf::IndiElement::Off;
3107 derived().sendNewProperty( ipFreq );
3108
3109 derivedT::template log<text_log>( "DM saturation threshold exceeded. Loop opened.",
3110 logPrio::LOG_WARNING );
3111 }
3112 catch( ... )
3113 {
3114 }
3115 }
3116 }
3117}
3118
3119template <class derivedT, typename realT>
3121{
3122 if( !derived().m_indiDriver )
3123 {
3124 return 0;
3125 }
3126
3127 return 0;
3128}
3129
3130template <class derivedT, typename realT>
3131int dm<derivedT, realT>::st_newCallBack_init( void *app, const pcf::IndiProperty &ipRecv )
3132{
3133 return static_cast<derivedT *>( app )->newCallBack_init( ipRecv );
3134}
3135
3136template <class derivedT, typename realT>
3137int dm<derivedT, realT>::newCallBack_init( const pcf::IndiProperty &ipRecv )
3138{
3139 if( ipRecv.createUniqueKey() != m_indiP_init.createUniqueKey() )
3140 {
3141 return derivedT::template log<software_error, -1>( { "wrong INDI-P in callback" } );
3142 }
3143
3144 if( !ipRecv.find( "request" ) )
3145 {
3146 return 0;
3147 }
3148
3149 if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
3150 {
3151 int rv = baseInitDM();
3152 if( rv < 0 )
3153 {
3154 return derivedT::template log<software_error, -1>( { "error from initDM in INDI callback" } );
3155 }
3156 }
3157
3158 return 0;
3159}
3160
3161template <class derivedT, typename realT>
3162int dm<derivedT, realT>::st_newCallBack_zero( void *app, const pcf::IndiProperty &ipRecv )
3163{
3164 return static_cast<derivedT *>( app )->newCallBack_zero( ipRecv );
3165}
3166
3167template <class derivedT, typename realT>
3168int dm<derivedT, realT>::newCallBack_zero( const pcf::IndiProperty &ipRecv )
3169{
3170 if( ipRecv.createUniqueKey() != m_indiP_zero.createUniqueKey() )
3171 {
3172 return derivedT::template log<software_error, -1>( { "wrong INDI-P in callback" } );
3173 }
3174
3175 if( !ipRecv.find( "request" ) )
3176 return 0;
3177
3178 if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
3179 {
3180 return derived().zeroDM();
3181 }
3182 return 0;
3183}
3184
3185template <class derivedT, typename realT>
3186int dm<derivedT, realT>::st_newCallBack_release( void *app, const pcf::IndiProperty &ipRecv )
3187{
3188 return static_cast<derivedT *>( app )->newCallBack_release( ipRecv );
3189}
3190
3191template <class derivedT, typename realT>
3193{
3194 if( ipRecv.createUniqueKey() != m_indiP_release.createUniqueKey() )
3195 {
3196 return derivedT::template log<software_error, -1>( { "wrong INDI-P in callback" } );
3197 }
3198
3199 if( !ipRecv.find( "request" ) )
3200 return 0;
3201
3202 if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
3203 {
3204 return baseReleaseDM();
3205 }
3206 return 0;
3207}
3208
3209template <class derivedT, typename realT>
3210int dm<derivedT, realT>::st_newCallBack_flats( void *app, const pcf::IndiProperty &ipRecv )
3211{
3212 return static_cast<derivedT *>( app )->newCallBack_flats( ipRecv );
3213}
3214
3215template <class derivedT, typename realT>
3216int dm<derivedT, realT>::newCallBack_flats( const pcf::IndiProperty &ipRecv )
3217{
3218 if( ipRecv.createUniqueKey() != m_indiP_flats.createUniqueKey() )
3219 {
3220 derivedT::template log<software_error>( { "invalid indi property received" } );
3221 return -1;
3222 }
3223
3224 std::string newFlat;
3225
3226 if( ipRecv.find( "default" ) )
3227 {
3228 if( ipRecv["default"].getSwitchState() == pcf::IndiElement::On )
3229 {
3230 newFlat = "default";
3231 }
3232 }
3233
3234 // always do this to check for error:
3235 for( auto i = m_flatCommands.begin(); i != m_flatCommands.end(); ++i )
3236 {
3237 if( !ipRecv.find( i->first ) )
3238 continue;
3239
3240 if( ipRecv[i->first].getSwitchState() == pcf::IndiElement::On )
3241 {
3242 if( newFlat != "" )
3243 {
3244 derivedT::template log<text_log>( "More than one flat selected", logPrio::LOG_ERROR );
3245 return -1;
3246 }
3247
3248 newFlat = i->first;
3249 }
3250 }
3251
3252 if( newFlat == "" )
3253 {
3254 return 0;
3255 }
3256
3257 return loadFlat( newFlat );
3258}
3259
3260template <class derivedT, typename realT>
3261int dm<derivedT, realT>::st_newCallBack_setFlat( void *app, const pcf::IndiProperty &ipRecv )
3262{
3263 return static_cast<derivedT *>( app )->newCallBack_setFlat( ipRecv );
3264}
3265
3266template <class derivedT, typename realT>
3268{
3269 if( ipRecv.createUniqueKey() != m_indiP_setFlat.createUniqueKey() )
3270 {
3271 return derivedT::template log<software_error, -1>( { "wrong INDI-P in callback" } );
3272 }
3273
3274 if( !ipRecv.find( "toggle" ) )
3275 return 0;
3276
3277 if( ipRecv["toggle"] == pcf::IndiElement::On )
3278 {
3279 return setFlat();
3280 }
3281 else
3282 {
3283 return zeroFlat();
3284 }
3285}
3286
3287template <class derivedT, typename realT>
3288int dm<derivedT, realT>::st_newCallBack_tests( void *app, const pcf::IndiProperty &ipRecv )
3289{
3290 return static_cast<derivedT *>( app )->newCallBack_tests( ipRecv );
3291}
3292
3293template <class derivedT, typename realT>
3294int dm<derivedT, realT>::newCallBack_tests( const pcf::IndiProperty &ipRecv )
3295{
3296 if( ipRecv.createUniqueKey() != m_indiP_tests.createUniqueKey() )
3297 {
3298 derivedT::template log<software_error>( { "invalid indi property received" } );
3299 return -1;
3300 }
3301
3302 std::string newTest;
3303
3304 if( ipRecv.find( "default" ) )
3305 {
3306 if( ipRecv["default"].getSwitchState() == pcf::IndiElement::On )
3307 {
3308 newTest = "default";
3309 }
3310 }
3311
3312 // always do this to check for error:
3313 for( auto i = m_testCommands.begin(); i != m_testCommands.end(); ++i )
3314 {
3315 if( !ipRecv.find( i->first ) )
3316 continue;
3317
3318 if( ipRecv[i->first].getSwitchState() == pcf::IndiElement::On )
3319 {
3320 if( newTest != "" )
3321 {
3322 derivedT::template log<text_log>( "More than one test selected", logPrio::LOG_ERROR );
3323 return -1;
3324 }
3325
3326 newTest = i->first;
3327 }
3328 }
3329
3330 if( newTest == "" )
3331 {
3332 return 0;
3333 }
3334
3335 return loadTest( newTest );
3336}
3337
3338template <class derivedT, typename realT>
3339int dm<derivedT, realT>::st_newCallBack_setTest( void *app, const pcf::IndiProperty &ipRecv )
3340{
3341 return static_cast<derivedT *>( app )->newCallBack_setTest( ipRecv );
3342}
3343
3344template <class derivedT, typename realT>
3346{
3347 if( ipRecv.createUniqueKey() != m_indiP_setTest.createUniqueKey() )
3348 {
3349 return derivedT::template log<software_error, -1>( { "wrong INDI-P in callback" } );
3350 }
3351
3352 if( !ipRecv.find( "toggle" ) )
3353 return 0;
3354
3355 if( ipRecv["toggle"] == pcf::IndiElement::On )
3356 {
3357 return setTest();
3358 }
3359 else
3360 {
3361 return zeroTest();
3362 }
3363}
3364
3365template <class derivedT, typename realT>
3366int dm<derivedT, realT>::st_newCallBack_zeroAll( void *app, const pcf::IndiProperty &ipRecv )
3367{
3368 return static_cast<derivedT *>( app )->newCallBack_zeroAll( ipRecv );
3369}
3370
3371template <class derivedT, typename realT>
3373{
3374 if( ipRecv.createUniqueKey() != m_indiP_zeroAll.createUniqueKey() )
3375 {
3376 return derivedT::template log<software_error, -1>( { "wrong INDI-P in callback" } );
3377 }
3378
3379 if( !ipRecv.find( "request" ) )
3380 return 0;
3381
3382 if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
3383 {
3385 m_indiP_zeroAll, "request", pcf::IndiElement::On, derived().m_indiDriver, INDI_BUSY );
3386
3387 std::lock_guard<std::mutex> guard( derived().m_indiMutex );
3388 return zeroAll();
3389 }
3390 return 0;
3391}
3392
3393/// Call dmT::setupConfig with error checking for dm
3394/**
3395 * \param cfig the application configurator
3396 */
3397#define DM_SETUP_CONFIG( cfig ) \
3398 if( dmT::setupConfig( cfig ) < 0 ) \
3399 { \
3400 log<software_error>( { "Error from dmT::setupConfig" } ); \
3401 m_shutdown = true; \
3402 return; \
3403 }
3404
3405/// Call dmT::loadConfig with error checking for dm
3406/** This must be inside a function that returns int, e.g. the standard loadConfigImpl.
3407 * \param cfig the application configurator
3408 */
3409#define DM_LOAD_CONFIG( cfig ) \
3410 if( dmT::loadConfig( cfig ) < 0 ) \
3411 { \
3412 return log<software_error, -1>( { "Error from dmT::loadConfig" } ); \
3413 }
3414
3415/// Call shmimMonitorT::appStartup with error checking for dm
3416#define DM_APP_STARTUP \
3417 if( dmT::appStartup() < 0 ) \
3418 { \
3419 return log<software_error, -1>( { "Error from dmT::appStartup" } ); \
3420 }
3421
3422/// Call dmT::appLogic with error checking for dm
3423#define DM_APP_LOGIC \
3424 if( dmT::appLogic() < 0 ) \
3425 { \
3426 return log<software_error, -1>( { "Error from dmT::appLogic" } ); \
3427 }
3428
3429/// Call dmT::updateINDI with error checking for dm
3430#define DM_UPDATE_INDI \
3431 if( dmT::updateINDI() < 0 ) \
3432 { \
3433 return log<software_error, -1>( { "Error from dmT::updateINDI" } ); \
3434 }
3435
3436/// Call dmT::appShutdown with error checking for dm
3437#define DM_APP_SHUTDOWN \
3438 if( dmT::appShutdown() < 0 ) \
3439 { \
3440 return log<software_error, -1>( { "Error from dmT::appShutdown" } ); \
3441 }
3442
3443} // namespace dev
3444} // namespace app
3445} // namespace MagAOX
3446#endif
#define IMAGESTRUCT_FLOAT
#define IMAGESTRUCT_UINT8
std::string m_calibPath
The path to this DM's calibration files.
Definition dm.hpp:99
std::map< std::string, std::string > m_flatCommands
Map of flat file name to full path.
Definition dm.hpp:150
int newCallBack_tests(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
Definition dm.hpp:3294
bool m_testSet
Flag indicating whether the test command has been set.
Definition dm.hpp:168
std::string m_flatPath
The path to this DM's flat files (usually the same as calibPath)
Definition dm.hpp:100
float percThreshold() const
Get the saturation percentage threshold.
Definition dm.hpp:906
const std::string & testDefault() const
Get the.
Definition dm.hpp:846
const std::string & calibRelDir() const
Definition dm.hpp:936
uint32_t dmWidth() const
Get the DM Width.
Definition dm.hpp:888
std::string m_shmimDiff
The name of the shmim stream to write the difference to.
Definition dm.hpp:121
const mx::improc::eigenImage< uint16_t > & accumSatMap() const
Definition dm.hpp:954
pcf::IndiProperty m_indiP_setFlat
INDI toggle switch to set the current flat.
Definition dm.hpp:596
static void satThreadStart(dm *d)
Thread starter, called by MagAOXApp::threadStart on thread construction. Calls satThreadExec.
Definition dm.hpp:2887
pcf::IndiProperty m_indiP_init
Definition dm.hpp:590
uint32_t m_dmWidth
The width of the images in the stream.
Definition dm.hpp:123
int satAvgInt() const
Get the saturation accumulation interval.
pcf::IndiProperty m_indiP_flats
INDI Selection switch containing the flat files.
Definition dm.hpp:594
int zeroFlat()
Zero the flat command on the DM.
Definition dm.hpp:2311
static int st_newCallBack_init(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for initializing the DM.
Definition dm.hpp:3131
int checkFlats()
Check the flats directory and update the list of flats if anything changes.
Definition dm.hpp:1968
pcf::IndiProperty m_indiP_testShmim
Publish the shmim being used for the test command.
Definition dm.hpp:599
int checkTests()
Check the tests directory and update the list of tests if anything changes.
Definition dm.hpp:2387
static int st_newCallBack_tests(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for selecting the test file.
Definition dm.hpp:3288
const mx::improc::eigenImage< float > & satPercMap() const
Definition dm.hpp:960
std::string m_flatDefault
Definition dm.hpp:105
int setTest()
Send the current test command to the DM.
Definition dm.hpp:2562
int clearSat()
Clear the saturation maps and zero the shared memory.
Definition dm.hpp:2826
const std::vector< std::string > & satTriggerProperty() const
Get the saturation trigger property(ies)
Definition dm.hpp:930
static int st_newCallBack_zero(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for initializing the DM.
Definition dm.hpp:3162
int loadTest(const std::string &target)
Load a test file.
Definition dm.hpp:2482
int loadFlat(const std::string &target)
Load a flat file.
Definition dm.hpp:2103
int m_satThreadPrio
Priority of the saturation thread. Usually ok to be 0.
Definition dm.hpp:117
IMAGE m_satPercImageStream
The ImageStreamIO shared memory buffer for the sat percentage map.
Definition dm.hpp:180
uint8_t dmDataType() const
Get the DM data type.
Definition dm.hpp:900
std::string m_calibRelDir
Definition dm.hpp:143
pcf::IndiProperty m_indiP_setTest
INDI toggle switch to set the current test pattern.
Definition dm.hpp:600
const std::string & shmimShape() const
Get the.
Definition dm.hpp:876
int findDMChannels()
Find the DM comb channels.
Definition dm.hpp:1652
static constexpr uint8_t m_dmDataType
The ImageStreamIO type code.
Definition dm.hpp:126
std::vector< size_t > m_notDeltas
Indices of the channels which are not delta commands.
Definition dm.hpp:191
pcf::IndiProperty m_indiP_tests
INDI Selection switch containing the test pattern files.
Definition dm.hpp:598
mx::improc::eigenImage< realT > m_totalDelta
the total of all delta channels
Definition dm.hpp:194
mx::improc::eigenImage< realT > m_totalFlat
the total of all non-delta channels
Definition dm.hpp:193
std::string m_shmimDelta
The name of the shmim stream to write the desaturated delta command to.
Definition dm.hpp:120
std::string m_shmimFlat
The name of the shmim stream to write the flat to.
Definition dm.hpp:110
int processImage(void *curr_src, const dev::shmimT &sp)
Definition dm.hpp:1844
const std::string & shmimSatPerc() const
Get the stream name for saturation percentage.
Definition dm.hpp:870
const std::string & shmimDelta() const
Get the.
Definition dm.hpp:882
std::vector< mx::improc::milkImage< realT > * > m_channels
Definition dm.hpp:148
uint32_t m_dmHeight
The height of the images in the stream.
Definition dm.hpp:124
int appShutdown()
DM shutdown.
Definition dm.hpp:1617
std::string m_testPath
The path to this DM's test files (default is calibPath/tests;.
Definition dm.hpp:101
int newCallBack_zeroAll(const pcf::IndiProperty &ipRecv)
The callback for the zeroAll toggle switch, called by the static version.
Definition dm.hpp:3372
bool m_flatSet
Flag indicating whether the flat command has been set.
Definition dm.hpp:157
pcf::IndiProperty m_satThreadProp
The property to hold the saturation thread details.
Definition dm.hpp:211
int baseReleaseDM()
Calls derived()->releaseDM() and then 0s all channels and the sat map.
Definition dm.hpp:1942
std::string m_shmimSatPerc
The name of the shmim stream to write the saturation percentage map to.
Definition dm.hpp:113
int newCallBack_setTest(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
Definition dm.hpp:3345
const std::string & calibPath() const
Get the.
Definition dm.hpp:822
static int st_newCallBack_flats(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for selecting the flat file.
Definition dm.hpp:3210
std::string m_actMaskPath
The file name of the actuator mask for this DM.
Definition dm.hpp:103
mx::verbose::vvv verboseT
Definition dm.hpp:92
std::string m_testCurrent
Definition dm.hpp:162
int m_intervalSatExceeds
Definition dm.hpp:183
bool m_flatLoaded
Flag indicating whether a flat is loaded in memory.
Definition dm.hpp:154
bool m_testLoaded
Flag indicating whether a test command is loaded in memory.
Definition dm.hpp:165
mx::improc::eigenImage< float > m_satPercMap
Definition dm.hpp:176
int setFlat(bool update=false)
Send the current flat command to the DM.
Definition dm.hpp:2207
static int st_newCallBack_zeroAll(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for zeroing all channels.
Definition dm.hpp:3366
const std::vector< std::string > & deltaChannels() const
Definition dm.hpp:966
int newCallBack_init(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
Definition dm.hpp:3137
const std::string & shmimTest() const
Get the.
Definition dm.hpp:858
const std::vector< size_t > & notDeltas() const
Definition dm.hpp:972
const std::string & shmimFlat() const
Get the.
Definition dm.hpp:852
mx::improc::milkImage< realT > m_outputDelta
The true output delta command after saturation.
Definition dm.hpp:196
int zeroAll(bool nosem=false)
Zero all channels.
Definition dm.hpp:2711
mx::improc::milkImage< realT > m_actMask
Definition dm.hpp:159
pid_t m_satThreadID
The ID of the saturation thread.
Definition dm.hpp:209
int intervalSatCountThreshold() const
Get the interval saturation count threshold.
Definition dm.hpp:918
int baseInitDM()
Calls derived()->initDM()
Definition dm.hpp:1919
int zeroTest()
Zero the test command on the DM.
Definition dm.hpp:2653
int m_satAvgInt
The time in milliseconds to accumulate saturation over.
Definition dm.hpp:115
int newCallBack_release(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
Definition dm.hpp:3192
std::vector< std::string > m_satTriggerProperty
Definition dm.hpp:138
const std::string & flatDefault() const
Get the.
Definition dm.hpp:840
int updateINDI()
Update the INDI properties for this device controller.
Definition dm.hpp:3120
std::vector< size_t > m_deltas
Indices of the channels which are delta commands.
Definition dm.hpp:190
mx::improc::milkImage< realT > m_outputDiff
The difference between command and true delta command after saturation.
Definition dm.hpp:198
int newCallBack_flats(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
Definition dm.hpp:3216
sem_t m_satSemaphore
Semaphore used to tell the saturation thread to run.
Definition dm.hpp:205
pcf::IndiProperty m_indiP_flat
Property used to set and report the current flat.
Definition dm.hpp:588
IMAGE m_satImageStream
The ImageStreamIO shared memory buffer for the sat map.
Definition dm.hpp:179
int newCallBack_zero(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
Definition dm.hpp:3168
bool m_satThreadInit
Synchronizer for thread startup, to allow priority setting to finish.
Definition dm.hpp:207
derivedT & derived()
Definition dm.hpp:803
std::vector< std::string > m_satTriggerDevice
Device(s) with a toggle switch to toggle on saturation trigger.
Definition dm.hpp:136
mx::improc::eigenImage< realT > m_flatCommand
Data storage for the flat command.
Definition dm.hpp:153
int m_numChannels
The number of dmcomb channels found as part of allocation.
Definition dm.hpp:146
const mx::improc::eigenImage< uint8_t > & instSatMap() const
Definition dm.hpp:948
mx::improc::eigenImage< uint8_t > m_instSatMap
Definition dm.hpp:170
void satThreadExec()
Execute saturation processing.
Definition dm.hpp:2893
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
Definition dm.hpp:1255
static int st_newCallBack_release(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for initializing the DM.
Definition dm.hpp:3186
pcf::IndiProperty m_indiP_flatShmim
Publish the shmim being used for the flat.
Definition dm.hpp:595
float m_intervalSatThreshold
Definition dm.hpp:130
int whilePowerOff()
DM Poweroff Updates.
Definition dm.hpp:1643
static int st_newCallBack_setFlat(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for setting the flat.
Definition dm.hpp:3261
static int st_newCallBack_setTest(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for setting the test shape.
Definition dm.hpp:3339
pcf::IndiProperty m_indiP_zeroAll
Definition dm.hpp:602
std::map< std::string, std::string > m_testCommands
Map of test file name to full path.
Definition dm.hpp:161
int makeDelta()
Calculate the delta command from the output shape.
Definition dm.hpp:2797
mx::improc::milkImage< realT > m_outputShape
The true output shape after saturation.
Definition dm.hpp:186
std::vector< std::string > m_deltaChannels
The names of channels which are treated as delta commands.
Definition dm.hpp:188
const std::string & testPath() const
Get the.
Definition dm.hpp:834
IMAGE m_testImageStream
The ImageStreamIO shared memory buffer for the test.
Definition dm.hpp:167
std::string m_testDefault
Definition dm.hpp:107
std::string m_shmimTest
The name of the shmim stream to write the test to.
Definition dm.hpp:111
bool m_intervalSatTrip
Definition dm.hpp:184
std::string m_shmimShape
The name of the shmim stream to write the desaturated true shape to.
Definition dm.hpp:119
int m_intervalSatCountThreshold
Definition dm.hpp:133
~dm()
Destructor.
Definition dm.hpp:810
int appStartup()
Startup function.
Definition dm.hpp:1405
int newCallBack_setFlat(const pcf::IndiProperty &ipRecv)
The callback called by the static version, to actually process the new request.
Definition dm.hpp:3267
mx::improc::eigenImage< uint16_t > m_accumSatMap
Definition dm.hpp:173
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
Definition dm.hpp:984
int onPowerOff()
DM Poweroff.
Definition dm.hpp:1635
float m_percThreshold
Threshold on percentage of frames an actuator is saturated over an interval.
Definition dm.hpp:128
int satThreadPrio() const
Get the saturation thread priority.
std::string m_shmimSat
The name of the shmim stream to write the saturation map to.
Definition dm.hpp:112
float intervalSatThreshold() const
Get the interval saturation threshold.
Definition dm.hpp:912
const mx::improc::eigenImage< float > & totalFlat() const
Definition dm.hpp:978
mx::improc::eigenImage< realT > m_testCommand
Data storage for the test command.
Definition dm.hpp:164
IMAGE m_flatImageStream
The ImageStreamIO shared memory buffer for the flat.
Definition dm.hpp:156
std::thread m_satThread
A separate thread for the actual saturation processing.
Definition dm.hpp:213
pcf::IndiProperty m_indiP_release
Definition dm.hpp:592
pcf::IndiProperty m_indiP_zero
Definition dm.hpp:591
int numChannels() const
Definition dm.hpp:942
uint32_t dmHeight() const
Get the DM Height.
Definition dm.hpp:894
int allocate(const dev::shmimT &sp)
Called after shmimMonitor connects to the dmXXdisp stream. Checks for proper size.
Definition dm.hpp:1746
std::string m_flatCurrent
The name of the current flat command.
Definition dm.hpp:151
const std::vector< std::string > & satTriggerDevice() const
Get the saturation trigger device(s)
Definition dm.hpp:924
const std::string & shmimSat() const
Get the.
Definition dm.hpp:864
void intervalSatTrip()
Trigger loop openings because of excessive saturation.
Definition dm.hpp:3092
const std::string & flatPath() const
Get the.
Definition dm.hpp:828
int appLogic()
DM application logic.
Definition dm.hpp:1551
@ OPERATING
The device is operating, other than homing.
@ POWEROFF
The device power is off.
@ NOTHOMED
The device has not been homed.
@ HOMING
The device is homing.
@ ERROR
The application has encountered an error, from which it is recovering (with or without intervention)
@ READY
The device is ready for operation, but is not operating.
#define INDI_IDLE
Definition indiUtils.hpp:27
#define INDI_BUSY
Definition indiUtils.hpp:29
constexpr uint8_t ImageStreamTypeCode< float >()
Definition dm.hpp:32
constexpr uint8_t ImageStreamTypeCode()
Definition dm.hpp:26
constexpr uint8_t ImageStreamTypeCode< double >()
Definition dm.hpp:38
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
Definition dm.hpp:19
The MagAO-X generic shared memory monitor.