Line data Source code
1 : /** \file shmimIntegrator.hpp
2 : * \brief The MagAO-X generic ImageStreamIO stream integrator
3 : *
4 : * \ingroup app_files
5 : */
6 :
7 : #ifndef shmimIntegrator_hpp
8 : #define shmimIntegrator_hpp
9 :
10 : #include <limits>
11 :
12 : #include <mx/improc/eigenCube.hpp>
13 : #include <mx/improc/eigenImage.hpp>
14 :
15 : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
16 : #include "../../magaox_git_version.h"
17 :
18 : namespace MagAOX
19 : {
20 : namespace app
21 : {
22 :
23 : struct darkShmimT
24 : {
25 0 : static std::string configSection()
26 : {
27 0 : return "darkShmim";
28 : };
29 :
30 0 : static std::string indiPrefix()
31 : {
32 0 : return "dark";
33 : };
34 : };
35 :
36 : struct dark2ShmimT
37 : {
38 0 : static std::string configSection()
39 : {
40 0 : return "dark2Shmim";
41 : };
42 :
43 0 : static std::string indiPrefix()
44 : {
45 0 : return "dark2";
46 : };
47 : };
48 :
49 : /** \defgroup shmimIntegrator ImageStreamIO Stream Integrator
50 : * \brief Integrates (i.e. averages) an ImageStreamIO image stream.
51 : *
52 : * <a href="../handbook/operating/software/apps/shmimIntegrator.html">Application Documentation</a>
53 : *
54 : * \ingroup apps
55 : *
56 : */
57 :
58 : /** \defgroup shmimIntegrator_files ImageStreamIO Stream Integrator
59 : * \ingroup shmimIntegrator
60 : */
61 :
62 : /** MagAO-X application to control integrating (averaging) an ImageStreamIO stream
63 : *
64 : * \ingroup shmimIntegrator
65 : *
66 : */
67 : class shmimIntegrator : public MagAOXApp<true>,
68 : public dev::shmimMonitor<shmimIntegrator>,
69 : public dev::shmimMonitor<shmimIntegrator, darkShmimT>,
70 : public dev::shmimMonitor<shmimIntegrator, dark2ShmimT>,
71 : public dev::frameGrabber<shmimIntegrator>,
72 : public dev::telemeter<shmimIntegrator>
73 : {
74 :
75 : // Give the test harness access.
76 : friend class shmimIntegrator_test;
77 :
78 : friend class dev::shmimMonitor<shmimIntegrator>;
79 : friend class dev::shmimMonitor<shmimIntegrator, darkShmimT>;
80 : friend class dev::shmimMonitor<shmimIntegrator, dark2ShmimT>;
81 : friend class dev::frameGrabber<shmimIntegrator>;
82 : friend class dev::telemeter<shmimIntegrator>;
83 :
84 : // The base shmimMonitor type
85 : typedef dev::shmimMonitor<shmimIntegrator> shmimMonitorT;
86 :
87 : // The dark shmimMonitor type
88 : typedef dev::shmimMonitor<shmimIntegrator, darkShmimT> darkMonitorT;
89 :
90 : // The dark shmimMonitor type for a 2nd dark
91 : typedef dev::shmimMonitor<shmimIntegrator, dark2ShmimT> dark2MonitorT;
92 :
93 : // The base frameGrabber type
94 : typedef dev::frameGrabber<shmimIntegrator> frameGrabberT;
95 :
96 : typedef dev::telemeter<shmimIntegrator> telemeterT;
97 :
98 : /// Floating point type in which to do all calculations.
99 : typedef float realT;
100 :
101 : public:
102 : /** \name app::dev Configurations
103 : *@{
104 : */
105 :
106 : static constexpr bool c_frameGrabber_flippable =
107 : false; ///< app:dev config to tell framegrabber these images can not be flipped
108 :
109 : ///@}
110 :
111 : protected:
112 : /** \name Configurable Parameters
113 : *@{
114 : */
115 :
116 : unsigned m_nAverageDefault{ 10 }; ///< The number of frames to average. Default 10.
117 :
118 : std::string m_fpsSource; ///< Device name for getting fps if time-based averaging is used. This device should have
119 : ///< *.fps.current.
120 :
121 : float m_avgTime{ 0 }; ///< If non zero, then m_nAverage adjusts automatically to keep a constant averaging time
122 : ///< [sec]. Default 0.
123 :
124 : unsigned m_nUpdate{ 0 }; ///< The rate at which to update the average. If 0 < m_nUpdate < m_nAverage then this is a
125 : ///< moving averager. Default 0.
126 :
127 : bool m_continuous{ true }; ///< Set to false in configuration to have this run once then stop until triggered.
128 :
129 : bool m_running{ true }; ///< Set to false in configuration to have it not start averaging until triggered.
130 :
131 : std::string m_stateSource; ///< The source of the state string used for file management
132 :
133 : bool m_fileSaver{ false }; ///< Set to true in configuration to have this save and reload files automatically.
134 :
135 : ///@}
136 :
137 : mx::improc::eigenCube<realT> m_accumImages; ///< Cube used to accumulate images
138 :
139 : mx::improc::eigenImage<realT> m_avgImage; ///< The average image.
140 :
141 : unsigned m_nAverage{ 10 };
142 :
143 : float m_fps{ 0 }; ///< Current FPS from the FPS source.
144 :
145 : unsigned m_nprocessed{ 0 };
146 : size_t m_currImage{ 0 };
147 : size_t m_sinceUpdate{ 0 };
148 : bool m_updated{ false };
149 :
150 : bool m_imageValid{ false };
151 : std::string m_stateString;
152 : bool m_stateStringValid{ false };
153 : bool m_stateStringValidOnStart{ false };
154 : bool m_stateStringChanged{ false };
155 : std::string m_fileSaveDir;
156 :
157 : sem_t m_smSemaphore{ 0 }; ///< Semaphore used to synchronize the fg thread and the sm thread.
158 :
159 : realT ( *pixget )( void *, size_t ){
160 : nullptr }; ///< Pointer to a function to extract the image data as our desired type realT.
161 :
162 : /// Mutex for locking dark operations.
163 : std::mutex m_darkMutex;
164 :
165 : mx::improc::eigenImage<realT> m_darkImage;
166 : bool m_darkSet{ false };
167 : bool m_darkValid{ false };
168 : realT ( *dark_pixget )( void *, size_t ){
169 : nullptr }; ///< Pointer to a function to extract the image data as our desired type realT.
170 :
171 : mx::improc::eigenImage<realT> m_dark2Image;
172 : bool m_dark2Set{ false };
173 : bool m_dark2Valid{ false };
174 : realT ( *dark2_pixget )( void *, size_t ){
175 : nullptr }; ///< Pointer to a function to extract the image data as our desired type realT.
176 :
177 : public:
178 : /// Default c'tor.
179 : shmimIntegrator();
180 :
181 : /// D'tor, declared and defined for noexcept.
182 0 : ~shmimIntegrator() noexcept
183 0 : {
184 0 : }
185 :
186 : virtual void setupConfig();
187 :
188 : /// Implementation of loadConfig logic, separated for testing.
189 : /** This is called by loadConfig().
190 : */
191 : int loadConfigImpl(
192 : mx::app::appConfigurator &_config /**< [in] an application configuration from which to load values*/ );
193 :
194 : virtual void loadConfig();
195 :
196 : /// Startup function
197 : /**
198 : *
199 : */
200 : virtual int appStartup();
201 :
202 : /// Implementation of the FSM for shmimIntegrator.
203 : /**
204 : * \returns 0 on no critical error
205 : * \returns -1 on an error requiring shutdown
206 : */
207 : virtual int appLogic();
208 :
209 : /// Shutdown the app.
210 : /**
211 : *
212 : */
213 : virtual int appShutdown();
214 :
215 : protected:
216 : int allocate( const dev::shmimT &dummy /**< [in] tag to differentiate shmimMonitor parents.*/ );
217 :
218 : int processImage( void *curr_src, ///< [in] pointer to start of current frame.
219 : const dev::shmimT &dummy ///< [in] tag to differentiate shmimMonitor parents.
220 : );
221 :
222 : int allocate( const darkShmimT &dummy /**< [in] tag to differentiate shmimMonitor parents.*/ );
223 :
224 : int processImage( void *curr_src, ///< [in] pointer to start of current frame.
225 : const darkShmimT &dummy ///< [in] tag to differentiate shmimMonitor parents.
226 : );
227 :
228 : int allocate( const dark2ShmimT &dummy /**< [in] tag to differentiate shmimMonitor parents.*/ );
229 :
230 : int processImage( void *curr_src, ///< [in] pointer to start of current frame.
231 : const dark2ShmimT &dummy ///< [in] tag to differentiate shmimMonitor parents.
232 : );
233 :
234 : int findMatchingDark();
235 :
236 : /** \name dev::frameGrabber interface
237 : *
238 : * @{
239 : */
240 :
241 : /// Implementation of the framegrabber configureAcquisition interface
242 : /**
243 : * \returns 0 on success
244 : * \returns -1 on error
245 : */
246 : int configureAcquisition();
247 :
248 : /// Implementation of the framegrabber fps interface
249 : /**
250 : * \todo this needs to infer the stream fps and return it
251 : */
252 0 : float fps()
253 : {
254 0 : if( m_fps > 0 && m_nAverage > 0 && ( m_running || m_continuous ) )
255 : {
256 0 : return m_fps / m_nAverage;
257 : }
258 : else
259 : { // this will cause a default averaging
260 :
261 0 : return 1.0;
262 : }
263 : }
264 :
265 : /// Implementation of the framegrabber startAcquisition interface
266 : /**
267 : * \returns 0 on success
268 : * \returns -1 on error
269 : */
270 : int startAcquisition();
271 :
272 : /// Implementation of the framegrabber acquireAndCheckValid interface
273 : /**
274 : * \returns 0 on success
275 : * \returns -1 on error
276 : */
277 : int acquireAndCheckValid();
278 :
279 : /// Implementation of the framegrabber loadImageIntoStream interface
280 : /**
281 : * \returns 0 on success
282 : * \returns -1 on error
283 : */
284 : int loadImageIntoStream( void *dest /**< [in] */ );
285 :
286 : /// Implementation of the framegrabber reconfig interface
287 : /**
288 : * \returns 0 on success
289 : * \returns -1 on error
290 : */
291 : int reconfig();
292 :
293 : ///@}
294 :
295 : pcf::IndiProperty m_indiP_nAverage;
296 :
297 : pcf::IndiProperty m_indiP_avgTime;
298 :
299 : pcf::IndiProperty m_indiP_nUpdate;
300 :
301 : pcf::IndiProperty m_indiP_startAveraging;
302 :
303 0 : INDI_NEWCALLBACK_DECL( shmimIntegrator, m_indiP_nAverage );
304 0 : INDI_NEWCALLBACK_DECL( shmimIntegrator, m_indiP_avgTime );
305 0 : INDI_NEWCALLBACK_DECL( shmimIntegrator, m_indiP_nUpdate );
306 0 : INDI_NEWCALLBACK_DECL( shmimIntegrator, m_indiP_startAveraging );
307 :
308 : pcf::IndiProperty m_indiP_fpsSource;
309 0 : INDI_SETCALLBACK_DECL( shmimIntegrator, m_indiP_fpsSource );
310 :
311 : pcf::IndiProperty m_indiP_fps; ///< this integrator's FPS
312 :
313 : pcf::IndiProperty m_indiP_stateSource;
314 0 : INDI_SETCALLBACK_DECL( shmimIntegrator, m_indiP_stateSource );
315 :
316 : pcf::IndiProperty m_indiP_imageValid;
317 :
318 : /** \name Telemeter Interface
319 : *
320 : * @{
321 : */
322 : int checkRecordTimes();
323 :
324 : int recordTelem( const telem_fgtimings * );
325 :
326 : ///@}
327 : };
328 :
329 : inline shmimIntegrator::shmimIntegrator() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
330 : {
331 : darkMonitorT::m_getExistingFirst = true;
332 : return;
333 : }
334 :
335 0 : inline void shmimIntegrator::setupConfig()
336 : {
337 0 : shmimMonitorT::setupConfig( config );
338 0 : darkMonitorT::setupConfig( config );
339 0 : dark2MonitorT::setupConfig( config );
340 :
341 0 : frameGrabberT::setupConfig( config );
342 0 : telemeterT::setupConfig( config );
343 :
344 0 : config.add( "integrator.nAverage",
345 : "",
346 : "integrator.nAverage",
347 : argType::Required,
348 : "integrator",
349 : "nAverage",
350 : false,
351 : "unsigned",
352 : "The default number of frames to average. Default 10. Can be changed via INDI." );
353 0 : config.add(
354 : "integrator.fpsSource",
355 : "",
356 : "integrator.fpsSource",
357 : argType::Required,
358 : "integrator",
359 : "fpsSource",
360 : false,
361 : "string",
362 : "Device name for getting fps if time-based averaging is used. This device should have *.fps.current." );
363 :
364 0 : config.add( "integrator.avgTime",
365 : "",
366 : "integrator.avgTime",
367 : argType::Required,
368 : "integrator",
369 : "avgTime",
370 : false,
371 : "float",
372 : "If non zero, then m_nAverage adjusts automatically to keep a constant averaging time [sec]. Default "
373 : "0. Can be changed via INDI." );
374 :
375 0 : config.add( "integrator.nUpdate",
376 : "",
377 : "integrator.nUpdate",
378 : argType::Required,
379 : "integrator",
380 : "nUpdate",
381 : false,
382 : "unsigned",
383 : "The rate at which to update the average. If 0 < m_nUpdate < m_nAverage then this is a moving "
384 : "averager. Default 0. If 0, then it is a simple average." );
385 :
386 0 : config.add( "integrator.continuous",
387 : "",
388 : "integrator.continuous",
389 : argType::Required,
390 : "integrator",
391 : "continuous",
392 : false,
393 : "bool",
394 : "Flag controlling whether averaging is continuous or only when triggered. Default true." );
395 0 : config.add( "integrator.running",
396 : "",
397 : "integrator.running",
398 : argType::Required,
399 : "integrator",
400 : "running",
401 : false,
402 : "bool",
403 : "Flag controlling whether averaging is running at startup. Default true." );
404 :
405 0 : config.add( "integrator.stateSource",
406 : "",
407 : "integrator.stateSource",
408 : argType::Required,
409 : "integrator",
410 : "stateSource",
411 : false,
412 : "string",
413 : "///< Device name for getting the state string for file management. This device should have "
414 : "*.state_string.current." );
415 0 : config.add( "integrator.fileSaver",
416 : "",
417 : "integrator.fileSaver",
418 : argType::Required,
419 : "integrator",
420 : "fileSaver",
421 : false,
422 : "bool",
423 : "Flag controlling whether this saves and reloads files automatically. Default false." );
424 0 : }
425 :
426 0 : inline int shmimIntegrator::loadConfigImpl( mx::app::appConfigurator &_config )
427 : {
428 :
429 0 : shmimMonitorT::loadConfig( config );
430 0 : darkMonitorT::loadConfig( config );
431 0 : dark2MonitorT::loadConfig( config );
432 :
433 0 : frameGrabberT::loadConfig( config );
434 0 : telemeterT::loadConfig( config );
435 :
436 0 : _config( m_nAverageDefault, "integrator.nAverage" );
437 0 : m_nAverage = m_nAverageDefault;
438 0 : _config( m_fpsSource, "integrator.fpsSource" );
439 0 : _config( m_avgTime, "integrator.avgTime" );
440 0 : _config( m_nUpdate, "integrator.nUpdate" );
441 :
442 0 : _config( m_continuous, "integrator.continuous" );
443 :
444 0 : _config( m_running, "integrator.running" );
445 :
446 0 : _config( m_stateSource, "integrator.stateSource" );
447 0 : _config( m_fileSaver, "integrator.fileSaver" );
448 :
449 0 : return 0;
450 : }
451 :
452 0 : inline void shmimIntegrator::loadConfig()
453 : {
454 0 : loadConfigImpl( config );
455 0 : }
456 :
457 0 : inline int shmimIntegrator::appStartup()
458 : {
459 :
460 0 : createStandardIndiNumber<unsigned>(
461 0 : m_indiP_nAverage, "nAverage", 1, std::numeric_limits<unsigned>::max(), 1, "%u" );
462 0 : m_indiP_nAverage["current"].set( m_nAverage );
463 0 : m_indiP_nAverage["target"].set( m_nAverage );
464 :
465 0 : if( registerIndiPropertyNew( m_indiP_nAverage, INDI_NEWCALLBACK( m_indiP_nAverage ) ) < 0 )
466 : {
467 0 : log<software_error>( { __FILE__, __LINE__ } );
468 0 : return -1;
469 : }
470 :
471 0 : createStandardIndiNumber<float>( m_indiP_avgTime, "avgTime", 0, std::numeric_limits<float>::max(), 0, "%0.1f" );
472 0 : m_indiP_avgTime["current"].set( m_avgTime );
473 0 : m_indiP_avgTime["target"].set( m_avgTime );
474 :
475 0 : if( registerIndiPropertyNew( m_indiP_avgTime, INDI_NEWCALLBACK( m_indiP_avgTime ) ) < 0 )
476 : {
477 0 : log<software_error>( { __FILE__, __LINE__ } );
478 0 : return -1;
479 : }
480 :
481 0 : createStandardIndiNumber<unsigned>( m_indiP_nUpdate, "nUpdate", 1, std::numeric_limits<unsigned>::max(), 1, "%u" );
482 0 : m_indiP_nUpdate["current"].set( m_nUpdate );
483 0 : m_indiP_nUpdate["target"].set( m_nUpdate );
484 :
485 0 : if( registerIndiPropertyNew( m_indiP_nUpdate, INDI_NEWCALLBACK( m_indiP_nUpdate ) ) < 0 )
486 : {
487 0 : log<software_error>( { __FILE__, __LINE__ } );
488 0 : return -1;
489 : }
490 :
491 0 : createStandardIndiToggleSw( m_indiP_startAveraging, "start" );
492 0 : if( registerIndiPropertyNew( m_indiP_startAveraging, INDI_NEWCALLBACK( m_indiP_startAveraging ) ) < 0 )
493 : {
494 0 : log<software_error>( { __FILE__, __LINE__ } );
495 0 : return -1;
496 : }
497 :
498 0 : if( m_fpsSource != "" )
499 : {
500 0 : REG_INDI_SETPROP( m_indiP_fpsSource, m_fpsSource, std::string( "fps" ) );
501 : }
502 :
503 0 : CREATE_REG_INDI_RO_NUMBER( m_indiP_fps, "fps", "", "" );
504 0 : m_indiP_fps.add( pcf::IndiElement( "current" ) );
505 0 : m_indiP_fps["current"].set( 0 );
506 :
507 0 : if( m_fileSaver == true && m_stateSource != "" )
508 : {
509 0 : REG_INDI_SETPROP( m_indiP_stateSource, m_stateSource, std::string( "state_string" ) );
510 :
511 0 : createROIndiText( m_indiP_imageValid, "image_valid", "flag", "Image Valid", "Image", "Valid" );
512 0 : if( !m_imageValid ) // making sure we stay up with default
513 : {
514 0 : m_indiP_imageValid["flag"] = "no";
515 : }
516 : else
517 : {
518 0 : m_indiP_imageValid["flag"] = "yes";
519 : }
520 :
521 0 : if( registerIndiPropertyReadOnly( m_indiP_imageValid ) < 0 )
522 : {
523 0 : log<software_error>( { __FILE__, __LINE__ } );
524 0 : return -1;
525 : }
526 :
527 0 : m_fileSaveDir = m_calibDir + "/" + m_configName;
528 :
529 : // Create save directory.
530 0 : errno = 0;
531 0 : if( mkdir( m_fileSaveDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) < 0 )
532 : {
533 0 : if( errno != EEXIST )
534 : {
535 0 : std::stringstream logss;
536 0 : logss << "Failed to create image directory (" << m_fileSaveDir
537 0 : << "). Errno says: " << strerror( errno );
538 0 : log<software_critical>( { __FILE__, __LINE__, errno, 0, logss.str() } );
539 :
540 0 : return -1;
541 0 : }
542 : }
543 : }
544 :
545 0 : if( sem_init( &m_smSemaphore, 0, 0 ) < 0 )
546 : {
547 0 : log<software_critical>( { __FILE__, __LINE__, errno, 0, "Initializing S.M. semaphore" } );
548 0 : return -1;
549 : }
550 :
551 0 : if( shmimMonitorT::appStartup() < 0 )
552 : {
553 0 : return log<software_error, -1>( { __FILE__, __LINE__ } );
554 : }
555 :
556 0 : if( darkMonitorT::appStartup() < 0 )
557 : {
558 0 : return log<software_error, -1>( { __FILE__, __LINE__ } );
559 : }
560 :
561 0 : if( dark2MonitorT::appStartup() < 0 )
562 : {
563 0 : return log<software_error, -1>( { __FILE__, __LINE__ } );
564 : }
565 :
566 0 : if( frameGrabberT::appStartup() < 0 )
567 : {
568 0 : return log<software_error, -1>( { __FILE__, __LINE__ } );
569 : }
570 :
571 0 : if( telemeterT::appStartup() < 0 )
572 : {
573 0 : return log<software_error, -1>( { __FILE__, __LINE__ } );
574 : }
575 :
576 0 : state( stateCodes::READY );
577 :
578 0 : return 0;
579 : }
580 :
581 0 : inline int shmimIntegrator::appLogic()
582 : {
583 0 : if( shmimMonitorT::appLogic() < 0 )
584 : {
585 0 : return log<software_error, -1>( { __FILE__, __LINE__ } );
586 : }
587 :
588 0 : if( darkMonitorT::appLogic() < 0 )
589 : {
590 0 : return log<software_error, -1>( { __FILE__, __LINE__ } );
591 : }
592 :
593 0 : if( dark2MonitorT::appLogic() < 0 )
594 : {
595 0 : return log<software_error, -1>( { __FILE__, __LINE__ } );
596 : }
597 :
598 0 : if( frameGrabberT::appLogic() < 0 )
599 : {
600 0 : return log<software_error, -1>( { __FILE__, __LINE__ } );
601 : }
602 :
603 0 : if( telemeterT::appLogic() < 0 )
604 : {
605 0 : return log<software_error, -1>( { __FILE__, __LINE__ } );
606 : }
607 :
608 0 : std::unique_lock<std::mutex> lock( m_indiMutex );
609 :
610 0 : if( shmimMonitorT::updateINDI() < 0 )
611 : {
612 0 : log<software_error>( { __FILE__, __LINE__ } );
613 : }
614 :
615 0 : if( darkMonitorT::updateINDI() < 0 )
616 : {
617 0 : log<software_error>( { __FILE__, __LINE__ } );
618 : }
619 :
620 0 : if( dark2MonitorT::updateINDI() < 0 )
621 : {
622 0 : log<software_error>( { __FILE__, __LINE__ } );
623 : }
624 :
625 0 : if( frameGrabberT::updateINDI() < 0 )
626 : {
627 0 : log<software_error>( { __FILE__, __LINE__ } );
628 : }
629 :
630 0 : if( m_running == false )
631 : {
632 0 : state( stateCodes::READY );
633 0 : updateSwitchIfChanged( m_indiP_startAveraging, "toggle", pcf::IndiElement::Off, INDI_IDLE );
634 :
635 0 : if( m_fileSaver )
636 : {
637 0 : if( m_stateStringChanged ) // So if not running and the state has changed, we check
638 : {
639 0 : if( findMatchingDark() < 0 )
640 : {
641 0 : log<software_error>( { __FILE__, __LINE__ } );
642 : }
643 : }
644 :
645 0 : if( !m_imageValid )
646 : {
647 0 : updateIfChanged( m_indiP_imageValid, "flag", "no" );
648 : }
649 : else
650 : {
651 0 : updateIfChanged( m_indiP_imageValid, "flag", "yes" );
652 : }
653 : }
654 : }
655 : else
656 : {
657 0 : state( stateCodes::OPERATING );
658 0 : updateSwitchIfChanged( m_indiP_startAveraging, "toggle", pcf::IndiElement::On, INDI_BUSY );
659 : }
660 :
661 0 : updateIfChanged( m_indiP_nAverage, "current", m_nAverage, INDI_IDLE );
662 0 : updateIfChanged( m_indiP_nAverage, "target", m_nAverage, INDI_IDLE );
663 :
664 0 : updateIfChanged( m_indiP_avgTime, "current", m_avgTime, INDI_IDLE );
665 0 : updateIfChanged( m_indiP_avgTime, "target", m_avgTime, INDI_IDLE );
666 :
667 0 : updateIfChanged( m_indiP_nUpdate, "current", m_nUpdate, INDI_IDLE );
668 0 : updateIfChanged( m_indiP_nUpdate, "target", m_nUpdate, INDI_IDLE );
669 :
670 0 : updateIfChanged( m_indiP_fps, "current", fps(), INDI_OK );
671 :
672 0 : return 0;
673 0 : }
674 :
675 0 : inline int shmimIntegrator::appShutdown()
676 : {
677 0 : shmimMonitorT::appShutdown();
678 :
679 0 : darkMonitorT::appShutdown();
680 :
681 0 : dark2MonitorT::appShutdown();
682 :
683 0 : frameGrabberT::appShutdown();
684 :
685 0 : telemeterT::appShutdown();
686 :
687 0 : return 0;
688 : }
689 :
690 0 : inline int shmimIntegrator::allocate( const dev::shmimT &dummy )
691 : {
692 : static_cast<void>( dummy ); // be unused
693 :
694 : // This whole thing could invalidate the dark.
695 0 : std::unique_lock<std::mutex> lock( m_darkMutex ); // Lock the mutex before messing with the dark.
696 :
697 0 : if( m_avgTime > 0 && m_fps > 0 )
698 : {
699 0 : m_nAverage = m_avgTime * m_fps;
700 0 : if( m_nAverage <= 0 )
701 : {
702 0 : m_nAverage = 1;
703 : }
704 0 : log<text_log>( "set nAverage to " + std::to_string( m_nAverage ) + " based on FPS", logPrio::LOG_NOTICE );
705 : }
706 0 : else if( m_avgTime > 0 && m_fps == 0 ) // Haven't gotten the update yet so we keep going for now
707 : {
708 0 : if( m_nAverage != m_nAverageDefault )
709 : {
710 0 : m_nAverage = m_nAverageDefault;
711 0 : log<text_log>( "set nAverage to default " + std::to_string( m_nAverage ), logPrio::LOG_NOTICE );
712 : }
713 : }
714 :
715 0 : if( m_nUpdate > 0 )
716 : {
717 0 : m_accumImages.resize( shmimMonitorT::m_width, shmimMonitorT::m_height, m_nAverage );
718 0 : m_accumImages.setZero();
719 : }
720 : else
721 : {
722 0 : m_accumImages.resize( 1, 1, 1 );
723 : }
724 :
725 0 : m_nprocessed = 0;
726 0 : m_currImage = 0;
727 0 : m_sinceUpdate = 0;
728 :
729 0 : m_avgImage.resize( shmimMonitorT::m_width, shmimMonitorT::m_height );
730 : // m_avgImage.setZero();
731 :
732 0 : pixget = getPixPointer<realT>( shmimMonitorT::m_dataType );
733 :
734 0 : if( pixget == nullptr )
735 : {
736 0 : log<software_error>( { __FILE__, __LINE__, "bad data type" } );
737 0 : return -1;
738 : }
739 :
740 0 : updateIfChanged( m_indiP_nAverage, "current", m_nAverage, INDI_IDLE );
741 0 : updateIfChanged( m_indiP_nAverage, "target", m_nAverage, INDI_IDLE );
742 :
743 0 : updateIfChanged( m_indiP_avgTime, "current", m_avgTime, INDI_IDLE );
744 0 : updateIfChanged( m_indiP_avgTime, "target", m_avgTime, INDI_IDLE );
745 :
746 0 : updateIfChanged( m_indiP_nUpdate, "current", m_nUpdate, INDI_IDLE );
747 0 : updateIfChanged( m_indiP_nUpdate, "target", m_nUpdate, INDI_IDLE );
748 :
749 0 : if( darkMonitorT::m_width == shmimMonitorT::m_width || darkMonitorT::m_height == shmimMonitorT::m_height )
750 : {
751 0 : m_darkValid = true;
752 : }
753 : else
754 : {
755 0 : m_darkValid = false;
756 : }
757 :
758 0 : if( dark2MonitorT::m_width == shmimMonitorT::m_width || dark2MonitorT::m_height == shmimMonitorT::m_height )
759 : {
760 0 : m_dark2Valid = true;
761 : }
762 : else
763 : {
764 0 : m_dark2Valid = false;
765 : }
766 :
767 0 : m_reconfig = true;
768 :
769 0 : return 0;
770 0 : }
771 :
772 0 : inline int shmimIntegrator::processImage( void *curr_src, const dev::shmimT &dummy )
773 : {
774 : static_cast<void>( dummy ); // be unused
775 :
776 0 : if( !m_running )
777 0 : return 0;
778 :
779 0 : if( m_nUpdate == 0 )
780 : {
781 0 : if( m_updated )
782 0 : return 0;
783 0 : if( m_sinceUpdate == 0 )
784 0 : m_avgImage.setZero();
785 :
786 0 : realT *data = m_avgImage.data();
787 :
788 0 : for( unsigned nn = 0; nn < shmimMonitorT::m_width * shmimMonitorT::m_height; ++nn )
789 : {
790 0 : data[nn] += pixget( curr_src, nn );
791 : }
792 0 : ++m_sinceUpdate;
793 0 : if( m_sinceUpdate >= m_nAverage )
794 : {
795 0 : m_avgImage /= m_nAverage; ///\todo should this be /= m_sinceUpdate?
796 :
797 0 : if( ( m_darkSet && m_darkValid ) && !( m_dark2Set && m_dark2Valid ) )
798 : {
799 0 : std::unique_lock<std::mutex> lock( m_darkMutex ); // Lock the mutex before messing with the dark.
800 0 : m_avgImage -= m_darkImage;
801 0 : }
802 0 : else if( !( m_darkSet && m_darkValid ) && ( m_dark2Set && m_dark2Valid ) )
803 : {
804 0 : std::unique_lock<std::mutex> lock( m_darkMutex ); // Lock the mutex before messing with the dark.
805 0 : m_avgImage -= m_dark2Image;
806 0 : }
807 0 : else if( ( m_darkSet && m_darkValid ) && ( m_dark2Set && m_dark2Valid ) )
808 : {
809 0 : std::unique_lock<std::mutex> lock( m_darkMutex ); // Lock the mutex before messing with the dark.
810 0 : m_avgImage -= m_darkImage + m_dark2Image;
811 0 : }
812 :
813 0 : m_updated = true;
814 :
815 : // Now tell the f.g. to get going
816 0 : if( sem_post( &m_smSemaphore ) < 0 )
817 : {
818 0 : log<software_critical>( { __FILE__, __LINE__, errno, 0, "Error posting to semaphore" } );
819 0 : return -1;
820 : }
821 :
822 0 : m_sinceUpdate = 0;
823 0 : if( !m_continuous )
824 : {
825 0 : m_running = false;
826 0 : if( m_fileSaver )
827 : {
828 0 : if( m_stateStringChanged || !m_stateStringValid || !m_stateStringValidOnStart )
829 : {
830 0 : m_imageValid = false;
831 0 : log<text_log>( "state changed during acquisition, not saving", logPrio::LOG_NOTICE );
832 : }
833 : else
834 : {
835 0 : m_imageValid = true;
836 0 : m_stateStringChanged = false;
837 :
838 : ///\todo this should happen in a different less-real-time thread.
839 : // Otherwise we save:
840 : timespec fts;
841 0 : clock_gettime( CLOCK_REALTIME, &fts );
842 :
843 : tm uttime; // The broken down time.
844 :
845 0 : if( gmtime_r( &fts.tv_sec, &uttime ) == 0 )
846 : {
847 : // Yell at operator but keep going
848 0 : log<software_alert>( { __FILE__,
849 : __LINE__,
850 0 : errno,
851 : 0,
852 : "gmtime_r error. possible loss of timing information." } );
853 : }
854 :
855 0 : char cts[] = "YYYYMMDDHHMMSSNNNNNNNNN";
856 0 : int rv = snprintf( cts,
857 : sizeof( cts ),
858 : "%04i%02i%02i%02i%02i%02i%09i",
859 0 : uttime.tm_year + 1900,
860 0 : uttime.tm_mon + 1,
861 : uttime.tm_mday,
862 : uttime.tm_hour,
863 : uttime.tm_min,
864 : uttime.tm_sec,
865 0 : static_cast<int>( fts.tv_nsec ) );
866 :
867 0 : if( rv != sizeof( cts ) - 1 )
868 : {
869 : // Something is very wrong. Keep going to try to get it on disk.
870 0 : log<software_alert>(
871 0 : { __FILE__, __LINE__, errno, rv, "did not write enough chars to timestamp" } );
872 : }
873 :
874 : std::string fname =
875 0 : m_fileSaveDir + "/" + m_configName + "_" + m_stateString + "__T" + cts + ".fits";
876 :
877 0 : mx::fits::fitsFile<float> ff;
878 0 : ff.write( fname, m_avgImage );
879 0 : log<text_log>( "Wrote " + fname );
880 0 : }
881 : }
882 : }
883 : }
884 : }
885 : else
886 : {
887 0 : realT *data = m_accumImages.image( m_currImage ).data();
888 :
889 0 : for( unsigned nn = 0; nn < shmimMonitorT::m_width * shmimMonitorT::m_height; ++nn )
890 : {
891 0 : data[nn] = pixget( curr_src, nn );
892 : }
893 0 : ++m_nprocessed;
894 0 : ++m_currImage;
895 0 : if( m_currImage >= m_nAverage )
896 0 : m_currImage = 0;
897 :
898 0 : if( m_nprocessed < m_nAverage ) // Check that we are burned in on first pass through cube
899 : {
900 0 : return 0;
901 : }
902 :
903 0 : ++m_sinceUpdate;
904 :
905 0 : if( m_sinceUpdate >= m_nUpdate )
906 : {
907 0 : if( m_updated )
908 : {
909 0 : return 0; // In case f.g. thread is behind, we skip and come back.
910 : }
911 : // Don't use eigenCube functions to avoid any omp
912 0 : m_avgImage.setZero();
913 0 : for( size_t n = 0; n < m_nAverage; ++n )
914 : {
915 0 : for( size_t ii = 0; ii < shmimMonitorT::m_width; ++ii )
916 : {
917 0 : for( size_t jj = 0; jj < shmimMonitorT::m_height; ++jj )
918 : {
919 0 : m_avgImage( ii, jj ) += m_accumImages.image( n )( ii, jj );
920 : }
921 : }
922 : }
923 0 : m_avgImage /= m_nAverage;
924 :
925 0 : if( m_darkValid && m_darkSet )
926 : {
927 0 : std::unique_lock<std::mutex> lock( m_darkMutex ); // Lock the mutex before messing with the dark.
928 0 : m_avgImage -= m_darkImage;
929 0 : }
930 :
931 0 : m_updated = true;
932 :
933 : // Now tell the f.g. to get going
934 0 : if( sem_post( &m_smSemaphore ) < 0 )
935 : {
936 0 : log<software_critical>( { __FILE__, __LINE__, errno, 0, "Error posting to semaphore" } );
937 0 : return -1;
938 : }
939 :
940 0 : m_sinceUpdate = 0;
941 : }
942 : }
943 0 : return 0;
944 : }
945 :
946 0 : inline int shmimIntegrator::allocate( const darkShmimT &dummy )
947 : {
948 : static_cast<void>( dummy ); // be unused
949 :
950 0 : std::unique_lock<std::mutex> lock( m_darkMutex ); // Lock the mutex before messing with the dark.
951 :
952 0 : m_darkImage.resize( darkMonitorT::m_width, darkMonitorT::m_height );
953 0 : m_darkImage.setZero();
954 :
955 0 : dark_pixget = getPixPointer<realT>( darkMonitorT::m_dataType );
956 :
957 0 : if( dark_pixget == nullptr )
958 : {
959 0 : log<software_error>( { __FILE__, __LINE__, "bad data type" } );
960 0 : m_darkSet = false;
961 0 : m_darkValid = false;
962 0 : return -1;
963 : }
964 :
965 0 : if( darkMonitorT::m_width == shmimMonitorT::m_width || darkMonitorT::m_height == shmimMonitorT::m_height )
966 : {
967 0 : m_darkValid = true;
968 : }
969 : else
970 : {
971 0 : m_darkValid = false;
972 : }
973 :
974 0 : return 0;
975 0 : }
976 :
977 0 : inline int shmimIntegrator::processImage( void *curr_src, const darkShmimT &dummy )
978 : {
979 : static_cast<void>( dummy ); // be unused
980 :
981 0 : realT *data = m_darkImage.data();
982 :
983 0 : for( unsigned nn = 0; nn < darkMonitorT::m_width * darkMonitorT::m_height; ++nn )
984 : {
985 : // data[nn] = *( (int16_t * ) (curr_src + nn*shmimMonitorT::m_typeSize));
986 0 : data[nn] = dark_pixget( curr_src, nn );
987 : }
988 :
989 0 : m_darkSet = true; // There is a dark set and ready to use, but it may or may not be valid.
990 :
991 0 : return 0;
992 : }
993 :
994 0 : inline int shmimIntegrator::allocate( const dark2ShmimT &dummy )
995 : {
996 : static_cast<void>( dummy ); // be unused
997 :
998 0 : std::unique_lock<std::mutex> lock( m_darkMutex ); // Lock the mutex before messing with the dark.
999 :
1000 0 : m_dark2Image.resize( dark2MonitorT::m_width, dark2MonitorT::m_height );
1001 0 : m_dark2Image.setZero();
1002 :
1003 0 : dark2_pixget = getPixPointer<realT>( dark2MonitorT::m_dataType );
1004 :
1005 0 : if( dark2_pixget == nullptr )
1006 : {
1007 0 : log<software_error>( { __FILE__, __LINE__, "bad data type" } );
1008 0 : m_dark2Set = false;
1009 0 : m_dark2Valid = false;
1010 0 : return -1;
1011 : }
1012 :
1013 0 : if( dark2MonitorT::m_width == shmimMonitorT::m_width || dark2MonitorT::m_height == shmimMonitorT::m_height )
1014 : {
1015 0 : m_dark2Valid = true;
1016 : }
1017 : else
1018 : {
1019 0 : m_dark2Valid = false;
1020 : }
1021 :
1022 0 : return 0;
1023 0 : }
1024 :
1025 0 : inline int shmimIntegrator::processImage( void *curr_src, const dark2ShmimT &dummy )
1026 : {
1027 : static_cast<void>( dummy ); // be unused
1028 :
1029 0 : realT *data = m_dark2Image.data();
1030 :
1031 0 : for( unsigned nn = 0; nn < dark2MonitorT::m_width * dark2MonitorT::m_height; ++nn )
1032 : {
1033 : // data[nn] = *( (int16_t * ) (curr_src + nn*shmimMonitorT::m_typeSize));
1034 0 : data[nn] = dark2_pixget( curr_src, nn );
1035 : }
1036 :
1037 0 : m_dark2Set = true; // There is a dark set and ready to use, but it may or may not be valid.
1038 :
1039 0 : return 0;
1040 : }
1041 :
1042 0 : inline int shmimIntegrator::findMatchingDark()
1043 : {
1044 0 : std::vector<std::string> fnames;
1045 :
1046 0 : mx::error_t errc = mx::ioutils::getFileNames(fnames, m_fileSaveDir, m_configName, "", ".fits" );
1047 :
1048 0 : if(errc != mx::error_t::noerror)
1049 : {
1050 0 : return log<text_log,-1>(std::format("Could not get list of darks: {} "
1051 0 : "({})", mx::errorMessage(errc), mx::errorName(errc)), logPrio::LOG_ERROR);
1052 : }
1053 :
1054 : // getFileNames sorts, so these will be in oldest to newest order by lexical timestamp sort
1055 : // So we search in reverse to always pick newest
1056 0 : long N = fnames.size();
1057 0 : for( long n = N - 1; n >= 0; --n )
1058 : {
1059 0 : std::string fn = mx::ioutils::pathStem( fnames[n] );
1060 :
1061 0 : if( fn.size() < m_configName.size() + 1 )
1062 0 : continue;
1063 :
1064 0 : size_t st = m_configName.size() + 1;
1065 0 : size_t ed = fn.find( "__T" );
1066 0 : if( ed == std::string::npos || ed - st < 2 )
1067 0 : continue;
1068 0 : std::string stateStr = fn.substr( st, ed - st );
1069 :
1070 0 : if( stateStr == m_stateString )
1071 : {
1072 0 : mx::fits::fitsFile<float> ff;
1073 0 : ff.read( m_avgImage, fnames[n] );
1074 :
1075 0 : if( m_avgImage.rows() != shmimMonitorT::m_width || m_avgImage.cols() != shmimMonitorT::m_height )
1076 : {
1077 : // Means the camera has changed but stream hasn't caught up
1078 : //(This happens on startup before stream connection completes.)
1079 :
1080 : // And possibly that we haven't turned the shmimMonitor on yet by switching to OPERATING
1081 0 : if( shmimMonitorT::m_width == 0 && shmimMonitorT::m_height == 0 )
1082 : {
1083 0 : sleep( 1 ); // wait for everything else to get initialized
1084 0 : shmimMonitorT::m_width = m_avgImage.rows();
1085 0 : shmimMonitorT::m_height = m_avgImage.cols();
1086 0 : m_reconfig = true;
1087 : }
1088 : else
1089 : {
1090 0 : if( m_running )
1091 0 : return 0;
1092 0 : m_imageValid = false;
1093 0 : m_stateStringChanged = true; // So we let appLogic try again next time around.
1094 0 : return 0;
1095 : }
1096 : }
1097 :
1098 0 : if( m_running )
1099 0 : return 0;
1100 :
1101 0 : m_updated = true;
1102 : // Now tell the f.g. to get going
1103 0 : if( sem_post( &m_smSemaphore ) < 0 )
1104 : {
1105 0 : log<software_critical>( { __FILE__, __LINE__, errno, 0, "Error posting to semaphore" } );
1106 0 : return -1;
1107 : }
1108 0 : m_imageValid = true;
1109 0 : m_stateStringChanged = false;
1110 :
1111 0 : log<text_log>( "loaded last matching dark from disk", logPrio::LOG_NOTICE );
1112 :
1113 0 : return 0;
1114 0 : }
1115 0 : }
1116 :
1117 0 : if( m_running )
1118 0 : return 0;
1119 :
1120 0 : m_imageValid = false;
1121 0 : m_stateStringChanged = false; // stop trying b/c who else is going to add a dark?
1122 :
1123 0 : log<text_log>( "dark is not valid", logPrio::LOG_WARNING );
1124 :
1125 0 : return 0;
1126 0 : }
1127 :
1128 0 : inline int shmimIntegrator::configureAcquisition()
1129 : {
1130 0 : std::unique_lock<std::mutex> lock( m_indiMutex );
1131 :
1132 : ///\todo potential but verrrrry unlikely bug: shmimMonitorT could change these before allocate sets the lock above.
1133 : ///Should use a local set of w/h instead.
1134 0 : if( shmimMonitorT::m_width == 0 || shmimMonitorT::m_height == 0 )
1135 : {
1136 : // This means we haven't connected to the stream to average. so wait.
1137 0 : lock.unlock(); // don't hold the lock for a whole second.
1138 0 : sleep( 1 );
1139 0 : return -1;
1140 : }
1141 :
1142 0 : frameGrabberT::m_width = shmimMonitorT::m_width;
1143 0 : frameGrabberT::m_height = shmimMonitorT::m_height;
1144 0 : frameGrabberT::m_dataType = _DATATYPE_FLOAT;
1145 :
1146 0 : return 0;
1147 0 : }
1148 :
1149 0 : inline int shmimIntegrator::startAcquisition()
1150 : {
1151 0 : return 0;
1152 : }
1153 :
1154 0 : inline int shmimIntegrator::acquireAndCheckValid()
1155 : {
1156 : timespec ts;
1157 :
1158 0 : if( clock_gettime( CLOCK_REALTIME, &ts ) < 0 )
1159 : {
1160 0 : log<software_critical>( { __FILE__, __LINE__, errno, 0, "clock_gettime" } );
1161 0 : return -1;
1162 : }
1163 :
1164 0 : ts.tv_sec += 1;
1165 :
1166 0 : if( sem_timedwait( &m_smSemaphore, &ts ) == 0 )
1167 : {
1168 0 : if( m_updated )
1169 : {
1170 0 : clock_gettime( CLOCK_REALTIME, &m_currImageTimestamp );
1171 0 : return 0;
1172 : }
1173 : else
1174 : {
1175 0 : return 1;
1176 : }
1177 : }
1178 : else
1179 : {
1180 0 : return 1;
1181 : }
1182 : }
1183 :
1184 0 : inline int shmimIntegrator::loadImageIntoStream( void *dest )
1185 : {
1186 0 : memcpy( dest, m_avgImage.data(), shmimMonitorT::m_width * shmimMonitorT::m_height * frameGrabberT::m_typeSize );
1187 0 : m_updated = false;
1188 0 : return 0;
1189 : }
1190 :
1191 0 : inline int shmimIntegrator::reconfig()
1192 : {
1193 0 : return 0;
1194 : }
1195 :
1196 0 : INDI_NEWCALLBACK_DEFN( shmimIntegrator, m_indiP_nAverage )( const pcf::IndiProperty &ipRecv )
1197 : {
1198 0 : if( ipRecv.getName() != m_indiP_nAverage.getName() )
1199 : {
1200 0 : log<software_error>( { __FILE__, __LINE__, "invalid indi property received" } );
1201 0 : return -1;
1202 : }
1203 :
1204 : unsigned target;
1205 :
1206 0 : if( indiTargetUpdate( m_indiP_nAverage, target, ipRecv, true ) < 0 )
1207 : {
1208 0 : log<software_error>( { __FILE__, __LINE__ } );
1209 0 : return -1;
1210 : }
1211 :
1212 0 : m_nAverage = target;
1213 :
1214 0 : if( m_avgTime > 0 && m_fps > 0 )
1215 : {
1216 0 : m_avgTime = m_nAverage / m_fps;
1217 : }
1218 :
1219 0 : updateIfChanged( m_indiP_nAverage, "current", m_nAverage, INDI_IDLE );
1220 0 : updateIfChanged( m_indiP_nAverage, "target", m_nAverage, INDI_IDLE );
1221 :
1222 0 : updateIfChanged( m_indiP_avgTime, "current", m_avgTime, INDI_IDLE );
1223 0 : updateIfChanged( m_indiP_avgTime, "target", m_avgTime, INDI_IDLE );
1224 :
1225 0 : shmimMonitorT::m_restart = true;
1226 :
1227 0 : log<text_log>( "set nAverage to " + std::to_string( m_nAverage ), logPrio::LOG_NOTICE );
1228 :
1229 0 : return 0;
1230 : }
1231 :
1232 0 : INDI_NEWCALLBACK_DEFN( shmimIntegrator, m_indiP_avgTime )( const pcf::IndiProperty &ipRecv )
1233 : {
1234 0 : if( ipRecv.getName() != m_indiP_avgTime.getName() )
1235 : {
1236 0 : log<software_error>( { __FILE__, __LINE__, "invalid indi property received" } );
1237 0 : return -1;
1238 : }
1239 :
1240 : float target;
1241 :
1242 0 : if( indiTargetUpdate( m_indiP_avgTime, target, ipRecv, true ) < 0 )
1243 : {
1244 0 : log<software_error>( { __FILE__, __LINE__ } );
1245 0 : return -1;
1246 : }
1247 :
1248 0 : m_avgTime = target;
1249 :
1250 0 : updateIfChanged( m_indiP_avgTime, "current", m_avgTime, INDI_IDLE );
1251 0 : updateIfChanged( m_indiP_avgTime, "target", m_avgTime, INDI_IDLE );
1252 :
1253 0 : shmimMonitorT::m_restart = true;
1254 :
1255 0 : log<text_log>( "set avgTime to " + std::to_string( m_avgTime ), logPrio::LOG_NOTICE );
1256 :
1257 0 : return 0;
1258 : }
1259 :
1260 0 : INDI_NEWCALLBACK_DEFN( shmimIntegrator, m_indiP_nUpdate )( const pcf::IndiProperty &ipRecv )
1261 : {
1262 0 : if( ipRecv.getName() != m_indiP_nUpdate.getName() )
1263 : {
1264 0 : log<software_error>( { __FILE__, __LINE__, "invalid indi property received" } );
1265 0 : return -1;
1266 : }
1267 :
1268 : unsigned target;
1269 :
1270 0 : if( indiTargetUpdate( m_indiP_nUpdate, target, ipRecv, true ) < 0 )
1271 : {
1272 0 : log<software_error>( { __FILE__, __LINE__ } );
1273 0 : return -1;
1274 : }
1275 :
1276 0 : m_nUpdate = target;
1277 :
1278 0 : updateIfChanged( m_indiP_nUpdate, "current", m_nUpdate, INDI_IDLE );
1279 0 : updateIfChanged( m_indiP_nUpdate, "target", m_nUpdate, INDI_IDLE );
1280 :
1281 0 : shmimMonitorT::m_restart = true;
1282 :
1283 0 : log<text_log>( "set nUpdate to " + std::to_string( m_nUpdate ), logPrio::LOG_NOTICE );
1284 :
1285 0 : return 0;
1286 : }
1287 :
1288 0 : INDI_NEWCALLBACK_DEFN( shmimIntegrator, m_indiP_startAveraging )( const pcf::IndiProperty &ipRecv )
1289 : {
1290 0 : if( ipRecv.getName() != m_indiP_startAveraging.getName() )
1291 : {
1292 0 : log<software_error>( { __FILE__, __LINE__, "invalid indi property received" } );
1293 0 : return -1;
1294 : }
1295 :
1296 0 : if( !ipRecv.find( "toggle" ) )
1297 0 : return 0;
1298 :
1299 0 : if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off )
1300 : {
1301 0 : std::unique_lock<std::mutex> lock( m_indiMutex );
1302 :
1303 0 : m_running = false;
1304 :
1305 0 : state( stateCodes::READY );
1306 :
1307 0 : updateSwitchIfChanged( m_indiP_startAveraging, "toggle", pcf::IndiElement::Off, INDI_IDLE );
1308 0 : }
1309 :
1310 0 : if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
1311 : {
1312 0 : std::unique_lock<std::mutex> lock( m_indiMutex );
1313 :
1314 0 : if( m_fileSaver && !m_continuous )
1315 0 : m_stateStringChanged = false; // We reset this here so we can detect a change at the end of the integration
1316 :
1317 0 : m_stateStringValidOnStart = m_stateStringValid;
1318 0 : m_running = true;
1319 :
1320 0 : state( stateCodes::OPERATING );
1321 :
1322 0 : updateSwitchIfChanged( m_indiP_startAveraging, "toggle", pcf::IndiElement::On, INDI_BUSY );
1323 0 : }
1324 0 : return 0;
1325 : }
1326 :
1327 0 : INDI_SETCALLBACK_DEFN( shmimIntegrator, m_indiP_fpsSource )( const pcf::IndiProperty &ipRecv )
1328 : {
1329 0 : if( ipRecv.getName() != m_indiP_fpsSource.getName() )
1330 : {
1331 0 : log<software_error>( { __FILE__, __LINE__, "Invalid INDI property." } );
1332 0 : return -1;
1333 : }
1334 :
1335 0 : if( ipRecv.find( "current" ) != true ) // this isn't valie
1336 : {
1337 0 : return 0;
1338 : }
1339 :
1340 0 : std::lock_guard<std::mutex> guard( m_indiMutex );
1341 :
1342 0 : realT fps = ipRecv["current"].get<float>();
1343 :
1344 0 : if( fps != m_fps )
1345 : {
1346 0 : m_fps = fps;
1347 0 : std::cout << "Got fps: " << m_fps << "\n";
1348 0 : shmimMonitorT::m_restart = true;
1349 0 : frameGrabberT::m_reconfig = true;
1350 : }
1351 :
1352 0 : return 0;
1353 0 : }
1354 :
1355 0 : INDI_SETCALLBACK_DEFN( shmimIntegrator, m_indiP_stateSource )( const pcf::IndiProperty &ipRecv )
1356 : {
1357 0 : if( ipRecv.getName() != m_indiP_stateSource.getName() )
1358 : {
1359 0 : log<software_error>( { __FILE__, __LINE__, "Invalid INDI property." } );
1360 0 : return -1;
1361 : }
1362 :
1363 0 : if( ipRecv.find( "valid" ) == true )
1364 : {
1365 : bool stateStringValid;
1366 0 : if( ipRecv["valid"].get<std::string>() == "yes" )
1367 0 : stateStringValid = true;
1368 : else
1369 0 : stateStringValid = false;
1370 :
1371 0 : if( stateStringValid != m_stateStringValid )
1372 0 : m_stateStringChanged = true;
1373 :
1374 0 : m_stateStringValid = stateStringValid;
1375 : }
1376 :
1377 0 : if( ipRecv.find( "current" ) != true )
1378 : {
1379 0 : return 0;
1380 : }
1381 :
1382 0 : std::lock_guard<std::mutex> guard( m_indiMutex );
1383 :
1384 0 : std::string ss = ipRecv["current"].get<std::string>();
1385 :
1386 0 : if( ss != m_stateString )
1387 : {
1388 0 : m_stateString = ss;
1389 0 : m_imageValid = false; // This will mark the current dark invalid
1390 0 : updateIfChanged( m_indiP_imageValid, "flag", "no" );
1391 0 : m_stateStringChanged = true; // We declare it changed. This can have two effects:
1392 : // 1) if we are not currently integrating, it will start a lookup in appLogic
1393 : // 2) if we are integrating, after it finishes it will not be declared valid and
1394 : // then we'll lookup in appLogic
1395 : }
1396 :
1397 0 : return 0;
1398 0 : }
1399 :
1400 0 : inline int shmimIntegrator::checkRecordTimes()
1401 : {
1402 0 : return telemeterT::checkRecordTimes( telem_fgtimings() );
1403 : }
1404 :
1405 0 : inline int shmimIntegrator::recordTelem( const telem_fgtimings * )
1406 : {
1407 0 : return recordFGTimings( true );
1408 : }
1409 :
1410 : } // namespace app
1411 : } // namespace MagAOX
1412 :
1413 : #endif // shmimIntegrator_hpp
|