API
 
Loading...
Searching...
No Matches
strehlEstimator.hpp
Go to the documentation of this file.
1/** \file strehlEstimator.hpp
2 * \brief Declares the `strehlEstimator` MagAO-X application.
3 *
4 * \ingroup strehlEstimator_files
5 */
6
7#ifndef strehlEstimator_hpp
8#define strehlEstimator_hpp
9
10#include <cmath>
11#include <limits>
12#include <mutex>
13#include <sstream>
14
15#include <mx/ao/analysis/aoSystem.hpp>
16using namespace mx::math;
17
18#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
19#include "../../magaox_git_version.h"
20
21/** \defgroup strehlEstimator
22 * \brief Predicts Strehl and WFE for live or operator-estimated observing conditions.
23 *
24 * <a href="../handbook/operating/software/apps/XXXXXX.html">Application Documentation</a>
25 *
26 * \ingroup apps
27 *
28 */
29
30/** \defgroup strehlEstimator_files
31 * \ingroup strehlEstimator
32 */
33
34namespace MagAOX
35{
36namespace app
37{
38
39/// Tag type for the live WFS average shmim monitor.
40struct wfsavgShmimT
41{
42 /// Return the configuration section name for this shmim monitor.
43 static std::string configSection()
44 {
45 return "wfsavgShmim";
46 };
47
48 /// Return the INDI prefix for this shmim monitor.
49 static std::string indiPrefix()
50 {
51 return "wfsavg";
52 };
53};
54
55/// Tag type for the WFS mask shmim monitor.
56struct wfsmaskShmimT
57{
58 /// Return the configuration section name for this shmim monitor.
59 static std::string configSection()
60 {
61 return "wfsmaskShmim";
62 };
63
64 /// Return the INDI prefix for this shmim monitor.
65 static std::string indiPrefix()
66 {
67 return "wfsmask";
68 };
69};
70
71/// Predicts Strehl and WFE from live WFS telemetry and optional planning overrides.
72/**
73 * \ingroup strehlEstimator
74 */
75class strehlEstimator : public MagAOXApp<true>,
76 dev::shmimMonitor<strehlEstimator, wfsavgShmimT>,
77 dev::shmimMonitor<strehlEstimator, wfsmaskShmimT>
78{
79
80 // Give the test harness access.
82
85
86 public:
89 typedef mx::AO::analysis::aoSystem<float, mx::AO::analysis::vonKarmanSpectrum<float>> aoSystemT;
90
91 protected:
92 /** \name Configurable Parameters - Data
93 *@{
94 */
95
96 /// Loop number used to resolve the WFS shmim names.
97 int m_loopNum{ 1 };
98
99 /// WFS device providing the live FPS property.
100 std::string m_wfsDevice{ "camwfs" };
101
102 /// Beamsplitter stage device used to choose the active photometric calibration.
103 std::string m_stagebsDevice{ "stagebs" };
104
105 /// Analog gain factor converting WFS counts into photo-electrons.
106 float m_again{ 28.547f };
107
108 /// Active WFS quantum efficiency for the currently selected beamsplitter branch.
109 float m_qe{ 0.53f };
110
111 /// Zero-magnitude photon flux for the 65/35 beamsplitter branch.
112 float m_F0_6535{ 4.2e10f };
113
114 /// Zero-magnitude photon flux for the Ha/IR beamsplitter branch.
115 float m_F0_HaIR{ 5.3e10f };
116
117 /// Effective WFS wavelength in microns for the 65/35 beamsplitter branch.
118 float m_lam0_6535{ 0.791f };
119
120 /// Effective WFS wavelength in microns for the Ha/IR beamsplitter branch.
121 float m_lam0_HaIR{ 0.837f };
122
123 /// WFS QE for the 65/35 beamsplitter branch.
124 float m_qe_6535{ 0.53f };
125
126 /// WFS QE for the Ha/IR beamsplitter branch.
127 float m_qe_HaIR{ 0.53f };
128
129 ///@}
130
131 /** \name Runtime State - Data
132 *@{
133 */
134
135 /// Live WFS frame rate in Hz.
136 float m_fps{ 2000.0f };
137
138 /// Live EM gain reported by the WFS camera.
139 float m_emg{ 1.0f };
140
141 /// Active zero-magnitude photon flux for the selected beamsplitter branch.
142 float m_F0{ m_F0_6535 };
143
144 /// Active WFS/science wavelength in microns for the selected beamsplitter branch.
146
147 /// Live seeing estimate in arcseconds from `tcsi.seeing.dimm_fwhm_corr`.
148 float m_seeing{ 0.64f };
149
150 /// Live Fried parameter corresponding to `m_seeing`.
151 float m_r0{ 0.2063f * 0.5f / 0.64f };
152
153 /// Telescope elevation in degrees.
154 float m_elevation{ 90.0f };
155
156 /// Number of illuminated WFS pixels in the current mask.
157 int m_npix{ 0 };
158
159 /// Total masked WFS counts used to derive the live guide-star magnitude.
160 float m_counts{ 0.0f };
161
162 /// Live guide-star magnitude derived from `m_counts`.
163 float m_mag{ 0.0f };
164
165 /// Operator-entered star magnitude used when planning overrides are enabled.
166 float m_magEstimated{ 0.0f };
167
168 /// Tracks whether the estimated star magnitude has been explicitly set by an operator.
169 bool m_magEstimatedManual{ false };
170
171 /// Operator-entered seeing in arcseconds used when planning overrides are enabled.
172 float m_seeingEstimated{ 0.64f };
173
174 /// Tracks whether the estimated seeing has been explicitly set by an operator.
176
177 /// Operator-selected wind speed in m/s used for planning calculations.
178 float m_windSpeed{ 9.4f };
179
180 /// Selects whether predicted outputs use the live or estimated planning inputs.
181 bool m_useEstimates{ false };
182
183 /// Latest WFS mask image.
184 mx::improc::eigenImage<float> m_wfsmask;
185
186 /// Latest WFS average image.
187 mx::improc::eigenImage<float> m_wfsavg;
188
189 /// AO model used for the current predicted Strehl and WFE outputs.
191
192 /// AO model dedicated to the fixed-FPS optimum-loop-speed scan.
194
195 /// Latest DIMM elevation-corrected FWHM.
196 double m_dimm_fwhm_corr{ 0.0 };
197
198 /// Seconds since midnight of the latest DIMM measurement.
199 int m_dimm_time{ 0 };
200
201 /// Latest MAG1 elevation-corrected FWHM.
202 double m_mag1_fwhm_corr{ 0.0 };
203
204 /// Seconds since midnight of the latest MAG1 measurement.
205 int m_mag1_time{ 0 };
206
207 /// Latest MAG2 elevation-corrected FWHM.
208 double m_mag2_fwhm_corr{ 0.0 };
209
210 /// Seconds since midnight of the latest MAG2 measurement.
211 int m_mag2_time{ 0 };
212
213 /// Protects the live telemetry and planning-input state while prediction snapshots are assembled.
214 mutable std::mutex m_stateMutex;
215
216 /// Serializes access to the shared AO-model instances and prediction-property updates.
217 mutable std::mutex m_predictionMutex;
218
219 /// Tracks the last suspicious optimum FPS reported in debug logging.
221
222 ///@}
223
224 public:
225 /// Construct the application with the compiled git-version metadata.
227
228 /// Destroy the application.
232
233 /// Declare configuration keys and initialize the AO models.
234 virtual void setupConfig();
235
236 /// Load configuration values after `setupConfig()` has registered them.
237 /**
238 * This is split from `loadConfig()` so the unit tests can call it directly.
239 */
240 int loadConfigImpl( mx::app::appConfigurator &_config /**< [in] application configuration source to read from */ );
241
242 /// Load the configured runtime values.
243 virtual void loadConfig();
244
245 /// Register INDI properties and transition the app into the operating state.
246 virtual int appStartup();
247
248 /// Refresh the AO predictions and service the shmim-monitor state machine.
249 /**
250 * \returns 0 on no critical error
251 * \returns -1 on an error requiring shutdown
252 */
253 virtual int appLogic();
254
255 /// Shut down the shmim monitors.
256 virtual int appShutdown();
257
258 /// React to allocation of the WFS average shmim stream.
259 int allocate( const wfsavgShmimT &dummy /**< [in] tag distinguishing the shmimMonitor parent */ );
260
261 /// Process one WFS average frame.
262 int processImage( void *curr_src, /**< [in] pointer to the start of the current frame */
263 const wfsavgShmimT &dummy /**< [in] tag distinguishing the shmimMonitor parent */ );
264
265 /// React to allocation of the WFS mask shmim stream.
266 int allocate( const wfsmaskShmimT &dummy /**< [in] tag distinguishing the shmimMonitor parent */ );
267
268 /// Process one WFS mask frame.
269 int processImage( void *curr_src, /**< [in] pointer to the start of the current frame */
270 const wfsmaskShmimT &dummy /**< [in] tag distinguishing the shmimMonitor parent */ );
271
272 /// Recalculate the live guide-star magnitude from the current WFS counts.
273 void calcMag();
274
275 /// Snapshot of the scalar inputs used to update the AO prediction model.
277 {
278 /// Live loop speed in Hz.
279 float m_fps{ 0.0f };
280
281 /// Live EM gain.
282 float m_emg{ 0.0f };
283
284 /// Active quantum efficiency.
285 float m_qe{ 0.0f };
286
287 /// Active zero-magnitude photon flux.
288 float m_F0{ 0.0f };
289
290 /// Active wavelength in microns.
291 float m_lam0{ 0.0f };
292
293 /// Telescope elevation in degrees.
294 float m_elevation{ 0.0f };
295
296 /// Current illuminated WFS-pixel count.
297 int m_npix{ 0 };
298
299 /// Live guide-star magnitude.
300 float m_mag{ 0.0f };
301
302 /// Operator-entered guide-star magnitude estimate.
303 float m_magEstimated{ 0.0f };
304
305 /// Selected guide-star magnitude used for prediction.
306 float m_selectedMag{ 0.0f };
307
308 /// Live seeing in arcseconds.
309 float m_seeing{ 0.0f };
310
311 /// Operator-entered seeing estimate in arcseconds.
312 float m_seeingEstimated{ 0.0f };
313
314 /// Selected seeing used for prediction.
315 float m_selectedSeeing{ 0.0f };
316
317 /// Operator-selected wind speed in m/s.
318 float m_windSpeed{ 0.0f };
319
320 /// Selected wind speed used for prediction.
321 float m_selectedWindSpeed{ 0.0f };
322
323 /// Whether estimated planning inputs are currently selected.
324 bool m_useEstimates{ false };
325 };
326
327 /// Snapshot the scalar state used by the planning properties and AO-model calculations.
328 predictionInputs snapshotPredictionInputs() const;
329
330 /// Return the selected star magnitude for prediction calculations.
331 float selectedStarMag() const;
332
333 /// Return the selected seeing for prediction calculations.
334 float selectedSeeing() const;
335
336 /// Return the selected wind speed for prediction calculations.
337 float selectedWindSpeed() const;
338
339 /// Return the supported wind-speed selection element names.
340 static const std::vector<std::string> &windSpeedSelectionElements();
341
342 /// Return the supported wind-speed selection labels.
343 static const std::vector<std::string> &windSpeedSelectionLabels();
344
345 /// Convert a wind-speed selection element name into its configured speed in m/s.
346 static float windSpeedSelectionValue( const std::string &selection /**< [in] selected wind-speed element name */ );
347
348 /// Return the nearest supported wind-speed selection element name for a speed in m/s.
349 static std::string windSpeedSelectionName( float windSpeed /**< [in] wind speed in m/s */ );
350
351 /// Convert seeing in arcseconds to Fried parameter `r0` in meters.
352 static float seeingToR0( float seeing /**< [in] seeing in arcseconds */ );
353
354 /// Return whether a value is finite.
355 static bool finiteValue( float value /**< [in] value to test */ );
356
357 /// Return whether a value is finite and strictly positive.
358 static bool finitePositiveValue( float value /**< [in] value to test */ );
359
360 /// Return whether two scalar AO-model inputs are effectively equal.
361 static bool nearlyEqual( float a, /**< [in] first value */
362 float b, /**< [in] second value */
363 float relTol, /**< [in] relative tolerance */
364 float absTol /**< [in] absolute tolerance */
365 );
366
367 /// Convert AO model phase variance into WFE in nm RMS at the specified wavelength.
368 static float wfeNm( float variance, /**< [in] phase variance at the science wavelength */
369 float lam0 /**< [in] active wavelength in microns */
370 );
371
372 /// Create a writable number property with `current` and `estimated` elements.
373 int createCurrentEstimatedProperty( pcf::IndiProperty &prop, /**< [out] property to initialize */
374 const std::string &name, /**< [in] INDI property name */
375 const std::string &label, /**< [in] suggested GUI label */
376 const std::string &group /**< [in] suggested GUI group */
377 );
378
379 /// Update the published planning-input properties from the current runtime state.
381
382 /// Configure an AO model for the selected inputs and requested loop speed.
383 void configureAoSystem( aoSystemT &aosys, /**< [in,out] AO model instance to configure */
384 const predictionInputs &inputs, /**< [in] scalar state snapshot to apply */
385 float fps, /**< [in] loop speed in Hz */
386 bool optimizeTau /**< [in] true to preserve the current optimal-tau behavior */
387 );
388
389 /// Refresh the predicted Strehl, WFE, and optimum-loop-speed properties.
391
392 /// Refresh the fixed-grid optimum-loop-speed summary property.
393 void updateOptimumLoopSpeed( const predictionInputs &inputs /**< [in] scalar state snapshot to evaluate */ );
394
395 /** \name INDI - Data
396 * @{
397 */
398
399 /// Subscription to the live WFS FPS property.
400 pcf::IndiProperty m_indiP_fps;
401
402 /// Subscription to the live WFS EM-gain property.
403 pcf::IndiProperty m_indiP_emg;
404
405 /// Subscription to the beamsplitter preset state.
406 pcf::IndiProperty m_indiP_stage;
407
408 /// Subscription to the TCS seeing property.
409 pcf::IndiProperty m_indiP_tcsi_seeing;
410
411 /// Subscription to the TCS telescope position property.
412 pcf::IndiProperty m_indiP_tcsi_telpos;
413
414 /// Local writable seeing property exposing `current` and `estimated`.
415 pcf::IndiProperty m_indiP_seeing_magaox;
416
417 /// Local writable star-magnitude property exposing `current` and `estimated`.
418 pcf::IndiProperty m_indiP_mag;
419
420 /// Local writable wind-speed selection property exposing `slow`, `normal`, and `fast`.
421 pcf::IndiProperty m_indiP_windSpeed;
422
423 /// Local toggle selecting whether predicted outputs use estimated inputs.
424 pcf::IndiProperty m_indiP_useEstimates;
425
426 /// Predicted Strehl property for the currently selected conditions.
427 pcf::IndiProperty m_indiP_strehl;
428
429 /// Predicted WFE breakdown for the currently selected conditions.
430 pcf::IndiProperty m_indiP_wfe;
431
432 /// Summary property for the best fixed-grid loop speed.
433 pcf::IndiProperty m_indiP_loopSpeedOptimum;
434
435 /// Callback for live FPS updates.
437
438 /// Callback for live EM-gain updates.
440
441 /// Callback for beamsplitter preset updates.
443
444 /// Callback for live TCS seeing updates.
446
447 /// Callback for live TCS elevation updates.
449
450 /// Callback for local star-magnitude estimate writes.
452
453 /// Callback for local seeing estimate writes.
455
456 /// Callback for local wind-speed selection writes.
458
459 /// Callback for the `use_estimates` toggle.
461
462 ///@}
463};
464
465strehlEstimator::strehlEstimator() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
466{
469
470 return;
471}
472
474{
475 m_aosys.loadMagAOX();
476 m_aosysScan.loadMagAOX();
477
479
480 config.add( "loop.number",
481 "",
482 "loop.number",
483 argType::Required,
484 "loop",
485 "number",
486 false,
487 "int",
488 "The number of the loop. Used to set shmim names, as in aolN_mgainfact." );
489
490 config.add( "phot.qe_6535",
491 "",
492 "phot.qe_6535",
493 argType::Required,
494 "phot",
495 "qe_6535",
496 false,
497 "float",
498 "The WFS QE in the 65-35 B/S." );
499
500 config.add( "phot.qe_HaIR",
501 "",
502 "phot.qe_HaIR",
503 argType::Required,
504 "phot",
505 "qe_HaIR",
506 false,
507 "float",
508 "The WFS QE in the Ha-IR B/S." );
509
512}
513
514int strehlEstimator::loadConfigImpl( mx::app::appConfigurator &_config )
515{
516 _config( m_loopNum, "loop.number" );
517
518 _config( m_qe_6535, "phot.qe_6535" );
519 _config( m_qe_HaIR, "phot.qe_HaIR" );
520
521 char shmim[1024];
522 snprintf( shmim, sizeof( shmim ), "aol%d_wfsavg", m_loopNum );
525
526 snprintf( shmim, sizeof( shmim ), "aol%d_wfsmask", m_loopNum );
529
530 return 0;
531}
532
534{
535 loadConfigImpl( config );
536}
537
539 const std::string &name,
540 const std::string &label,
541 const std::string &group )
542{
543 prop = pcf::IndiProperty( pcf::IndiProperty::Number );
544 prop.setDevice( configName() );
545 prop.setName( name );
546 prop.setPerm( pcf::IndiProperty::ReadWrite );
547 prop.setState( pcf::IndiProperty::Idle );
548
549 if( label != "" )
550 {
551 prop.setLabel( label );
552 }
553
554 if( group != "" )
555 {
556 prop.setGroup( group );
557 }
558
559 prop.add( pcf::IndiElement( "current", 0.0f ) );
560 prop.add( pcf::IndiElement( "estimated", 0.0f ) );
561
562 return 0;
563}
564
566{
567 std::lock_guard<std::mutex> lock( m_stateMutex );
568
569 if( m_useEstimates )
570 {
571 return m_magEstimated;
572 }
573
574 return m_mag;
575}
576
578{
579 std::lock_guard<std::mutex> lock( m_stateMutex );
580
581 if( m_useEstimates )
582 {
583 return m_seeingEstimated;
584 }
585
586 return m_seeing;
587}
588
590{
591 std::lock_guard<std::mutex> lock( m_stateMutex );
592 return m_windSpeed;
593}
594
595const std::vector<std::string> &strehlEstimator::windSpeedSelectionElements()
596{
597 static const std::vector<std::string> names{ "slow", "normal", "fast", "very-fast" };
598 return names;
599}
600
601const std::vector<std::string> &strehlEstimator::windSpeedSelectionLabels()
602{
603 static const std::vector<std::string> labels{
604 "Slow (9.4 m/s)", "Normal (18.7 m/s)", "Fast (23.4 m/s)", "Very Fast (30.0 m/s)" };
605 return labels;
606}
607
608float strehlEstimator::windSpeedSelectionValue( const std::string &selection )
609{
610 if( selection == "very-fast" )
611 {
612 return 30.0f;
613 }
614
615 if( selection == "fast" )
616 {
617 return 23.4f;
618 }
619
620 if( selection == "normal" )
621 {
622 return 18.7f;
623 }
624
625 return 9.4f;
626}
627
628std::string strehlEstimator::windSpeedSelectionName( float windSpeed )
629{
630 float nearestDiff = std::numeric_limits<float>::max();
631 std::string nearestSelection = "slow";
632
633 for( const auto &selection : windSpeedSelectionElements() )
634 {
637 {
640 }
641 }
642
643 return nearestSelection;
644}
645
647{
649
650 { // mutex scope
651 std::lock_guard<std::mutex> lock( m_stateMutex );
652
653 inputs.m_fps = m_fps;
654 inputs.m_emg = m_emg;
655 inputs.m_qe = m_qe;
656 inputs.m_F0 = m_F0;
657 inputs.m_lam0 = m_lam0;
658 inputs.m_elevation = m_elevation;
659 inputs.m_npix = m_npix;
660 inputs.m_mag = m_mag;
661 inputs.m_magEstimated = m_magEstimated;
662 inputs.m_seeing = m_seeing;
663 inputs.m_seeingEstimated = m_seeingEstimated;
664 inputs.m_windSpeed = m_windSpeed;
665 inputs.m_useEstimates = m_useEstimates;
666 }
667
668 if( inputs.m_useEstimates )
669 {
670 inputs.m_selectedMag = inputs.m_magEstimated;
671 inputs.m_selectedSeeing = inputs.m_seeingEstimated;
672 inputs.m_selectedWindSpeed = inputs.m_windSpeed;
673 }
674 else
675 {
676 inputs.m_selectedMag = inputs.m_mag;
677 inputs.m_selectedSeeing = inputs.m_seeing;
678 inputs.m_selectedWindSpeed = inputs.m_windSpeed;
679 }
680
681 return inputs;
682}
683
684float strehlEstimator::seeingToR0( float seeing )
685{
686 return 0.2063f * 0.5f / seeing;
687}
688
690{
691 return std::isfinite( value );
692}
693
695{
696 return finiteValue( value ) && value > 0.0f;
697}
698
699bool strehlEstimator::nearlyEqual( float a, float b, float relTol, float absTol )
700{
701 float diff = std::fabs( a - b );
702 if( diff <= absTol )
703 {
704 return true;
705 }
706
707 float scale = std::fabs( a );
708 if( std::fabs( b ) > scale )
709 {
710 scale = std::fabs( b );
711 }
712
713 return diff <= relTol * scale;
714}
715
716float strehlEstimator::wfeNm( float variance, float lam0 )
717{
718 if( variance <= 0.0f )
719 {
720 return 0.0f;
721 }
722
723 return std::sqrt( variance ) * ( 1000.0f * lam0 / two_pi<float>() );
724}
725
727{
729 std::string windSelection = windSpeedSelectionName( inputs.m_windSpeed );
730
731 if( !m_indiDriver )
732 {
733 m_indiP_mag["current"].set( inputs.m_mag );
734 m_indiP_mag["estimated"].set( inputs.m_magEstimated );
735 m_indiP_mag.setState( INDI_OK );
736
737 m_indiP_seeing_magaox["current"].set( inputs.m_seeing );
738 m_indiP_seeing_magaox["estimated"].set( inputs.m_seeingEstimated );
739 m_indiP_seeing_magaox.setState( INDI_OK );
740
741 for( auto &&el : m_indiP_windSpeed.getElements() )
742 {
743 m_indiP_windSpeed[el.first].setSwitchState( el.first == windSelection ? pcf::IndiElement::On
744 : pcf::IndiElement::Off );
745 }
746 m_indiP_windSpeed.setState( INDI_OK );
747
748 m_indiP_useEstimates["toggle"].setSwitchState( inputs.m_useEstimates ? pcf::IndiElement::On
749 : pcf::IndiElement::Off );
750 m_indiP_useEstimates.setState( inputs.m_useEstimates ? INDI_OK : INDI_IDLE );
751
752 return;
753 }
754
755 updatesIfChanged<float>( m_indiP_mag, { "current", "estimated" }, { inputs.m_mag, inputs.m_magEstimated } );
757 m_indiP_seeing_magaox, { "current", "estimated" }, { inputs.m_seeing, inputs.m_seeingEstimated } );
760 "toggle",
761 inputs.m_useEstimates ? pcf::IndiElement::On : pcf::IndiElement::Off,
762 inputs.m_useEstimates ? INDI_OK : INDI_IDLE );
763}
764
765void strehlEstimator::configureAoSystem( aoSystemT &aosys, const predictionInputs &inputs, float fps, bool optimizeTau )
766{
767 aosys.optTau( optimizeTau );
768 aosys.starMag( inputs.m_selectedMag );
769 aosys.F0( inputs.m_qe * inputs.m_F0 );
770 aosys.lam_wfs( inputs.m_lam0 * 1.0e-6f );
771 aosys.lam_sci( inputs.m_lam0 * 1.0e-6f );
772 aosys.ron_wfs( std::vector<float>( { 245.0f / inputs.m_emg } ) );
773 aosys.npix_wfs( std::vector<float>( { static_cast<float>( inputs.m_npix ) } ) );
774 aosys.minTauWFS( std::vector<float>( { 1.0f / fps } ) );
775 aosys.tauWFS( 1.0f / fps );
776 aosys.atm.r_0( seeingToR0( inputs.m_selectedSeeing ), 0.5e-6f );
777 aosys.atm.v_wind( inputs.m_selectedWindSpeed );
778 aosys.zeta( ( 90.0f - inputs.m_elevation ) * pi<float>() / 180.0f );
779}
780
782{
783 struct scanPoint
784 {
785 int fps{ 0 };
786 float strehl{ 0.0f };
787 float wfeMeasurement{ 0.0f };
788 float wfeTimeDelay{ 0.0f };
789 float wfeFitting{ 0.0f };
790 float wfeTotal{ 0.0f };
791 float dOpt{ 0.0f };
792 int binOpt{ 0 };
793 };
794
795 constexpr float strehlTieTolerance = 1.0e-4f;
796
797 float bestFPS = 0.0f;
798 float bestStrehl = -1.0f;
799 float bestTotalWfe = 0.0f;
800 float bestMeasurementWfe = 0.0f;
801 float bestTimeDelayWfe = 0.0f;
802 float bestFittingWfe = 0.0f;
803 std::vector<scanPoint> scanCurve;
804 scanCurve.reserve( 30 );
805
806 for( int fps = 100; fps <= 3000; fps += 100 )
807 {
808 configureAoSystem( m_aosysScan, inputs, static_cast<float>( fps ), false );
809
810 float strehl = m_aosysScan.strehl();
811 if( !finiteValue( strehl ) )
812 {
813 continue;
814 }
815
817 point.fps = fps;
818 point.strehl = strehl;
819 point.wfeMeasurement = wfeNm( m_aosysScan.measurementErrorTotal(), inputs.m_lam0 );
820 point.wfeTimeDelay = wfeNm( m_aosysScan.timeDelayErrorTotal(), inputs.m_lam0 );
821 point.wfeFitting = wfeNm( m_aosysScan.fittingErrorTotal(), inputs.m_lam0 );
822 point.wfeTotal = wfeNm( m_aosysScan.wfeVar(), inputs.m_lam0 );
823 point.dOpt = m_aosysScan.d_opt();
824 point.binOpt = m_aosysScan.bin_opt();
825 scanCurve.push_back( point );
826
827 if( bestFPS == 0.0f || strehl > bestStrehl + strehlTieTolerance )
828 {
829 bestFPS = static_cast<float>( fps );
831 bestTotalWfe = point.wfeTotal;
832 bestMeasurementWfe = point.wfeMeasurement;
833 bestTimeDelayWfe = point.wfeTimeDelay;
834 bestFittingWfe = point.wfeFitting;
835 }
836 }
837
838 bool suspiciousWinner = bestFPS > 0.0f && ( bestFPS <= 400.0f || bestStrehl >= 0.98f ||
839 bestMeasurementWfe == 0.0f || bestTimeDelayWfe == 0.0f );
840
842 {
843 std::ostringstream oss;
844 oss << "strehlEstimator suspicious optimum scan" << " use_estimates=" << std::boolalpha << inputs.m_useEstimates
845 << " selected_mag=" << inputs.m_selectedMag << " selected_seeing=" << inputs.m_selectedSeeing
846 << " selected_wind=" << inputs.m_selectedWindSpeed << " fps_live=" << inputs.m_fps
847 << " emg=" << inputs.m_emg << " elevation=" << inputs.m_elevation << " npix=" << inputs.m_npix
848 << " winner_fps=" << bestFPS << " winner_strehl=" << bestStrehl << " winner_wfe_total=" << bestTotalWfe
849 << " winner_wfe_meas=" << bestMeasurementWfe << " winner_wfe_delay=" << bestTimeDelayWfe
850 << " winner_wfe_fit=" << bestFittingWfe << '\n';
851
852 for( const auto &point : scanCurve )
853 {
854 oss << " fps=" << point.fps << " strehl=" << point.strehl << " wfe_total=" << point.wfeTotal
855 << " wfe_meas=" << point.wfeMeasurement << " wfe_delay=" << point.wfeTimeDelay
856 << " wfe_fit=" << point.wfeFitting << " d_opt=" << point.dOpt << " bin_opt=" << point.binOpt << '\n';
857 }
858
859 std::cerr << oss.str();
861 }
862 else if( !suspiciousWinner )
863 {
865 }
866
867 if( !m_indiDriver )
868 {
869 m_indiP_loopSpeedOptimum["fps"].set( bestFPS );
870 m_indiP_loopSpeedOptimum["strehl"].set( bestStrehl );
871 m_indiP_loopSpeedOptimum["wfe_total"].set( bestTotalWfe );
872 m_indiP_loopSpeedOptimum["wfe_measurement"].set( bestMeasurementWfe );
873 m_indiP_loopSpeedOptimum["wfe_time_delay"].set( bestTimeDelayWfe );
874 m_indiP_loopSpeedOptimum["wfe_fitting"].set( bestFittingWfe );
876 return;
877 }
878
881 { "fps", "strehl", "wfe_total", "wfe_measurement", "wfe_time_delay", "wfe_fitting" },
883}
884
886{
887 std::lock_guard<std::mutex> predictionLock( m_predictionMutex );
888 constexpr float predictionRelTol = 1.0e-6f;
889 constexpr float predictionAbsTol = 1.0e-7f;
890
892
893 if( !finitePositiveValue( inputs.m_fps ) || !finitePositiveValue( inputs.m_emg ) ||
895 !finitePositiveValue( inputs.m_selectedSeeing ) || !finitePositiveValue( inputs.m_selectedWindSpeed ) ||
896 inputs.m_npix <= 0 )
897 {
898 return;
899 }
900
901 const float configuredF0 = inputs.m_qe * inputs.m_F0;
902 const float configuredLam = inputs.m_lam0 * 1.0e-6f;
903 const float configuredRon = 245.0f / inputs.m_emg;
904 const float configuredNpix = static_cast<float>( inputs.m_npix );
905 const float configuredTau = 1.0f / inputs.m_fps;
906 const float configuredZeta = ( 90.0f - inputs.m_elevation ) * pi<float>() / 180.0f;
907
909 m_aosys.optTau() == true && m_aosys.ron_wfs().size() > 0 && m_aosys.npix_wfs().size() > 0 &&
910 m_aosys.minTauWFS().size() > 0 &&
911 nearlyEqual( m_aosys.starMag(), inputs.m_selectedMag, predictionRelTol, predictionAbsTol ) &&
919 nearlyEqual( m_aosys.atm.v_wind(), inputs.m_selectedWindSpeed, predictionRelTol, predictionAbsTol ) &&
921
923 {
924 return;
925 }
926
927 configureAoSystem( m_aosys, inputs, inputs.m_fps, true );
928
929 if( !m_indiDriver )
930 {
931 m_indiP_strehl["pyramid"].set( m_aosys.strehl() );
932 m_indiP_strehl.setState( INDI_OK );
933
934 m_indiP_wfe["total"].set( wfeNm( m_aosys.wfeVar(), inputs.m_lam0 ) );
935 m_indiP_wfe["measurement"].set( wfeNm( m_aosys.measurementErrorTotal(), inputs.m_lam0 ) );
936 m_indiP_wfe["time_delay"].set( wfeNm( m_aosys.timeDelayErrorTotal(), inputs.m_lam0 ) );
937 m_indiP_wfe["fitting"].set( wfeNm( m_aosys.fittingErrorTotal(), inputs.m_lam0 ) );
938 m_indiP_wfe.setState( INDI_OK );
939 }
940 else
941 {
942 updateIfChanged( m_indiP_strehl, "pyramid", m_aosys.strehl() );
944 { "total", "measurement", "time_delay", "fitting" },
945 { wfeNm( m_aosys.wfeVar(), inputs.m_lam0 ),
946 wfeNm( m_aosys.measurementErrorTotal(), inputs.m_lam0 ),
947 wfeNm( m_aosys.timeDelayErrorTotal(), inputs.m_lam0 ),
948 wfeNm( m_aosys.fittingErrorTotal(), inputs.m_lam0 ) } );
949 }
950
952}
953
955{
958
962 REG_INDI_SETPROP( m_indiP_tcsi_seeing, "tcsi", "seeing" );
963 REG_INDI_SETPROP( m_indiP_tcsi_telpos, "tcsi", "telpos" );
964
965 if( createCurrentEstimatedProperty( m_indiP_mag, "star_mag", "Star Magnitude", "Error Budget" ) < 0 )
966 {
967 return log<software_error, -1>( { __FILE__, __LINE__, "error from createCurrentEstimatedProperty" } );
968 }
970 {
971 return log<software_error, -1>( { __FILE__, __LINE__, "error from registerIndiPropertyNew" } );
972 }
973
974 if( createCurrentEstimatedProperty( m_indiP_seeing_magaox, "seeing", "Seeing", "Error Budget" ) < 0 )
975 {
976 return log<software_error, -1>( { __FILE__, __LINE__, "error from createCurrentEstimatedProperty" } );
977 }
979 {
980 return log<software_error, -1>( { __FILE__, __LINE__, "error from registerIndiPropertyNew" } );
981 }
982
984 "wind_speed",
987 "Wind Speed",
988 "Error Budget" ) < 0 )
989 {
990 return log<software_error, -1>( { __FILE__, __LINE__, "error from createStandardIndiSelectionSw" } );
991 }
993 {
994 return log<software_error, -1>( { __FILE__, __LINE__, "error from registerIndiPropertyNew" } );
995 }
996
997 if( createStandardIndiToggleSw( m_indiP_useEstimates, "use_estimates", "Use Estimates", "Error Budget" ) < 0 )
998 {
999 return log<software_error, -1>( { __FILE__, __LINE__, "error from createStandardIndiToggleSw" } );
1000 }
1002 {
1003 return log<software_error, -1>( { __FILE__, __LINE__, "error from registerIndiPropertyNew" } );
1004 }
1005
1006 CREATE_REG_INDI_RO_NUMBER( m_indiP_strehl, "strehl_optimal", "Strehl", "Error Budget" );
1007 m_indiP_strehl.add( pcf::IndiElement( "pyramid", 0.0f ) );
1008
1009 CREATE_REG_INDI_RO_NUMBER( m_indiP_wfe, "wfe_predicted", "WFE", "Error Budget" );
1010 m_indiP_wfe.add( pcf::IndiElement( "total", 0.0f ) );
1011 m_indiP_wfe.add( pcf::IndiElement( "measurement", 0.0f ) );
1012 m_indiP_wfe.add( pcf::IndiElement( "time_delay", 0.0f ) );
1013 m_indiP_wfe.add( pcf::IndiElement( "fitting", 0.0f ) );
1014
1015 CREATE_REG_INDI_RO_NUMBER( m_indiP_loopSpeedOptimum, "loop_speed_optimum", "Optimum Loop Speed", "Error Budget" );
1016 m_indiP_loopSpeedOptimum.add( pcf::IndiElement( "fps", 0.0f ) );
1017 m_indiP_loopSpeedOptimum.add( pcf::IndiElement( "strehl", 0.0f ) );
1018 m_indiP_loopSpeedOptimum.add( pcf::IndiElement( "wfe_total", 0.0f ) );
1019 m_indiP_loopSpeedOptimum.add( pcf::IndiElement( "wfe_measurement", 0.0f ) );
1020 m_indiP_loopSpeedOptimum.add( pcf::IndiElement( "wfe_time_delay", 0.0f ) );
1021 m_indiP_loopSpeedOptimum.add( pcf::IndiElement( "wfe_fitting", 0.0f ) );
1022
1025
1027
1028 return 0;
1029}
1030
1044
1052
1054{
1055 static_cast<void>( dummy );
1056
1057 std::cerr << "Got WFS avg: " << wfsavgShmimMonitorT::m_width << " x " << wfsavgShmimMonitorT::m_height << '\n';
1058 return 0;
1059}
1060
1061int strehlEstimator::processImage( void *curr_src, const wfsavgShmimT &dummy )
1062{
1063 static_cast<void>( dummy );
1064
1065 auto wfsavg = mx::improc::eigenMap<float>(
1067
1068 float counts = 0.0f;
1069 bool haveCounts = false;
1070
1071 { // mutex scope
1072 std::lock_guard<std::mutex> lock( m_stateMutex );
1073
1074 m_wfsavg = wfsavg;
1075
1076 if( m_wfsavg.rows() == m_wfsmask.rows() && m_wfsavg.cols() == m_wfsmask.cols() )
1077 {
1078 m_counts = ( m_wfsavg * m_wfsmask ).sum();
1079 counts = m_counts;
1080 haveCounts = true;
1081 }
1082 }
1083
1084 if( haveCounts )
1085 {
1086 std::cerr << "counts: " << counts << '\n';
1087
1088 calcMag();
1089 }
1090
1091 return 0;
1092}
1093
1095{
1096 static_cast<void>( dummy );
1097
1098 std::cerr << "Got WFS mask: " << wfsmaskShmimMonitorT::m_width << " x " << wfsmaskShmimMonitorT::m_height << '\n';
1099 return 0;
1100}
1101
1102int strehlEstimator::processImage( void *curr_src, const wfsmaskShmimT &dummy )
1103{
1104 static_cast<void>( dummy );
1105
1106 auto wfsmask = mx::improc::eigenMap<float>(
1108
1109 bool haveCounts = false;
1110
1111 { // mutex scope
1112 std::lock_guard<std::mutex> lock( m_stateMutex );
1113
1115 m_npix = static_cast<int>( std::lround( m_wfsmask.sum() ) );
1116
1117 if( m_wfsavg.rows() == m_wfsmask.rows() && m_wfsavg.cols() == m_wfsmask.cols() )
1118 {
1119 // update counts because we might have been waiting on this.
1120 m_counts = ( m_wfsavg * m_wfsmask ).sum();
1121 haveCounts = true;
1122 }
1123 }
1124
1125 if( haveCounts )
1126 {
1127 calcMag();
1128 }
1129
1130 return 0;
1131}
1132
1134{
1135 float counts = 0.0f;
1136 float emg = 0.0f;
1137 float fps = 0.0f;
1138 float qe = 0.0f;
1139 float F0 = 0.0f;
1140 bool updated = false;
1141
1142 { // mutex scope
1143 std::lock_guard<std::mutex> lock( m_stateMutex );
1144
1145 counts = m_counts;
1146 emg = m_emg;
1147 fps = m_fps;
1148 qe = m_qe;
1149 F0 = m_F0;
1150
1153 {
1154 m_mag = -2.5f * std::log10( m_counts * m_again / m_emg * m_fps / ( m_qe * m_F0 ) );
1155
1157 {
1159 }
1160
1161 updated = true;
1162 }
1163 }
1164
1165 std::cerr << "calcMag: " << counts << ' ' << m_again << ' ' << ' ' << emg << ' ' << fps << ' ' << qe << ' ' << F0
1166 << '\n';
1167
1168 if( !updated )
1169 {
1170 return;
1171 }
1172
1175}
1176
1177INDI_SETCALLBACK_DEFN( strehlEstimator, m_indiP_fps )( const pcf::IndiProperty &ipRecv )
1178{
1179 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_fps, ipRecv );
1180
1181 if( ipRecv.find( "current" ) )
1182 {
1183 float fps = ipRecv["current"].get<float>();
1184
1185 bool changed = false;
1186
1187 { // mutex scope
1188 std::lock_guard<std::mutex> lock( m_stateMutex );
1189 if( finitePositiveValue( fps ) && fps != m_fps )
1190 {
1191 m_fps = fps;
1192 changed = true;
1193 }
1194 }
1195
1196 if( changed )
1197 {
1198 std::cerr << "Got FPS: " << fps << '\n';
1199
1200 calcMag();
1201 }
1202 }
1203 return 0;
1204}
1205
1206INDI_SETCALLBACK_DEFN( strehlEstimator, m_indiP_emg )( const pcf::IndiProperty &ipRecv )
1207{
1208 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_emg, ipRecv );
1209
1210 if( ipRecv.find( "current" ) )
1211 {
1212 float emg = ipRecv["current"].get<float>();
1213
1214 bool changed = false;
1215
1216 { // mutex scope
1217 std::lock_guard<std::mutex> lock( m_stateMutex );
1218 if( finitePositiveValue( emg ) && emg != m_emg )
1219 {
1220 m_emg = emg;
1221 changed = true;
1222 }
1223 }
1224
1225 if( changed )
1226 {
1227 std::cerr << "Got EMG: " << emg << '\n';
1228
1229 calcMag();
1230 }
1231 }
1232 return 0;
1233}
1234
1235INDI_SETCALLBACK_DEFN( strehlEstimator, m_indiP_stage )( const pcf::IndiProperty &ipRecv )
1236{
1237 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_stage, ipRecv );
1238
1239 std::string preset = "none";
1240
1241 for( auto &&el : ipRecv.getElements() )
1242 {
1243 if( el.second.getSwitchState() == pcf::IndiElement::On )
1244 {
1245 preset = el.first;
1246 break;
1247 }
1248 }
1249
1250 std::cerr << "Got stage bs: " << preset << '\n';
1251
1252 { // mutex scope
1253 std::lock_guard<std::mutex> lock( m_stateMutex );
1254
1255 if( preset == "ha-ir" )
1256 {
1257 m_F0 = m_F0_HaIR;
1258 m_lam0 = m_lam0_HaIR;
1259 m_qe = m_qe_HaIR;
1260 }
1261 else
1262 {
1263 m_F0 = m_F0_6535;
1264 m_lam0 = m_lam0_6535;
1265 m_qe = m_qe_6535;
1266 }
1267 }
1268
1269 calcMag();
1270
1271 return 0;
1272}
1273
1274INDI_SETCALLBACK_DEFN( strehlEstimator, m_indiP_tcsi_seeing )( const pcf::IndiProperty &ipRecv )
1275{
1276 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_tcsi_seeing, ipRecv );
1277
1278 if( ipRecv.find( "dimm_fwhm_corr" ) )
1279 {
1280 float seeing = ipRecv["dimm_fwhm_corr"].get<float>();
1281
1282 bool changed = false;
1283
1284 { // mutex scope
1285 std::lock_guard<std::mutex> lock( m_stateMutex );
1286
1287 if( finitePositiveValue( seeing ) && seeing != m_seeing )
1288 {
1289 m_seeing = seeing;
1290 m_r0 = seeingToR0( m_seeing );
1291 m_dimm_fwhm_corr = seeing;
1292
1293 if( !m_useEstimates && !m_seeingEstimatedManual )
1294 {
1295 m_seeingEstimated = m_seeing;
1296 }
1297
1298 changed = true;
1299 }
1300 }
1301
1302 if( changed )
1303 {
1304 std::cerr << "Got seeing: " << seeing << '\n';
1305
1306 updatePlanningProperties();
1307 updatePredictionOutputs();
1308 }
1309 }
1310
1311 return 0;
1312}
1313
1314INDI_SETCALLBACK_DEFN( strehlEstimator, m_indiP_tcsi_telpos )( const pcf::IndiProperty &ipRecv )
1315{
1316 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_tcsi_telpos, ipRecv );
1317
1318 if( ipRecv.find( "el" ) )
1319 {
1320 float elevation = ipRecv["el"].get<float>();
1321
1322 bool changed = false;
1323
1324 { // mutex scope
1325 std::lock_guard<std::mutex> lock( m_stateMutex );
1326 if( finiteValue( elevation ) && elevation != m_elevation )
1327 {
1328 m_elevation = elevation;
1329 changed = true;
1330 }
1331 }
1332
1333 if( changed )
1334 {
1335 std::cerr << "Got elevation: " << elevation << '\n';
1336
1337 updatePredictionOutputs();
1338 }
1339 }
1340 return 0;
1341}
1342
1343INDI_NEWCALLBACK_DEFN( strehlEstimator, m_indiP_mag )( const pcf::IndiProperty &ipRecv )
1344{
1345 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_mag, ipRecv );
1346
1347 if( ipRecv.find( "estimated" ) )
1348 {
1349 float mag = ipRecv["estimated"].get<float>();
1350
1351 bool changed = false;
1352
1353 { // mutex scope
1354 std::lock_guard<std::mutex> lock( m_stateMutex );
1355 if( finiteValue( mag ) && mag != m_magEstimated )
1356 {
1357 m_magEstimated = mag;
1358 m_magEstimatedManual = true;
1359 changed = true;
1360 }
1361 }
1362
1363 if( changed )
1364 {
1365 updatePlanningProperties();
1366 updatePredictionOutputs();
1367 }
1368 }
1369
1370 return 0;
1371}
1372
1373INDI_NEWCALLBACK_DEFN( strehlEstimator, m_indiP_seeing_magaox )( const pcf::IndiProperty &ipRecv )
1374{
1375 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_seeing_magaox, ipRecv );
1376
1377 if( ipRecv.find( "estimated" ) )
1378 {
1379 float seeing = ipRecv["estimated"].get<float>();
1380
1381 bool changed = false;
1382
1383 { // mutex scope
1384 std::lock_guard<std::mutex> lock( m_stateMutex );
1385 if( finitePositiveValue( seeing ) && seeing != m_seeingEstimated )
1386 {
1387 m_seeingEstimated = seeing;
1388 m_seeingEstimatedManual = true;
1389 changed = true;
1390 }
1391 }
1392
1393 if( changed )
1394 {
1395 updatePlanningProperties();
1396 updatePredictionOutputs();
1397 }
1398 }
1399
1400 return 0;
1401}
1402
1403INDI_NEWCALLBACK_DEFN( strehlEstimator, m_indiP_windSpeed )( const pcf::IndiProperty &ipRecv )
1404{
1405 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_windSpeed, ipRecv );
1406
1407 std::string selection;
1408 for( auto &&el : ipRecv.getElements() )
1409 {
1410 if( el.second.getSwitchState() == pcf::IndiElement::On )
1411 {
1412 selection = el.first;
1413 break;
1414 }
1415 }
1416
1417 if( selection == "" )
1418 {
1419 return 0;
1420 }
1421
1422 float windSpeed = windSpeedSelectionValue( selection );
1423 bool changed = false;
1424
1425 { // mutex scope
1426 std::lock_guard<std::mutex> lock( m_stateMutex );
1427 if( finitePositiveValue( windSpeed ) && windSpeed != m_windSpeed )
1428 {
1429 m_windSpeed = windSpeed;
1430 changed = true;
1431 }
1432 }
1433
1434 updatePlanningProperties();
1435
1436 if( changed )
1437 {
1438 updatePredictionOutputs();
1439 }
1440
1441 return 0;
1442}
1443
1444INDI_NEWCALLBACK_DEFN( strehlEstimator, m_indiP_useEstimates )( const pcf::IndiProperty &ipRecv )
1445{
1446 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_useEstimates, ipRecv );
1447
1448 if( !ipRecv.find( "toggle" ) )
1449 {
1450 return 0;
1451 }
1452
1453 bool useEstimates = ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On;
1454 bool changed = false;
1455
1456 { // mutex scope
1457 std::lock_guard<std::mutex> lock( m_stateMutex );
1458 if( useEstimates != m_useEstimates )
1459 {
1460 m_useEstimates = useEstimates;
1461 changed = true;
1462 }
1463 }
1464
1465 updatePlanningProperties();
1466
1467 if( changed )
1468 {
1469 updatePredictionOutputs();
1470 }
1471
1472 return 0;
1473}
1474
1475} // namespace app
1476} // namespace MagAOX
1477
1478#endif // strehlEstimator_hpp
The base-class for XWCTk applications.
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.
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.
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.
std::string configName()
Get the config name.
int createStandardIndiSelectionSw(pcf::IndiProperty &prop, const std::string &name, const std::vector< std::string > &elements, const std::vector< std::string > &elementLabels, const std::string &label="", const std::string &group="")
Create a standard R/W INDI selection (one of many) switch with vector of elements and element labels.
uint32_t m_width
The width of the images in the stream.
uint32_t m_height
The height of the images in the stream.
Predicts Strehl and WFE from live WFS telemetry and optional planning overrides.
virtual int appShutdown()
Shut down the shmim monitors.
float m_magEstimated
Operator-entered guide-star magnitude estimate.
float m_selectedSeeing
Selected seeing used for prediction.
INDI_SETCALLBACK_DECL(strehlEstimator, m_indiP_tcsi_telpos)
Callback for live TCS elevation updates.
float m_mag
Live guide-star magnitude derived from m_counts.
INDI_SETCALLBACK_DECL(strehlEstimator, m_indiP_tcsi_seeing)
Callback for live TCS seeing updates.
mx::AO::analysis::aoSystem< float, mx::AO::analysis::vonKarmanSpectrum< float > > aoSystemT
float selectedStarMag() const
Return the selected star magnitude for prediction calculations.
float m_windSpeed
Operator-selected wind speed in m/s used for planning calculations.
INDI_NEWCALLBACK_DECL(strehlEstimator, m_indiP_seeing_magaox)
Callback for local seeing estimate writes.
double m_dimm_fwhm_corr
Latest DIMM elevation-corrected FWHM.
float m_again
Analog gain factor converting WFS counts into photo-electrons.
bool m_seeingEstimatedManual
Tracks whether the estimated seeing has been explicitly set by an operator.
pcf::IndiProperty m_indiP_useEstimates
Local toggle selecting whether predicted outputs use estimated inputs.
float selectedSeeing() const
Return the selected seeing for prediction calculations.
static const std::vector< std::string > & windSpeedSelectionElements()
Return the supported wind-speed selection element names.
float m_selectedWindSpeed
Selected wind speed used for prediction.
float m_elevation
Telescope elevation in degrees.
virtual int appStartup()
Register INDI properties and transition the app into the operating state.
bool m_useEstimates
Whether estimated planning inputs are currently selected.
int allocate(const wfsavgShmimT &dummy)
React to allocation of the WFS average shmim stream.
void updateOptimumLoopSpeed(const predictionInputs &inputs)
Refresh the fixed-grid optimum-loop-speed summary property.
int loadConfigImpl(mx::app::appConfigurator &_config)
Load configuration values after setupConfig() has registered them.
INDI_NEWCALLBACK_DECL(strehlEstimator, m_indiP_windSpeed)
Callback for local wind-speed selection writes.
float m_windSpeed
Operator-selected wind speed in m/s.
pcf::IndiProperty m_indiP_loopSpeedOptimum
Summary property for the best fixed-grid loop speed.
std::string m_wfsDevice
WFS device providing the live FPS property.
~strehlEstimator() noexcept
Destroy the application.
float m_qe_HaIR
WFS QE for the Ha/IR beamsplitter branch.
pcf::IndiProperty m_indiP_fps
Subscription to the live WFS FPS property.
static float wfeNm(float variance, float lam0)
Convert AO model phase variance into WFE in nm RMS at the specified wavelength.
std::mutex m_predictionMutex
Serializes access to the shared AO-model instances and prediction-property updates.
virtual void setupConfig()
Declare configuration keys and initialize the AO models.
float m_lam0
Active wavelength in microns.
aoSystemT m_aosys
AO model used for the current predicted Strehl and WFE outputs.
INDI_SETCALLBACK_DECL(strehlEstimator, m_indiP_stage)
Callback for beamsplitter preset updates.
float m_qe
Active WFS quantum efficiency for the currently selected beamsplitter branch.
std::mutex m_stateMutex
Protects the live telemetry and planning-input state while prediction snapshots are assembled.
float m_lam0_HaIR
Effective WFS wavelength in microns for the Ha/IR beamsplitter branch.
void updatePredictionOutputs()
Refresh the predicted Strehl, WFE, and optimum-loop-speed properties.
void updatePlanningProperties()
Update the published planning-input properties from the current runtime state.
int processImage(void *curr_src, const wfsavgShmimT &dummy)
Process one WFS average frame.
bool m_useEstimates
Selects whether predicted outputs use the live or estimated planning inputs.
virtual int appLogic()
Refresh the AO predictions and service the shmim-monitor state machine.
bool m_magEstimatedManual
Tracks whether the estimated star magnitude has been explicitly set by an operator.
float m_F0
Active zero-magnitude photon flux for the selected beamsplitter branch.
float m_seeingEstimated
Operator-entered seeing in arcseconds used when planning overrides are enabled.
double m_mag1_fwhm_corr
Latest MAG1 elevation-corrected FWHM.
float m_fps
Live WFS frame rate in Hz.
float m_lam0_6535
Effective WFS wavelength in microns for the 65/35 beamsplitter branch.
float selectedWindSpeed() const
Return the selected wind speed for prediction calculations.
float m_selectedMag
Selected guide-star magnitude used for prediction.
static bool finiteValue(float value)
Return whether a value is finite.
pcf::IndiProperty m_indiP_stage
Subscription to the beamsplitter preset state.
static bool finitePositiveValue(float value)
Return whether a value is finite and strictly positive.
static std::string windSpeedSelectionName(float windSpeed)
Return the nearest supported wind-speed selection element name for a speed in m/s.
float m_lastLoggedSuspiciousOptimumFps
Tracks the last suspicious optimum FPS reported in debug logging.
float m_F0
Active zero-magnitude photon flux.
pcf::IndiProperty m_indiP_windSpeed
Local writable wind-speed selection property exposing slow, normal, and fast.
pcf::IndiProperty m_indiP_tcsi_seeing
Subscription to the TCS seeing property.
void configureAoSystem(aoSystemT &aosys, const predictionInputs &inputs, float fps, bool optimizeTau)
Configure an AO model for the selected inputs and requested loop speed.
float m_qe_6535
WFS QE for the 65/35 beamsplitter branch.
int m_npix
Number of illuminated WFS pixels in the current mask.
int m_mag1_time
Seconds since midnight of the latest MAG1 measurement.
float m_emg
Live EM gain reported by the WFS camera.
void calcMag()
Recalculate the live guide-star magnitude from the current WFS counts.
std::string m_stagebsDevice
Beamsplitter stage device used to choose the active photometric calibration.
pcf::IndiProperty m_indiP_wfe
Predicted WFE breakdown for the currently selected conditions.
dev::shmimMonitor< strehlEstimator, wfsmaskShmimT > wfsmaskShmimMonitorT
int m_loopNum
Loop number used to resolve the WFS shmim names.
int m_dimm_time
Seconds since midnight of the latest DIMM measurement.
static float windSpeedSelectionValue(const std::string &selection)
Convert a wind-speed selection element name into its configured speed in m/s.
strehlEstimator()
Construct the application with the compiled git-version metadata.
float m_counts
Total masked WFS counts used to derive the live guide-star magnitude.
INDI_SETCALLBACK_DECL(strehlEstimator, m_indiP_emg)
Callback for live EM-gain updates.
dev::shmimMonitor< strehlEstimator, wfsavgShmimT > wfsavgShmimMonitorT
static bool nearlyEqual(float a, float b, float relTol, float absTol)
Return whether two scalar AO-model inputs are effectively equal.
mx::improc::eigenImage< float > m_wfsavg
Latest WFS average image.
int createCurrentEstimatedProperty(pcf::IndiProperty &prop, const std::string &name, const std::string &label, const std::string &group)
Create a writable number property with current and estimated elements.
pcf::IndiProperty m_indiP_strehl
Predicted Strehl property for the currently selected conditions.
float m_r0
Live Fried parameter corresponding to m_seeing.
INDI_NEWCALLBACK_DECL(strehlEstimator, m_indiP_useEstimates)
Callback for the use_estimates toggle.
float m_magEstimated
Operator-entered star magnitude used when planning overrides are enabled.
mx::improc::eigenImage< float > m_wfsmask
Latest WFS mask image.
pcf::IndiProperty m_indiP_mag
Local writable star-magnitude property exposing current and estimated.
static float seeingToR0(float seeing)
Convert seeing in arcseconds to Fried parameter r0 in meters.
float m_lam0
Active WFS/science wavelength in microns for the selected beamsplitter branch.
static const std::vector< std::string > & windSpeedSelectionLabels()
Return the supported wind-speed selection labels.
double m_mag2_fwhm_corr
Latest MAG2 elevation-corrected FWHM.
INDI_NEWCALLBACK_DECL(strehlEstimator, m_indiP_mag)
Callback for local star-magnitude estimate writes.
INDI_SETCALLBACK_DECL(strehlEstimator, m_indiP_fps)
Callback for live FPS updates.
float m_F0_6535
Zero-magnitude photon flux for the 65/35 beamsplitter branch.
int m_mag2_time
Seconds since midnight of the latest MAG2 measurement.
pcf::IndiProperty m_indiP_seeing_magaox
Local writable seeing property exposing current and estimated.
virtual void loadConfig()
Load the configured runtime values.
pcf::IndiProperty m_indiP_emg
Subscription to the live WFS EM-gain property.
pcf::IndiProperty m_indiP_tcsi_telpos
Subscription to the TCS telescope position property.
float m_F0_HaIR
Zero-magnitude photon flux for the Ha/IR beamsplitter branch.
float m_seeingEstimated
Operator-entered seeing estimate in arcseconds.
float m_seeing
Live seeing estimate in arcseconds from tcsi.seeing.dimm_fwhm_corr.
predictionInputs snapshotPredictionInputs() const
Snapshot the scalar state used by the planning properties and AO-model calculations.
int m_npix
Current illuminated WFS-pixel count.
aoSystemT m_aosysScan
AO model dedicated to the fixed-FPS optimum-loop-speed scan.
float m_elevation
Telescope elevation in degrees.
Snapshot of the scalar inputs used to update the AO prediction model.
go_lp b(m_lp.m_c)
go_lp a(m_lp.m_c)
#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_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 CREATE_REG_INDI_RO_NUMBER(prop, name, label, group)
Create and register a RO INDI property as a number, using the standard callback name.
#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_OK
Definition indiUtils.hpp:28
void updateSelectionSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the values of a one-of-many INDI switch vector, but only if it has changed.
const pcf::IndiProperty & ipRecv
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition dm.hpp:19
#define SHMIMMONITORT_APP_STARTUP(SHMIMMONITORT)
Call shmimMonitorT::appStartup with error checking for a typedef-ed shmimMonitor.
#define SHMIMMONITORT_UPDATE_INDI(SHMIMMONITORT)
Call shmimMonitorT::updateINDI with error checking for a typedef-ed shmimMonitor.
#define SHMIMMONITORT_SETUP_CONFIG(SHMIMMONITORT, cfig)
Call shmimMonitorT::setupConfig with error checking for a typedef-ed shmimMonitor.
#define SHMIMMONITORT_APP_LOGIC(SHMIMMONITORT)
Call shmimMonitorT::appLogic with error checking for a typedef-ed shmimMonitor.
#define SHMIMMONITORT_APP_SHUTDOWN(SHMIMMONITORT)
Call shmimMonitorT::appShutodwn with error checking for a typedef-ed shmimMonitor.
#define SHMIMMONITORT_LOAD_CONFIG(SHMIMMONITORT, cfig)
Call shmimMonitorT::loadConfig with error checking for a typedef-ed shmimMonitor.
@ OPERATING
The device is operating, other than homing.
Tag type for the live WFS average shmim monitor.
static std::string configSection()
Return the configuration section name for this shmim monitor.
static std::string indiPrefix()
Return the INDI prefix for this shmim monitor.
Tag type for the WFS mask shmim monitor.
static std::string indiPrefix()
Return the INDI prefix for this shmim monitor.
static std::string configSection()
Return the configuration section name for this shmim monitor.
Software ERR log entry.
double variance(double data[], int size, int num_skip=0)