API
 
Loading...
Searching...
No Matches
psfAcq.hpp
Go to the documentation of this file.
1/** \file psfAcq.hpp
2 * \brief The MagAO-X PSF Fitter application header
3 *
4 * \ingroup psfAcq_files
5 */
6
7#ifndef psfAcq_hpp
8#define psfAcq_hpp
9
10#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
11#include "../../magaox_git_version.h"
12
13#include <mx/math/fit/fitGaussian.hpp>
14#include <mx/improc/imageFilters.hpp>
15
16/** \defgroup psfAcq
17 * \brief The MagAO-X PSF fitter.
18 *
19 * <a href="../handbook/operating/software/apps/psfAcq.html">Application Documentation</a>
20 *
21 * \ingroup apps
22 *
23 */
24
25/** \defgroup psfAcq_files
26 * \ingroup psfAcq
27 */
28
29namespace MagAOX
30{
31namespace app
32{
33
34struct darkShmimT
35{
36 static std::string configSection()
37 {
38 return "darkShmim";
39 };
40
41 static std::string indiPrefix()
42 {
43 return "dark";
44 };
45};
46
47struct Star
48{
49public:
50
51 float x, y; // Star coordinates
52 float max; // Star brightness
53 float fwhm; // Star FWHM
54 float seeing; // Star's seeing
55
56private:
57
58 pcf::IndiProperty * m_prop {nullptr};
59
60public:
61
62 pcf::IndiProperty & prop()
63 {
64 if(m_prop == nullptr)
65 {
66 throw std::runtime_error("attempt to access nullptr prop");
67 }
68
69 return *m_prop;
70 }
71
72 void allocate()
73 {
74 m_prop = new pcf::IndiProperty;
75 }
76
78 {
79 pcf::IndiProperty * mp = m_prop;
80 m_prop = nullptr;
81 delete mp;
82 }
83
84
85};
86
87/// The MagAO-X PSF Fitter
88/**
89 * \ingroup psfAcq
90 */
91class psfAcq : public MagAOXApp<true>,
92 public dev::shmimMonitor<psfAcq>,
93 public dev::shmimMonitor<psfAcq, darkShmimT>
94{
95 // Give the test harness access.
96 friend class psfAcq_test;
97
98 friend class dev::shmimMonitor<psfAcq>;
99 friend class dev::shmimMonitor<psfAcq, darkShmimT>;
100
101 public:
102 // The base shmimMonitor type
104
106
107 /// Floating point type in which to do all calculations.
108 typedef float realT;
109
110 /** \name app::dev Configurations
111 *@{
112 */
113 ///@}
114
115 protected:
116 /** \name Configurable Parameters
117 *@{
118 */
119
120 std::string m_fpsSource; ///< Device name for getting fps if time-based averaging is used. This device should have
121 ///< *.fps.current.
122
123 uint16_t m_fitCircBuffMaxLength{ 3600 }; ///< Maximum length of the latency measurement circular buffers
124 float m_fitCircBuffMaxTime{ 5 }; ///< Maximum time of the latency meaurement circular buffers
125
126 float m_fwhmGuess{ 4 };
127 ///@}
128
129 mx::improc::eigenImage<float> m_image;
130 mx::improc::eigenImage<float> m_sm;
131
132 mx::improc::eigenImage<float> m_dark;
133
134 double m_acqQuitTime {0};
135 double m_acqPauseTime{2};
136
139 bool m_updated{ false };
140 float m_x{ 0 };
141 float m_y{ 0 };
142 int m_max_loops{ 5 }; // default to detecting a max of 5 stars
143 int m_zero_area{ 8 }; // default to zeroing an 8x8 pixel area around stars it finds
144 float m_threshold = { 7.0 }; // how many sigma away from the mean you want to classify a detection, default to
145 // 7sigma
146 float m_fwhm_threshold = { 4.0 }; // minumum fwhm to consider something a star
147 float m_max_fwhm = { 40.0 }; // max fwhm to consider a star
148
149 std::vector<float> m_first_x_vals = {};
150 std::vector<float> m_first_y_vals = {};
151 std::vector<Star> m_detectedStars; // vector to store all the stars and properties
152
153 float m_dx{ 0 };
154 float m_dy{ 0 };
155
156 double m_plate_scale = .0795336;
158 int m_num_stars{ 0 };
159 float m_seeing{ 0 };
160 int m_acquire_star{ -1 }; // Testing for user to select star
161 int m_seeing_star { -1 }; // default star to calc seeing
162 int m_x_center{}; // 'center' of image or hot spot
164
165 float m_fps{ 0 };
166
167 void resetAcq(); // class member for resetAcq function
168
169 // Working memory for poke fitting
170 mx::math::fit::fitGaussian2Dsym<float> m_gfit;
171
172 public:
173 /// Default c'tor.
174 psfAcq();
175
176 /// D'tor, declared and defined for noexcept.
178
179 virtual void setupConfig();
180
181 /// Implementation of loadConfig logic, separated for testing.
182 /** This is called by loadConfig().
183 */
184 int loadConfigImpl(
185 mx::app::appConfigurator &_config /**< [in] an application configuration from which to load values*/ );
186
187 virtual void loadConfig();
188
189 /// Startup function
190 /**
191 *
192 */
193 virtual int appStartup();
194
195 /// Implementation of the FSM for psfAcq.
196 /**
197 * \returns 0 on no critical error
198 * \returns -1 on an error requiring shutdown
199 */
200 virtual int appLogic();
201
202 /// Shutdown the app.
203 /**
204 *
205 */
206 virtual int appShutdown();
207
208 // shmimMonitor interface:
209 int allocate( const dev::shmimT & );
210
211 int processImage( void *curr_src, const dev::shmimT & );
212
213 // shmimMonitor interface for referenc:
214 int allocate( const darkShmimT & );
215
216 int processImage( void *curr_src, const darkShmimT & );
217
218 protected:
219 std::mutex m_imageMutex;
220
221 protected:
222 /** \name INDI
223 * @{
224 */
225
227
229
232
233 // For user to select star
236
237 // For user to set what star they want to use to calc seeing
240
241 // toggling
244
247
248 ///@}
249
250 /** \name Telemeter Interface
251 *
252 * @{
253 */
255
257
258 ///@}
259};
260
266
268{
269 for(size_t n = 0; n < m_detectedStars.size(); ++n)
270 {
271 m_detectedStars[n].deallocate();
272 }
273}
274
276{
279
280 config.add(
281 "fitter.fpsSource",
282 "",
283 "fitter.fpsSource",
284 argType::Required,
285 "fitter",
286 "fpsSource",
287 false,
288 "string",
289 "Device name for getting fps if time-based averaging is used. This device should have *.fps.current." );
290 config.add( "fitter.max_loops",
291 "",
292 "fitter.max_loops",
293 argType::Required,
294 "fitter",
295 "max_loops",
296 false,
297 "int",
298 "Setting the number of stars to detect in processImage function." );
299 config.add( "fitter.zero_area",
300 "",
301 "fitter.zero_area",
302 argType::Required,
303 "fitter",
304 "zero_area",
305 false,
306 "int",
307 "Setting the pixel area to zero out after detecting stars in processImage function." );
308 config.add( "fitter.threshold",
309 "",
310 "fitter.threshold",
311 argType::Required,
312 "fitter",
313 "threshold",
314 false,
315 "float",
316 "setting how many sigma away from the mean you want to classify a detection." );
317 config.add( "fitter.fwhm_threshold",
318 "",
319 "fitter.fwhm_threshold",
320 argType::Required,
321 "fitter",
322 "fwhm_threshold",
323 false,
324 "float",
325 "minumum fwhm to consider something a star." );
326 config.add( "acquisition.x_center",
327 "",
328 "acquisition.x_center",
329 argType::Required,
330 "acquisition",
331 "x_center",
332 false,
333 "int",
334 "X value for 'center' of image." );
335 config.add( "acquisition.y_center",
336 "",
337 "acquisition.y_center",
338 argType::Required,
339 "acquisition",
340 "y_center",
341 false,
342 "int",
343 "Y value for 'center' of image." );
344}
345
346inline int psfAcq::loadConfigImpl( mx::app::appConfigurator &_config )
347{
350
351 _config( m_fpsSource, "fitter.fpsSource" );
352 _config( m_max_loops, "fitter.max_loops" ); // Max number of stars to detect in processImage
353 _config( m_zero_area, "fitter.zero_area" ); // pixel area to zero out in processImage when a star is detected
354 _config( m_threshold, "fitter.threshold" ); // how many sigma away from the mean you want to classify a detection
356 "fitter.fwhm_threshold" ); // how many sigma away from the mean you want to classify a detection
357
358 _config( m_x_center, "acquisition.x_center" ); // star number to acquire
359 _config( m_y_center, "acquisition.y_center" ); // star number to acquire
360
361 return 0;
362}
363
365{
366 loadConfigImpl( config );
367}
368
370{
371 if( shmimMonitorT::appStartup() < 0 )
372 {
373 return log<software_error, -1>( { __FILE__, __LINE__ } );
374 }
375
377 {
378 return log<software_error, -1>( { __FILE__, __LINE__ } );
379 }
380
381 if( m_fpsSource != "" )
382 {
383 REG_INDI_SETPROP( m_indiP_fpsSource, m_fpsSource, std::string( "fps" ) );
384 }
385
386 // creating toggling to restart the acquisition
387 createStandardIndiRequestSw( m_indiP_restartAcq, "restart_acq", "Restart Acquisition", "psfAcq");
389
390 // INDI prop for seeing
392 m_indiP_seeing.add(pcf::IndiElement("current"));
393 m_indiP_seeing["current"].setValue( m_seeing ); //m_seeing gets assigned the seeing values of the star that is acquired
395
396 // create toggling for recording seeing
397 createStandardIndiToggleSw( m_indiP_recordSeeing, "record_seeing", "Record Seeing");
398 m_indiP_recordSeeing["toggle"].set(pcf::IndiElement::Off);
400 {
402 return -1;
403 }
404
405 // INDI prop for user to select star
406 CREATE_REG_INDI_NEW_NUMBERF( m_indiP_acquire_star, "acquire_star", 0, 20, 1, "%d", "", "" );
407 m_indiP_acquire_star["current"].setValue( m_acquire_star );
408 m_indiP_acquire_star["target"].setValue( m_acquire_star );
409
410 // INDI prop for user to select seeing star
411 CREATE_REG_INDI_NEW_NUMBERF( m_indiP_seeing_star, "seeing_star", 0, 20, 1, "%d", "", "" );
412 m_indiP_seeing_star["current"].setValue( m_seeing_star );
413 m_indiP_seeing_star["target"].setValue( m_seeing_star );
414
415 // number of stars INDI prop
417 m_indiP_num_stars.add(pcf::IndiElement("current"));
418 m_indiP_num_stars["current"].setValue( m_num_stars );
420
422
423 return 0;
424}
425
427{
428 if( shmimMonitorT::appLogic() < 0 )
429 {
430 return log<software_error, -1>( { __FILE__, __LINE__ } );
431 }
432
434 {
435 return log<software_error, -1>( { __FILE__, __LINE__ } );
436 }
437
438 std::unique_lock<std::mutex> lock( m_indiMutex );
439
442
445
446 for( size_t n = 0; n < m_detectedStars.size() ; ++n )
447 {
448
451 updateIfChanged( m_detectedStars[n].prop(), "peak", m_detectedStars[n].max );
452 updateIfChanged( m_detectedStars[n].prop(), "fwhm", m_detectedStars[n].fwhm );
453 }
454 return 0;
455}
456
458{
461
462 return 0;
463}
464
466{
467 static_cast<void>( dummy );
468
469 std::lock_guard<std::mutex> guard( m_imageMutex );
470
472 m_image.setZero();
473
474 m_sm.resize( m_image.rows(), m_image.cols() );
475
476 m_updated = false;
477 return 0;
478}
479
480// Function to calculate Euclidean distance between two stars
481float calculateDistance( float x1, float y1, float x2, float y2 )
482{
483 return sqrt( ( x2 - x1 ) * ( x2 - x1 ) + ( y2 - y1 ) * ( y2 - y1 ) );
484}
485
487{
488 if(mx::sys::get_curr_time() - m_acqQuitTime < m_acqPauseTime ) return 0; // Pausing while telescope moves to star
489 static_cast<void>( dummy );
490
491 std::unique_lock<std::mutex> lock( m_imageMutex );
492
493 if( m_dark.rows() == m_image.rows() && m_dark.cols() == m_image.cols() )
494 {
495 for( unsigned nn = 0; nn < shmimMonitorT::m_width * shmimMonitorT::m_height; ++nn )
496 {
497 m_image.data()[nn] = ( (float *)curr_src )[nn] - m_dark.data()[nn];
498 }
499 }
500 else
501 {
502 for( unsigned nn = 0; nn < shmimMonitorT::m_width * shmimMonitorT::m_height; ++nn )
503 {
504 m_image.data()[nn] = ( (float *)curr_src )[nn];
505 }
506 }
507
508 lock.unlock();
509 float max;
510 float seeing = 0;
511 // int max_loops=5;
512 int x = 0;
513 int y = 0;
514 int N_loops = 0;
515 // 1. find brightest star
516 max = m_image.maxCoeff( &x, &y );
517
518 std::cerr << __LINE__ << " " << max << " " << x << " " << y << "\n";
519
520 // mx::improc::medianSmooth(m_sm, x, y, max, m_image, 3);
521
522 // mx::improc::imageCenterOfLight(m_x, m_y, m_image);
523 // std::cerr << __LINE__ << std::endl;
524
525 /*if(fabs(m_x-x) > 2 || fabs(m_y-y) > 2)
526 {
527 std::cerr << "skip frame\n";
528 return 0;
529 }*/
530
531 eigenImage<float> llcorn = m_image.block( 0, 0, 32, 32 ); // calc std dev of 32x32 block in lower left corner
532 float mean = llcorn.mean(); // Calculate the mean
533 float variance = ( llcorn.array() - mean ).square().sum() / ( llcorn.size() ); // calculate variance
534 float stddev = std::sqrt( variance ); // Calculate the standard deviation
535 float z_score = ( max - mean ) / stddev; // how many std dev away from mean
536 float fwhm = m_fwhm_threshold + 1; // getting intial fwhm before entering while loop
537 std::size_t numStars = m_detectedStars.size();
538 if( numStars == 0 )
539 { // This runs when the vector of stars is empty (usually the first time)
540 while( ( z_score > m_threshold ) && ( fwhm > m_fwhm_threshold ) && ( N_loops < m_max_loops ) )
541 { // m_max_loops, m_fwhm_threshold, and m_threshold are configurable variables
542 N_loops = N_loops + 1;
543 m_gfit.set_itmax( 1000 );
544 // m_zero_area is used to zero out the pixel array once a star is detected but can also be used to set up a
545 // sub image around the max pixel
546 if( x < m_zero_area )
547 {
548 x = m_zero_area;
549 }
550 if( x >= ( m_image.rows() - m_zero_area ) )
551 {
552 x = m_image.rows() - m_zero_area;
553 }
554 if( y < m_zero_area )
555 {
556 y = m_zero_area;
557 }
558 if( y >= ( m_image.cols() - m_zero_area ) )
559 {
560 y = m_image.cols() - m_zero_area;
561 }
563 x - m_zero_area,
564 y - m_zero_area,
565 m_zero_area * 2,
566 m_zero_area * 2 ); // set m_image to subImage to speed up gaussian, x,y is position of max pixel
567 m_gfit.setArray( subImage.data(), subImage.rows(), subImage.cols() );
568 // m_gfit.setArray(m_image.data(), m_image.rows(), m_image.cols());
569 m_gfit.setGuess( 0, max, m_zero_area, m_zero_area, mx::math::func::fwhm2sigma( m_fwhmGuess ) );
570 m_gfit.fit();
571 m_x = ( x - m_zero_area ) + m_gfit.x0();
572 m_y = ( y - m_zero_area ) + m_gfit.y0();
573 m_first_x_vals.push_back( m_x ); // adding first detected x value to vector
574 m_first_y_vals.push_back( m_y ); // adding first detected y value to vector
575 fwhm = m_gfit.fwhm() ;
576 max = m_gfit.G();
577 seeing = fwhm * m_plate_scale;
578 int x_value = static_cast<int>(
579 m_x ); // convert m_x to an int so we can 0 out a rectangular area around the detected star
580 int y_value = static_cast<int>( m_y );
581 if (fwhm > m_fwhm_threshold && fwhm < m_max_fwhm ){
583 newStar.x = m_x; // Adding attributes to the new star
584 newStar.y = m_y;
585 newStar.max = max;
586 newStar.fwhm = fwhm;
587 newStar.seeing = seeing;
588 std::unique_lock<std::mutex> lock( m_indiMutex );
589 newStar.allocate();
590 m_detectedStars.push_back( newStar );
591 int index = m_detectedStars.size() - 1;
592 std::string starPrefix = "star_" + std::to_string( index );
593 createROIndiNumber(m_detectedStars.back().prop(), starPrefix); //, "Star " + std::to_string( m_detectedStars.size() ) + " Properties", "Star Acq" );
594 m_detectedStars.back().prop().add( pcf::IndiElement( "x" ) );
595 m_detectedStars.back().prop()["x"].set( m_detectedStars.back().x );
596 m_detectedStars.back().prop().add( pcf::IndiElement( "y" ) );
597 m_detectedStars.back().prop()["y"].set( m_detectedStars.back().y );
598 m_detectedStars.back().prop().add( pcf::IndiElement( "peak" ) );
599 m_detectedStars.back().prop()["peak"].set( m_detectedStars.back().max );
600 m_detectedStars.back().prop().add( pcf::IndiElement( "fwhm" ) );
601 m_detectedStars.back().prop()["fwhm"].set( m_detectedStars.back().fwhm );
603 }
604 if( x_value < m_zero_area )
605 {
607 }
608 if( x_value >= ( m_image.rows() - m_zero_area ) )
609 {
610 x_value = m_image.rows() - m_zero_area;
611 }
612 if( y_value < m_zero_area )
613 {
615 }
616 if( y_value >= ( m_image.cols() - m_zero_area ) )
617 {
618 y_value = m_image.cols() - m_zero_area;
619 }
620 for( int i = x_value - m_zero_area; i < ( x_value + m_zero_area ); i++ )
621 { // zeroing out area around the star centered at m_x and m_y(8x8 pixel area)
622 for( int j = y_value - m_zero_area; j < ( y_value + m_zero_area ); j++ )
623 {
624 m_image( i, j ) = 0; // m_zero_area is defaulted to 20 to zero out a pixel array around the star
625 }
626 }
627 max = m_image.maxCoeff( &x, &y );
628 z_score = ( max - mean ) / stddev;
629 }
630 }
631
632 else
633 {
634 // In here is where we track the stars using cross correlation between the first frame and subsequent frames
635 while( ( z_score > m_threshold ) && ( fwhm > m_fwhm_threshold ) && ( N_loops < m_max_loops ) )
636 {
637 N_loops = N_loops + 1;
638 m_gfit.set_itmax( 1000 );
639 // m_zero_area is used to zero out the pixel array once a star is detected but can also be used to set up a
640 // sub image around the max pixel
641 if( x < m_zero_area )
642 {
643 x = m_zero_area;
644 }
645 if( x >= ( m_image.rows() - m_zero_area ) )
646 {
647 x = m_image.rows() - m_zero_area;
648 }
649 if( y < m_zero_area )
650 {
651 y = m_zero_area;
652 }
653 if( y >= ( m_image.cols() - m_zero_area ) )
654 {
655 y = m_image.cols() - m_zero_area;
656 }
658 x - m_zero_area,
659 y - m_zero_area,
660 m_zero_area * 2,
661 m_zero_area * 2 ); // set m_image to subImage to speed up gaussian, x,y is position of max pixel
662 // 2. fit it's position
663 m_gfit.setArray( subImage.data(), subImage.rows(), subImage.cols() );
664 m_gfit.setGuess( 0, max, m_zero_area, m_zero_area, mx::math::func::fwhm2sigma( m_fwhmGuess ) );
665 m_gfit.fit();
666 fwhm = m_gfit.fwhm() ;
667 max = m_gfit.G();
668 seeing = fwhm * m_plate_scale;
669 m_x = ( x - m_zero_area ) + m_gfit.x0();
670 m_y = ( y - m_zero_area ) + m_gfit.y0();
671 int x_value = static_cast<int>(
672 m_x ); // convert m_x to an int so we can 0 out a rectangular area around the detected star
673 int y_value = static_cast<int>( m_y );
674 if( x_value < m_zero_area )
675 {
677 }
678 if( x_value >= ( m_image.rows() - m_zero_area ) )
679 {
680 x_value = m_image.rows() - m_zero_area;
681 }
682 if( y_value < m_zero_area )
683 {
685 }
686 if( y_value >= ( m_image.cols() - m_zero_area ) )
687 {
688 y_value = m_image.cols() - m_zero_area;
689 }
690 for( int i = x_value - m_zero_area; i < ( x_value + m_zero_area ); i++ )
691 { // zeroing out area around the star centered at m_x and m_y(8x8 pixel area)
692 for( int j = y_value - m_zero_area; j < ( y_value + m_zero_area ); j++ )
693 {
694 m_image( i, j ) = 0; // m_zero_area is defaulted to 20 to zero out a pixel array around the star
695 }
696 }
697 // 3. search through all known stars to figure out which one it corresponds to. You can NOT assume it is in the order of the vector.
698 // This simple for loop calculate the distance from the detected star to the cloest star already in the list
699 // and updates the values
700 int threshold_distance = 20; // distance between new stars should be a small positive number so this updates
701 int tracker = 0; // tracks if the current star detected updated an already known star
702 if (fwhm > m_fwhm_threshold && fwhm < m_max_fwhm ){
703 for( Star &star : m_detectedStars )
704 {
705 float dist = calculateDistance( star.x, star.y, x_value, y_value );
706 // 4. if it is found, update that star's data in the vector
708 {
709 star.x = m_x;
710 star.y = m_y;
711 star.max = max;
712 star.fwhm = fwhm;
713 star.seeing = seeing;
714 tracker = 1;
715 continue;
716 }
717 }
718 if (tracker == 0 && m_detectedStars.size() < m_max_loops) { // 5. if it is not found, add a star and corresponding INDI Property using push_back
720 newStar.x = m_x; // Adding attributes to the new star
721 newStar.y = m_y;
722 newStar.max = max;
723 newStar.fwhm = fwhm;
724 newStar.seeing = seeing;
725 std::unique_lock<std::mutex> lock( m_indiMutex );
726 newStar.allocate();
727 m_detectedStars.push_back( newStar );
728 int index = m_detectedStars.size() - 1;
729 std::string starPrefix = "star_" + std::to_string( index );
730 createROIndiNumber(m_detectedStars.back().prop(), starPrefix, "Star " + std::to_string( index ) + " Properties", "Star Acq" );
731 m_detectedStars.back().prop().add( pcf::IndiElement( "x" ) );
732 m_detectedStars.back().prop()["x"].set( m_detectedStars.back().x );
733 m_detectedStars.back().prop().add( pcf::IndiElement( "y" ) );
734 m_detectedStars.back().prop()["y"].set( m_detectedStars.back().y );
735 m_detectedStars.back().prop().add( pcf::IndiElement( "peak" ) );
736 m_detectedStars.back().prop()["peak"].set( m_detectedStars.back().max );
737 m_detectedStars.back().prop().add( pcf::IndiElement( "fwhm" ) );
738 m_detectedStars.back().prop()["fwhm"].set( m_detectedStars.back().fwhm );
740 }
741 }
742 max = m_image.maxCoeff( &x, &y );
743 z_score = ( max - mean ) / stddev;
744 }
745 }
746
748 // If statement that get the delta x and delta y from the 'center' of image
749 static int delta_x;
750 static int delta_y;
751 double plate_scale = .0795336; // plate scale factor: deltatheta/deltapixel, calculated in python, arcsec/pixel
752 //deltatheta -> Angular seperation between two stars in arcsec (from published data)
753 //deltapixel -> Pixel seperation between same two stars on our detector
754 if ( m_acquire_star != -1 && (m_acquire_star > m_detectedStars.size() - 1 || m_acquire_star < 0 )){
755 std::cout << "Please enter a star number between 0 and " << m_detectedStars.size() - 1 << "." << std::endl;
756 m_acquire_star = -1;
757 }
758
759 if ( m_seeing_star != -1 && (m_seeing_star > m_detectedStars.size() - 1 || m_seeing_star < 0 )){
760 std::cout << "Please enter a star number between 0 and " << m_detectedStars.size() - 1 << "." << std::endl;
761 m_seeing_star = -1;
762 }
763
764 if( m_acquire_star >= 0 && m_acquire_star < m_detectedStars.size())
765 {
766 m_acqQuitTime = mx::sys::get_curr_time();
770 std::cout << "delta_x = " << delta_x << " delta_y = " << delta_y << std::endl;
771
772 // negative signs because we want to move scope opposite of how far it is from 'center'
773 double x_arcsec = -1*delta_y * plate_scale; //positive x_arcsec moves up, negetive moves down
774 double y_arcsec = -1*delta_x * plate_scale; //positive y_arcsec moves right, negetive moves left
775 std::cout << "x_arcsec=" << x_arcsec << " y_arcsec=" << y_arcsec << std::endl;
776
777 // for moving telescope
778 pcf::IndiProperty ip( pcf::IndiProperty::Number );
779
780 ip.setDevice( "tcsi" );
781 ip.setName( "pyrNudge" );
782 //send telescope x and y offsets in acrsec
783 ip.add( pcf::IndiElement( "y" ) );
784 ip["y"] = x_arcsec; //how far to move in y direction in arcsec?
785 ip.add( pcf::IndiElement( "x" ) );
786 ip["x"] = y_arcsec; //how far to move in x direction in arcsec?
787
789 resetAcq();
790 m_acquire_star = -1;
791 }
792
795 }
796
797 m_updated = true;
798 return 0;
799}
800
802{
803 static_cast<void>( dummy );
804
805 std::lock_guard<std::mutex> guard( m_imageMutex );
806
808 {
809 return log<software_error, -1>( { __FILE__, __LINE__, "dark is not float" } );
810 }
811
813 m_dark.setZero();
814
815 return 0;
816}
817
819{
820 static_cast<void>( dummy );
821
822 std::unique_lock<std::mutex> lock( m_imageMutex );
823
825 {
826 m_dark.data()[nn] += ( (float *)curr_src )[nn];
827 }
828
829 lock.unlock();
830
831 log<text_log>( "dark updated", logPrio::LOG_INFO );
832
833 return 0;
834}
835
836//delete m_detectedStars Properties
838 for(size_t n=0; n < m_detectedStars.size(); ++n)
839 {
840 if(m_indiDriver) m_indiDriver->sendDelProperty(m_detectedStars[n].prop());
841 if(!m_indiNewCallBacks.erase(m_detectedStars[n].prop().createUniqueKey()))
842 {
843 log<software_error>({__FILE__, __LINE__, "failed to erase " + m_detectedStars[n].prop().createUniqueKey()});
844 }
845 }
846 std::cout << "size=" << m_detectedStars.size() << std::endl;
847 m_detectedStars.clear();
848}
849
850//for toggling Restart Acquisition
852{
854 if(!ipRecv.find("request")) return 0;
855 std::unique_lock<std::mutex> lock(m_indiMutex);
856
857 if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
858 {
859 std::cout << "size=" << m_detectedStars.size() << std::endl;
860 resetAcq();
861 std::cout << "size=" << m_detectedStars.size() << std::endl;
862 return 0;
863 }
864 else if( ipRecv["request"].getSwitchState() == pcf::IndiElement::Off)
865 {
866 return 0;
867 }
868
869 log<software_error>({__FILE__,__LINE__, "switch state fall through."});
870 return -1;
871}
872
873//for toggling Recording Seeing
875{
877 if(!ipRecv.find("toggle")) return 0;
878 std::unique_lock<std::mutex> lock(m_indiMutex);
879
880 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
881 {
883 updateSwitchIfChanged(m_indiP_recordSeeing, "toggle", pcf::IndiElement::On, INDI_BUSY);
884 //m_seeing = m_detectedStars[m_current_acq_star].seeing;
885 return 0;
886 }
887 else if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
888 {
890 updateSwitchIfChanged(m_indiP_recordSeeing, "toggle", pcf::IndiElement::Off, INDI_IDLE);
891 return 0;
892 }
893
894
895 log<software_error>({__FILE__,__LINE__, "switch state fall through."});
896 return -1;
897}
898
899// For user to select acquisition star number
901{
902 if( ipRecv.getName() != m_indiP_acquire_star.getName() )
903 {
904 log<software_error>( { __FILE__, __LINE__, "wrong INDI property received." } );
905 return -1;
906 }
907
908 float target;
909
910 if( indiTargetUpdate( m_indiP_acquire_star, target, ipRecv, true ) < 0 )
911 {
913 return -1;
914 }
915
916 m_acquire_star = target;
917
918 log<text_log>( "set acquire_star = " + std::to_string( m_acquire_star ), logPrio::LOG_NOTICE );
919 return 0;
920}
921
922// For user to select seeing star number
924{
925 if( ipRecv.getName() != m_indiP_seeing_star.getName() )
926 {
927 log<software_error>( { __FILE__, __LINE__, "wrong INDI property received." } );
928 return -1;
929 }
930
931 float target;
932
933 if( indiTargetUpdate( m_indiP_seeing_star, target, ipRecv, true ) < 0 )
934 {
936 return -1;
937 }
938
939 m_seeing_star = target;
940
941 log<text_log>( "set seeing_star = " + std::to_string( m_seeing_star ), logPrio::LOG_NOTICE );
942 return 0;
943}
944
946{
947 if( ipRecv.getName() != m_indiP_fpsSource.getName() )
948 {
949 log<software_error>( { __FILE__, __LINE__, "Invalid INDI property." } );
950 return -1;
951 }
952
953 if( ipRecv.find( "current" ) != true ) // this isn't valie
954 {
955 return 0;
956 }
957
958 std::lock_guard<std::mutex> guard( m_indiMutex );
959
960 realT fps = ipRecv["current"].get<float>();
961
962 if( fps != m_fps )
963 {
964 m_fps = fps;
966 }
967
968 return 0;
969}
970
971} // namespace app
972} // namespace MagAOX
973
974#endif // psfAcq_hpp
#define IMAGESTRUCT_FLOAT
The base-class for MagAO-X applications.
Definition MagAOXApp.hpp:73
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI property element value if it has changed.
int createStandardIndiRequestSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single request element.
stateCodes::stateCodeT state()
Get the current state code.
int registerIndiPropertyNew(pcf::IndiProperty &prop, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for.
int createStandardIndiToggleSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single toggle element.
std::unordered_map< std::string, indiCallBack > m_indiNewCallBacks
Map to hold the NewProperty indiCallBacks for this App, with fast lookup by property name.
indiDriver< MagAOXApp > * m_indiDriver
The INDI driver wrapper. Constructed and initialized by execute, which starts and stops communication...
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI switch element value if it has changed.
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
int createROIndiNumber(pcf::IndiProperty &prop, const std::string &propName, const std::string &propLabel="", const std::string &propGroup="")
Create a ReadOnly INDI Number property.
int registerIndiPropertyReadOnly(pcf::IndiProperty &prop)
Register an INDI property which is read only.
std::mutex m_indiMutex
Mutex for locking INDI communications.
int sendNewProperty(const pcf::IndiProperty &ipSend, const std::string &el, const T &newVal)
Send a newProperty command to another device (using the INDI Client interface)
int indiTargetUpdate(pcf::IndiProperty &localProperty, T &localTarget, const pcf::IndiProperty &remoteProperty, bool setBusy=true)
Get the target element value from an new property.
uint32_t m_width
The width of the images in the stream.
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
int updateINDI()
Update the INDI properties for this device controller.
int appLogic()
Checks the shmimMonitor thread.
uint32_t m_height
The height of the images in the stream.
int appShutdown()
Shuts down the shmimMonitor thread.
uint8_t m_dataType
The ImageStreamIO type code.
bool m_restart
Flag indicating tha the shared memory should be reinitialized.
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
The MagAO-X PSF Fitter.
Definition psfAcq.hpp:94
pcf::IndiProperty m_indiP_seeing_star
Definition psfAcq.hpp:238
mx::improc::eigenImage< float > m_sm
Definition psfAcq.hpp:130
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
Definition psfAcq.hpp:346
virtual void loadConfig()
Definition psfAcq.hpp:364
mx::improc::eigenImage< float > m_image
Definition psfAcq.hpp:129
std::string m_fpsSource
Definition psfAcq.hpp:120
dev::shmimMonitor< psfAcq, darkShmimT > darkShmimMonitorT
Definition psfAcq.hpp:105
psfAcq()
Default c'tor.
Definition psfAcq.hpp:261
int processImage(void *curr_src, const dev::shmimT &)
Definition psfAcq.hpp:486
pcf::IndiProperty m_indiP_num_stars
Definition psfAcq.hpp:226
float realT
Floating point type in which to do all calculations.
Definition psfAcq.hpp:108
virtual int appLogic()
Implementation of the FSM for psfAcq.
Definition psfAcq.hpp:426
friend class psfAcq_test
Definition psfAcq.hpp:96
std::vector< float > m_first_x_vals
Definition psfAcq.hpp:149
std::mutex m_imageMutex
Definition psfAcq.hpp:219
int recordTelem(const telem_fgtimings *)
uint16_t m_fitCircBuffMaxLength
Maximum length of the latency measurement circular buffers.
Definition psfAcq.hpp:123
virtual int appStartup()
Startup function.
Definition psfAcq.hpp:369
~psfAcq() noexcept
D'tor, declared and defined for noexcept.
Definition psfAcq.hpp:267
mx::improc::eigenImage< float > m_dark
Definition psfAcq.hpp:132
pcf::IndiProperty m_indiP_seeing
Definition psfAcq.hpp:228
pcf::IndiProperty m_indiP_recordSeeing
Definition psfAcq.hpp:245
virtual void setupConfig()
Definition psfAcq.hpp:275
int allocate(const dev::shmimT &)
Definition psfAcq.hpp:465
virtual int appShutdown()
Shutdown the app.
Definition psfAcq.hpp:457
mx::math::fit::fitGaussian2Dsym< float > m_gfit
Definition psfAcq.hpp:170
dev::shmimMonitor< psfAcq > shmimMonitorT
Definition psfAcq.hpp:103
float m_fitCircBuffMaxTime
Maximum time of the latency meaurement circular buffers.
Definition psfAcq.hpp:124
pcf::IndiProperty m_indiP_fpsSource
Definition psfAcq.hpp:230
std::vector< float > m_first_y_vals
Definition psfAcq.hpp:150
pcf::IndiProperty m_indiP_acquire_star
Definition psfAcq.hpp:234
std::vector< Star > m_detectedStars
Definition psfAcq.hpp:151
pcf::IndiProperty m_indiP_restartAcq
Definition psfAcq.hpp:242
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
#define INDI_SETCALLBACK_DECL(class, prop)
Declare the callback for a set property request, and declare and define the static wrapper.
#define CREATE_REG_INDI_NEW_NUMBERF(prop, name, min, max, step, format, label, group)
Create and register a NEW INDI property as a standard number as float, using the standard callback na...
#define INDI_SETCALLBACK_DEFN(class, prop)
Define the callback for a set property request.
#define REG_INDI_SETPROP(prop, devName, propName)
Register a SET INDI property with the class, using the standard callback name.
#define INDI_NEWCALLBACK_DECL(class, prop)
Declare the callback for a new property request, and declare and define the static wrapper.
@ OPERATING
The device is operating, other than homing.
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
#define INDI_IDLE
Definition indiUtils.hpp:27
#define INDI_BUSY
Definition indiUtils.hpp:29
float calculateDistance(float x1, float y1, float x2, float y2)
Definition psfAcq.hpp:481
const pcf::IndiProperty & ipRecv
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition dm.hpp:26
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
static constexpr logPrioT LOG_INFO
Informational. The info log level is the lowest level recorded during normal operations.
void deallocate()
Definition psfAcq.hpp:77
pcf::IndiProperty * m_prop
Definition psfAcq.hpp:58
pcf::IndiProperty & prop()
Definition psfAcq.hpp:62
static std::string configSection()
Definition psfAcq.hpp:36
static std::string indiPrefix()
Definition psfAcq.hpp:41
Software ERR log entry.
Log entry recording framegrabber timings.
double variance(double data[], int size, int num_skip=0)