mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
Loading...
Searching...
No Matches
aoSystem.hpp
Go to the documentation of this file.
1/** \file aoSystem.hpp
2 * \author Jared R. Males (jaredmales@gmail.com)
3 * \brief Declares and defines an analytical AO system
4 * \ingroup mxAO_files
5 *
6 */
7
8//***********************************************************************//
9// Copyright 2015-2022 Jared R. Males (jaredmales@gmail.com)
10//
11// This file is part of mxlib.
12//
13// mxlib is free software: you can redistribute it and/or modify
14// it under the terms of the GNU General Public License as published by
15// the Free Software Foundation, either version 3 of the License, or
16// (at your option) any later version.
17//
18// mxlib is distributed in the hope that it will be useful,
19// but WITHOUT ANY WARRANTY; without even the implied warranty of
20// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21// GNU General Public License for more details.
22//
23// You should have received a copy of the GNU General Public License
24// along with mxlib. If not, see <http://www.gnu.org/licenses/>.
25//***********************************************************************//
26
27#ifndef aoSystem_hpp
28#define aoSystem_hpp
29
30#include "../../mxlib.hpp"
31#include "../../math/constants.hpp"
32#include "../../math/roots.hpp"
33
34#include "aoConstants.hpp"
35
36#include "aoAtmosphere.hpp"
37#include "aoPSDs.hpp"
38#include "aoWFS.hpp"
39
40#define FITTING_ERROR_NO 0
41#define FITTING_ERROR_ZERO 1
42#define FITTING_ERROR_X 2
43#define FITTING_ERROR_Y 3
44
45namespace mx
46{
47namespace AO
48{
49namespace analysis
50{
51
52/// Describes an analytic adaptive optics (AO) system.
53/**
54 * Templatized by the turbulence power spectral density (PSD).
55 *
56 * \tparam realT the floating point type used for all calculations
57 * \tparam inputSpecT specifies the turbulence spatial PSD type
58 * \tparam iosT is an output stream type with operator << defined (default is std::ostream)
59 *
60 * \ingroup mxAOAnalytic
61 */
62template <typename _realT, class _inputSpectT, typename iosT = std::ostream>
64{
65 typedef verbose::d verboseT;
66
67 public:
68 typedef _realT realT;
69 typedef _inputSpectT inputSpectT;
70 typedef aoAtmosphere<realT> aoAtmosphereT;
71
72 aoAtmosphereT atm;
73 inputSpectT psd;
74
75 protected:
76 realT m_D{ 0 }; ///< Telescope diameter [m]
77
78 std::vector<realT> m_d_min{ 0 }; ///< Minimum AO system actuator pitch [m]. One per WFS mode.
79
80 realT m_d_opt{ 0 }; ///< Current optimum AO system actuator pitch [m]
81 bool m_optd{ false }; ///< Flag controlling whether actuator pitch is optimized (true) or just uses m_d_min (false).
82 ///< Default: true.
83 realT m_optd_delta{ 1.0 }; ///< The fractional change from d_min used in optimization. Set to 1 for integer
84 ///< binnings, > 1 for finer sampling.
85
86 wfs<realT, iosT> *m_wfsBeta{ nullptr }; ///< The WFS beta_p class.
87 bool m_ownWfsBeta{ false }; ///< Flag indicating if the WFS beta_p pointer is owned by this instance.
88
89 realT m_opticalGain{ 1 }; /**< The optical gain of the WFS, 0-1. Treated as a sensitivity
90 reduction in measurement noise. Default 1.*/
91
92 protected:
93 realT m_lam_wfs{ 0 }; ///< WFS wavelength [m]
94
95 // WFS detector parameters, which may be a function of binning
96 std::vector<realT> m_npix_wfs{ 0 }; ///< Number of WFS pixels. One per WFS mode.
97 std::vector<realT> m_ron_wfs{ 0 }; ///< WFS readout noise [electrons/pix]. One per WFS mode.
98 std::vector<realT> m_Fbg{ 0 }; ///< Background flux, [photons/sec/pixel]. One per WFS mode.
99 std::vector<realT> m_minTauWFS{ 0 }; ///< Minimum WFS exposure time [sec]. One per WFS mode.
100
101 bool m_bin_npix{ true }; ///< Flag controlling whether or not to bin WFS pixels according to the actuator spacing.
102
103 int m_bin_opt{ 0 }; /**< The optimum binning factor. If WFS modes are used, this is the mode
104 index (0 to N-1). If not, it is 1 minus the pixel binning factor.
105 It is always 1 minus the actuator binning factor. */
106
107 realT m_tauWFS{ 0 }; ///< Actual WFS exposure time [sec]
108
109 realT m_deltaTau{ 0 }; ///< Loop latency [sec]
110
111 bool m_optTau{ true }; ///< Flag controlling whether optimum integration time is calculated (true) enforcing
112 ///< m_minTauWFS, or if m_tauWFS is used (false). Default: true.
113
114 realT m_lam_sci{ 0 }; ///< Science wavelength [m]
115
116 realT m_zeta{ 0 }; ///< Zenith angle [radians]
117 realT m_secZeta{ 1 }; ///< Secant of the Zenith angle (calculated)
118
119 int m_fit_mn_max{ 100 }; ///< Maximum spatial frequency index to use for fitting error calculation.
120
121 bool m_circularLimit{ false }; ///< Flag to indicate that the spatial frequency limit is circular, not square.
122
123 realT m_spatialFilter_ku{ std::numeric_limits<realT>::max() }; ///< The spatial filter cutoff in u, [m^-1]
124 realT m_spatialFilter_kv{ std::numeric_limits<realT>::max() }; ///< The spatial filter cutoff in v, [m^-1]
125
126 realT m_ncp_wfe{ 0 }; ///< Static WFE [m rms]
127 realT m_ncp_alpha{ 2 }; ///< Power-law exponent for the NCP aberations. Default is 2.
128
129 realT m_F0{ 0 }; ///< 0 mag flux from star at WFS [photons/sec]
130
131 realT m_starMag{ 0 }; ///< The magnitude of the star.
132
133 bool m_specsChanged{ true }; ///< Flag to indicate that a specification has changed.
134 bool m_dminChanged{ true }; ///< Flag to indicate that d_min has changed.
135
136 Eigen::Array<int, -1, -1> m_controlledModes; ///< Map of which modes are under control. Set by calcStrehl.
137
138 realT m_wfeMeasurement{ 0 }; ///< Total WFE due to measurement a error [rad^2 at m_lam_sci]
139 realT m_wfeTimeDelay{ 0 }; ///< Total WFE due to time delay [rad^2 at m_lam_sci]
140 realT m_wfeFitting{ 0 }; ///< Total WFE due to fitting error [rad^2 at m_lam_sci]
141 realT m_wfeChromScintOPD{ 0 }; ///< Total WFE due to the chromaticity of scintillation OPD [rad^2 at lam_sc]
142 realT m_wfeChromIndex{ 0 }; ///< Total WFE due to the chromaticity of the index of refraction [rad^2 at lam_Sci]
143 realT m_wfeAnisoOPD{ 0 }; ///< Total WFE due to dispersive anisoplanatism OPD.
144
145 realT m_wfeNCP{ 0 }; ///< Total WFE due to NCP errors [rad^2 at m_lam_sci]
146
147 realT m_wfeVar{ 0 }; ///< The WFE variance, in meters^2. Never use this directly, instead use wfeVar().
148 realT m_strehl{ 0 }; ///< Strehl ratio, a calculated quantity. Never use this directdy, instead use strehl().
149
150 public:
151 /// Default c'tor
152 /** Calls initialize().
153 */
155
156 /// Destructor
158
159 /// Load the default parameters from Guyon, 2005 \cite guyon_2005.
160 /**
161 *
162 */
164
165 /// Load parameters corresponding to the MagAO-X system.
167
168 /// Load parameters corresponding to the G-MagAO-X system.
170
171 /// Set the value of the primary mirror diameter.
172 /**
173 */
174 void D( realT nD /**< [in] is the new value of m_D. */ );
175
176 /// Get the value of the primary mirror diamter
177 /**
178 * \returns the current value of m_D.
179 */
180 realT D();
181
182 /// Set the minimum subaperture sampling for each WFS mode.
183 /** This sets the vector
184 */
185 void d_min( const std::vector<realT> &nd /**< [in] is the new values of m_d_min */ );
186
187 /// Set the minimum subaperture sampling for a given WFS mode
188 /** This sets the entry in the vector, if it has the proper length.
189 */
190 void d_min( int idx, ///< [in] the index of the WFS mode
191 realT nd ///< [in] is the new value of m_d_min[idx]
192 );
193
194 /// Get the minimum subaperture sampling for a given WFS mode
195 /**
196 * \returns the current value of m_d_min[idx]
197 */
198 realT d_min( size_t idx /**< [in] the index of the WFS mode.*/ );
199
200 /// Get the minimum subaperture sampling for all WFS modes.
201 /**
202 * \returns the current value of m_d_min.
203 */
204 std::vector<realT> d_min();
205
206 /// Set whether or not the value of d is optimized or just set to m_d_min.
207 /**
208 */
209 void optd( bool od /**< [in] is the new value of m_optd */ );
210
211 /// Get the value of m_optd.
212 /**
213 * \returns the new value of m_optd_delta.
214 */
215 bool optd();
216
217 /// Set the fractional change in actuator spacing for optimization.
218 /** Sets the fraction of m_d_min by which the optimizer changes actautor spacing.
219 */
220 void optd_delta( realT odd /**< [in] is the new value of m_optd_delta */ );
221
222 /// Get the value of the fractional change in actuator spacing for optimization..
223 /**
224 * \returns the value of m_optd_delta.
225 */
226 realT optd_delta();
227
228 /// Set the WFS Beta pointer
229 /** The WFS beta parameter determines the photon noise sensitivity.
230 *
231 * \tparam wfsT is a type derived from ao::analysis::wfs
232 */
233 template <typename wfsT>
234 void wfsBeta( const wfsT &w /**< [in] an object derived from ao::analysis::wfs base class*/ );
235
236 /// Set the WFS Beta pointer
237 /** The WFS beta parameter determines the photon noise sensitivity.
238 *
239 * \tparam wfsT is a type derived from ao::analysis::wfs
240 */
241 template<typename wfsT>
242 void wfsBeta( const wfsT * w /**< [in] pointer to an object derived from ao::analysis::wfs base class. If nullptr then a new object is allocated and managed.*/);
243
244 /// Get the WFS Beta pointer
245 /**
246 * \returns a pointer to the current m_wfsBeta
247 */
249
250 /// Get the value of beta_p for a spatial frequency
251 /** beta_p is the photon noise sensitivity of the WFS.
252 *
253 * \returns beta_p as calculated by the WFS.
254 */
255 realT beta_p( realT m, ///< [in] the spatial frequency index
256 realT n ///< [in] the spatial frequency index
257 );
258
259 /// Get the value of beta_r for a spatial frequency
260 /** beta_r is the read noise sensitivity of the WFS.
261 *
262 * \returns beta_r as calculated by the WFS.
263 */
264 realT beta_r( realT m, ///< [in] the spatial frequency index
265 realT n ///< [in] the spatial frequency index
266 );
267
268 /// Set the value of the Optical Gain.
269 /**
270 */
271 void opticalGain( realT og /**< [in] the new value of m_opticalGain*/ );
272
273 /// Get the value of the optical gain
274 /** Optical gain is applied in the WFS noise calculation
275 *
276 * \returns the current value of m_opticalGain
277 */
278 realT opticalGain();
279
280 /// Set the value of the WFS wavelength.
281 /**
282 */
283 void lam_wfs( realT nlam /**< [in] is the new value of m_lam_wfs */ );
284
285 /// Get the value of the WFS wavelength.
286 /**
287 * \returns the current value of m_lam_wfs.
288 */
289 realT lam_wfs();
290
291 /// Set the number of pixels in the WFS for each WFS mode
292 /** This sets the vector.
293 */
294 void npix_wfs( const std::vector<realT> &npix /**< [in] is the new values of m_npix_wfs */ );
295
296 /// Set the number of pixels in the WFS for a given WFS mode
297 /** This sets the entry in the vector, if it has the proper length.
298 */
299 void npix_wfs( int idx, ///< [in] the index of the WFS mode
300 realT npix ///< [in] is the new value of m_npix_wfs[idx]
301 );
302
303 /// Get the number of pixels in the WFS for a given WFS mode
304 /**
305 * \returns the current value of m_npix_wfs[idx]
306 */
307 realT npix_wfs( size_t idx /**< [in] the index of the WFS mode.*/ );
308
309 /// Get the number of pixels in the WFS for all WFS modes
310 /**
311 * \returns the current value of m_npix_wfs
312 */
313 std::vector<realT> npix_wfs();
314
315 /// Set the value of the WFS readout noise for each WFS mode
316 /** This sets the vector.
317 */
318 void ron_wfs( const std::vector<realT> &nron /**< [in] is the new value of m_ron_wfs */ );
319
320 /// Set the value of the WFS readout noise for a given bining mode
321 /** This sets the entry in the vector if the vector has the proper length
322 */
323 void ron_wfs( int idx, ///< [in] the index of the WFS mode
324 realT nron ///< [in] is the new value of m_ron_wfs [idx]
325 );
326
327 /// Get the value of the WFS readout noise for a given WFS mode
328 /**
329 * \returns the current value of m_ron_wfs[idx]
330 */
331 realT ron_wfs( size_t idx /**< [in] the index of the WFS mode.*/ );
332
333 /// Get the value of the WFS readout noise for all WFS modes
334 /**
335 * \returns the current value of m_ron_wfs
336 */
337 std::vector<realT> ron_wfs();
338
339 /// Set the value of the background fluxes.
340 /** This sets the value of the background flux for each WFS mode
341 */
342 void Fbg( const std::vector<realT> &fbg /**< [in] is the new value of m_Fbg */ );
343
344 /// Set a single value of the background flux for a given WFS mode
345 /** This sets the entry in the vector if the vector has the proper length
346 */
347 void Fbg( int idx, ///< [in] the index of the WFS mode
348 realT fbg ///< [in] is the new value of m_Fbg[idx]
349 );
350
351 /// Get the value of the background flux for a given WFS mode
352 /**
353 * \returns m_Fbg[idx]
354 */
355 realT Fbg( size_t idx /**< [in] the index of the WFS mode.*/ );
356
357 /// Get the value of the background flux for all WFS modes
358 /**
359 * \returns the current value of m_Fbg
360 */
361 std::vector<realT> Fbg();
362
363 /// Set the value of the minimum WFS exposure times.
364 /** This sets the vector of the minimum WFS exposure times
365 */
366 void minTauWFS( const std::vector<realT> &ntau /**< [in] is the new value of m_minTauWFS */ );
367
368 /// Set a single value of the minimum WFS exposure time for a given WFS mode.
369 /** This sets the entry in the vector if the vector has sufficient length.
370 */
371 void minTauWFS( size_t idx, ///< [in] the index of the WFS mode
372 realT ntau ///< [in] is the new value of m_minTauWFS
373 );
374
375 /// Get the value of the minimum WFS exposure time for a given binning.
376 /**
377 * \returns the current value of m_minTauWFS[idx].
378 */
379 realT minTauWFS( size_t idx /**< [in] the index of the WFS mode.*/ );
380
381 /// Get the values of the minimum WFS exposure time.
382 /**
383 * \returns the current value of m_minTauWFS.
384 */
385 std::vector<realT> minTauWFS();
386
387 /// Set the value of the pixel binning flag
388 /**
389 */
390 void bin_npix( bool bnp /**< [in] is the new value of m_bin_npix */ );
391
392 /// Get the value of the pixel binngin flag
393 /**
394 * \returns
395 */
396 bool bin_npix();
397
398 /// Get the value of the optimum binning factor/index.
399 /** If WFS modes are used, this is the mode index (0 to N-1). If not, it is 1 minus the pixel binning factor.
400 * It is always 1 minus the actuator binning factor.
401 * This calls opt_d() to perform the optimization if needed.
402 *
403 * \returns the current value of m_bin_opt.
404 */
405 int bin_opt();
406
407 /// Set the value of the WFS exposure time.
408 /**
409 */
410 void tauWFS( realT ntau /**< [in] is the new value of m_tauWFS */ );
411
412 /// Get the value of the minimum WFS exposure time.
413 /**
414 * \returns the current value of m_tauWFS.
415 */
416 realT tauWFS();
417
418 /// Set the value of m_deltaTau.
419 /**
420 */
421 void deltaTau( realT ndel /**< [in] is the new value of m_deltaTau*/ );
422
423 /// Get the value of m_deltaTau.
424 /**
425 * \returns the current value of m_deltaTau.
426 */
427 realT deltaTau();
428
429 /// Set the value of m_optTau.
430 /**
431 */
432 void optTau( bool ot /**< [in] is the new value of m_optTau */ );
433
434 /// Get the value of m_optTau.
435 /**
436 * \returns the current value of m_optTau.
437 */
438 bool optTau();
439
440 /// Set the science wavelength.
441 /**
442 */
443 void lam_sci( realT nlam /**< [in] is the new value of m_lam_sci */ );
444
445 /// Get the science wavelength.
446 /**
447 * \returns the current value of m_lam_sci.
448 */
449 realT lam_sci();
450
451 /// Set the zenith angle, and its secant.
452 /**
453 */
454 void zeta( realT nz /**< [in] The new value of m_zeta */ );
455
456 /// Get the zenith angle
457 /**
458 * \return the current value of m_zeta
459 */
460 realT zeta();
461
462 /// Get the zecant of the zenith angle
463 /**
464 * \return the current value of m_secZeta
465 */
466 realT secZeta();
467
468 /// Set the value of m_fit_mn_max
469 /**
470 */
471 void fit_mn_max( int mnm /**< [in] is the new value of m_fit_mn_max */ );
472
473 /// Get the value of m_fit_mn_max
474 /**
475 */
477
478 /// Set the value of the circularLimit flag
479 /** If this is true, then the spatial frequency limits are treated as a circle
480 * rather than a square. This will create a circular dark hole.
481 */
482 void circularLimit( bool cl /**< [in] the new value of the circularLimit flag*/ );
483
484 /// Get the value of the circularLimit flag
485 /**
486 * \returns the current value of m_circularLimit
487 */
489
490 /// Set the value of spatialFilter_ku
491 /**
492 */
493 void spatialFilter_ku( realT ku /**< [in] is the new value of spatialFilter_ku*/ );
494
495 /// Get the value of spatialFilter_ku
496 /** \returns the current value of m_spatialFilter_ku
497 */
499
500 /// Set the value of spatialFilter_kv
501 /**
502 */
503 void spatialFilter_kv( realT kv /**< [in] is the new value of spatialFilter_kv*/ );
504
505 /// Get the value of spatialFilter_kv
506 /** \returns the current value of m_spatialFilter_kv
507 */
509
510 /// Set the value of the non-common path WFE.
511 /**
512 */
513 void ncp_wfe( realT nwfe /**< [in] is the new value of m_ncp_wfe*/ );
514
515 /// Get the value of the non-common path WFE.
516 /**
517 * \returns the current value of m_ncp_wfe.
518 */
519 realT ncp_wfe();
520
521 /// Set the value of the non-common path WFE PSD index.
522 /**
523 */
524 void ncp_alpha( realT alpha /**< [in] is the new value of m_ncp_alpha*/ );
525
526 /// Get the value of the non-common path WFE PSD index.
527 /**
528 * \returns the current value of m_ncp_alpha.
529 */
530 realT ncp_alpha();
531
532 /// Set the value of the 0 magnitude photon rate
533 /** This is the photon rate at the WFS, photons/sec.
534 *
535 */
536 void F0( realT nF0 /**< [in] is the new value of m_F0.*/ );
537
538 /// Get the value of the 0 magnitude photon rate
539 /**
540 * \returns the current value of m_F0.
541 */
542 realT F0();
543
544 /// Set the value of the Star's magnitude
545 /**
546 */
547 void starMag( realT nmag /**< [in] is the new value of m_starMag.*/ );
548
549 /// Get the value of the Star's magnitude
550 /**
551 * \returns the current value of m_starMag
552 */
553 realT starMag();
554
555 /// The photon flux at a given star magnitude.
556 /**
557 * \returns the photon flux of the star in the bandpass assumed by F0.
558 */
559 realT Fg( realT mag /**< [in] is the magnitude of the star. */ );
560
561 /// Get the photon rate at the current Star magnitude.
562 /** Calculates \f$ F_\gamma = F_0 10^{-0.4 m} \f$ where \f$ F_0 \f$ is m_F0 and \f$ m \f$ is m_starMag.
563 *
564 * \returns the current value of the current photon rate.
565 */
566 realT Fg();
567
568 /// Access the array of controlledModes
569 /** This array indicates which modes are controlled after optimizeStrehl chooses them.
570 *
571 * \returns a const reference to m_controlledModes.
572 */
573 const Eigen::Array<int, -1, -1> &controlledModes();
574
575 /** \name Measurement Error
576 * Calculating the WFE due to WFS measurement noise.
577 * @{
578 */
579
580 /// Calculate the terms of the signal to noise ratio squared (S/N)^2 for the WFS measurement
581 /** The S/N squared is
582 \f[
583 (S/N)^2 = \frac{ F_\gamma^2 \tau_{wfs}^2 }{ F_\gamma \tau_{wfs} + n_{pix} F_{bg} \tau_{wfs} + n_{pix}
584 \sigma_{ron}^2 } \f]
585
586 *
587 * \returns the S/N squared
588 */
589 realT
590 signal2Noise2( realT &Nph, ///< [out] the number of photons
591 realT tau_wfs, ///< [in] specifies the WFS exposure time. If 0, then optimumTauWFS is used
592 realT d, ///< [in] the actuator spacing in meters, used if binning WFS pixels
593 int b ///< [in] the binning parameter. Either the WFS mode index, or the binning factor minus 1.
594 );
595
596 /// Calculate the measurement noise at a spatial frequency and specified actuator spacing
597 /** Calculates the wavefront phase variance due measurement noise at \f$ k = (m/D)\hat{u} + (n/D)\hat{v} \f$.
598 *
599 * \returns the measurement noise in rad^2 rms at the science wavelength
600 */
601 realT
602 measurementError( realT m, ///< [in] specifies the u component of the spatial frequency
603 realT n, ///< [in] specifies the v component of the spatial frequency
604 realT d, ///< [in] the actuator spacing in meters
605 int b ///< [in] the binning parameter. Either the WFS mode index, or the binning factor minus 1.
606 );
607
608 /// Calculate the total measurement error over all corrected spatial frequencies
609 /** This totals the measurement WFE at the optimum actuator spacing and binning.
610 *
611 * \overload
612 *
613 * \returns the total WFE due to measurement error.
614 */
616
617 ///@}
618
619 /** \name Time Delay Error
620 * Calculating the WFE due to time delay.
621 * @{
622 */
623
624 /// Calculate the time delay at a spatial frequency at the optimum exposure time and the specified actuator spacing
625 /** Calculates the wavefront phase variance due to time delay at \f$ f = (m/D)\hat{u} + (n/D)\hat{v} \f$.
626 *
627 * \returns the measurement noise in rad^2 rms at the science wavelength
628 */
629 realT
630 timeDelayError( realT m, ///< [in] specifies the u component of the spatial frequency
631 realT n, ///< [in] specifies the v component of the spatial frequency
632 realT d, ///< [in] the actuator spacing, in meters
633 int b ///< [in] the binning parameter. Either the WFS mode index, or the binning factor minus 1.
634 );
635
636 /// Calculate the time delay error over all corrected spatial frequencies
637 /** This totals the time delay WFE at the optimum actuator spacing and binning.
638 *
639 * \overload
640 *
641 * \returns the total WFE due to time delay.
642 */
644
645 ///@}
646
647 /** \name Fitting Error
648 * Calculating the WFE due to uncorrected spatial frequencies.
649 * @{
650 */
651
652 /// Calculate the fitting error at a specific spatial frequency.
653 /**
654 * \returns the fitting error in rad^2 rms at the science wavelength at (m,n).
655 */
656 realT fittingError( realT m, ///< [in] specifies the u component of the spatial frequency
657 realT n ///< [in] specifies the v component of the spatial frequency
658 );
659
660 /// Calculate the total fitting error over all uncorrected spatial frequencies.
661 /** This totals the fitting WFE for the optimum actuator spacing and binning.
662 *
663 * \overload
664 *
665 * \returns the total fitting error.
666 */
668
669 ///@}
670
671 /** \name Chromatic Errors
672 * Calculating the WFE due to chromaticity in scintillaion, index of refraction, and dispersion.
673 * @{
674 */
675
676 /// Calculate the wavefront error due to scintillation chromaticity in the OPD at a spatial frequency.
677 /** This totals the WFE from scintillation chromaticity in the OPD.
678 *
679 * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
680 */
681 realT chromScintOPDError( int m, ///< [in] specifies the u component of the spatial frequency
682 int n ///< [in] specifies the v component of the spatial frequency
683 );
684
685 /// Calculate the wavefront error due to scintillation chromaticity in the OPD over all spatial frequencies.
686 /** This totals the WFE from scintillation chromaticity in the OPD for the optimum actuator spacing.
687 *
688 * \overload
689 *
690 * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
691 */
693
694 /// Calculate the wavefront error due to scintillation chromaticity in amplitude for a given spatial frequency.
695 /**
696 * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
697 */
698 realT chromScintAmpError( int m, ///< [in] specifies the u component of the spatial frequency
699 int n ///< [in] specifies the v component of the spatial frequency
700 );
701
702 /// Calculate the wavefront error due to scintillation chromaticity in amplitude over all spatial frequencies.
703 /**
704 * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
705 */
707
708 /// Calculate the wavefront error due to chromaticity in the index of refraction at a given spatial frequency.
709 /** This totals the WFE from chromaticity in the index of refraction.
710 *
711 * \overload
712 *
713 * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
714 */
715 realT chromIndexError( int m, ///< [in] specifies the u component of the spatial frequency
716 int n ///< [in] specifies the v component of the spatial frequency
717 );
718
719 /// Calculate the wavefront error due to chromaticity in the index of refraction at a specific spatial frequency.
720 /** This totals the WFE from chromaticity in the index of refraction for the optimum actuator spacing.
721 *
722 * \overload
723 *
724 * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
725 */
727
728 /// Calculate the wavefront error due to dispersive anisoplanatism in the OPD at a given spatial frequency
729 /** This calculates the WFE from dispersive anisoplanatism in the OPD.
730 *
731 * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
732 */
733 realT dispAnisoOPDError( int m, ///< [in] specifies the u component of the spatial frequency
734 int n ///< [in] specifies the v component of the spatial frequency
735 );
736
737 /// Calculate the wavefront error due to dispersive anisoplanatism in the OPD over all specific spatial frequencies.
738 /** This totals the WFE from dispersive anisoplanatism in the OPD for the optimum actuator spacing.
739 *
740 * \overload
741 *
742 * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
743 */
745
746 /// Calculate the wavefront error due to dispersive anisoplanatism in the amplitude at a specific spatial frequency.
747 /**
748 * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
749 */
751
752 ///@}
753
754 /** \name Optimum Parameters
755 * Functions to calculate the optimum integration time and actuator spacing.
756 *
757 * @{
758 */
759
760 /// Calculate the optimum exposure time for a given spatial frequency at a specified actuator spacing
761 /** Finds the optimum exposure time at \f$ k = (m/D)\hat{u} + (n/D)\hat{v} \f$.
762 *
763 * \todo Check inclusion of X in parameters
764 *
765 * \returns the optimum expsoure time.
766 */
767 realT
768 optimumTauWFS( realT m, ///< [in] is the spatial frequency index in u
769 realT n, ///< [in] is the spatial frequency index in v
770 realT d, ///< [in] the actuator spacing, in meters
771 int b ///< [in] the binning parameter. Either the WFS mode index, or the binning factor minus 1.
772 );
773
774 /// Calculate the optimum exposure time for a given spatial frequency at the optimum actuator spacing.
775 /** Finds the optimum exposure time at \f$ k = (m/D)\hat{u} + (n/D)\hat{v} \f$.
776 * If optd == false this uses the minimum actuator spacing.
777 *
778 * \todo Check inclusion of X in parameters
779 *
780 * \returns the optimum expsoure time.
781 */
782 realT optimumTauWFS( realT m, ///< [in] is the spatial frequency index in u
783 realT n ///< [in] is the spatial frequency index in v
784 );
785
786 /// Calculate the optimum actuator spacing.
787 /** Finds the value of m_d_opt where the fitting error is less than than the combined time delay and measurement
788 * error.
789 *
790 * \returns the current value of m_d_opt.
791 */
792 realT d_opt();
793
794 /// @}
795
796 /// Calculate the NCP variance at a spatial frequency.
797 /** Finds the NCP variance at \f$ k = (m/D)\hat{u} + (n/D)\hat{v} \f$.
798 *
799 * \returns the NCP variance at k.
800 */
801 realT ncpError( int m, ///< [in] is the spatial frequency index in u
802 int n ///< [in] is the spatial frequency index in v
803 );
804
805 /// Calculate the total NCP variance in rad^2.
806 /**
807 * \returns the total NCP variance.
808 */
809 realT ncpError();
810
811 /** \name Overall WFE and Strehl Ratio
812 * Functions to calculate the total WFE and Strehl ratio.
813 *
814 * @{
815 */
816
817 protected:
818 /// Calculate the component WFE, total WFE, and Strehl ratio.
819 /** This should only be called when something changes.
820 */
822
823 public:
824 /// Get the current value of the total WFE variance.
825 /** If no changes, merely returns m_wfeVar. Calls calcStrehl if there are changes.
826 *
827 * \returns the current value of m_wfeVar;
828 */
829 realT wfeVar();
830
831 /// Get the Strehl ratio for a given actuator pitch and WFS WFS mode.
832 /**
833 * Strehl is calculated using the extended Marechal approximation:
834 *
835 \f[
836 S = e^{-\sigma_{wfe}^2}
837 \f]
838 * where \f$ \sigma_{wfe}^2 \f$ is the WFE for the spacing and binning
839 *
840 * \returns the strehl for these values
841 */
842 realT strehl( realT d, ///< [in] the actuator spacing, in meters
843 int b ///< [in] the binning parameter. Either the WFS mode index, or the binning factor minus 1.
844 );
845
846 /// Get the current Strehl ratio.
847 /** If no changes, merely returns m_strehl. Calls calcStrehl if there are changes.
848 * Strehl is calculated using the extended Marechal approximation:
849 *
850 \f[
851 S = e^{-\sigma_{wfe}^2}
852 \f]
853 * where \f$ \sigma_{wfe}^2 \f$ is the current value of m_wfeVar.
854 *
855 * \returns the current value of m_strehl.
856 */
857 realT strehl();
858
859 /// @}
860
861 /** \name Contrast
862 * Calculating contrast
863 *
864 * @{
865 */
866
867 /// Worker function for raw contrast fuctions
868 /** The logic in this calculation is the same regardless of component, except for the calculation of variance.
869 * The varFunc function pointer is used to calculate the variance, otherwise This handles the other details such
870 * as Strehl normalization, fitting error (if appropriate), and bounds checking.
871 *
872 * \note this gives the raw PSD, it must be convolved with the PSF for the true contrast.
873 *
874 * \tparam varFuncT is a function-pointer-to-member (of this class) with signature realT(*f)(realT, realT)
875 */
876 template <typename varFuncT>
877 realT C_( int m, ///< [in] is the spatial frequency index in u/
878 int n, ///< [in] is the spatial frequency index in v.
879 bool normStrehl, ///< [in] flag controls whether the contrast is normalized by Strehl ratio/
880 varFuncT varFunc, ///< [in] the variance function to use.
881 int doFittingError ///< [in] flag to describe how fitting error should be considered for this term:
882 ///< FITTING_ERROR_NO, FITTING_ERROR_ZERO, FITTING_ERROR_X, or FITTING_ERROR_Y.
883 );
884
885 /// Worker function for the contrast-map functions.
886 /** The map calculation is the same for all terms, except for the calculation of variance at each pixel.
887 * The Cfunc function pointer is used to actually get the contrast.
888 *
889 * \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
890 *
891 * \tparam imageT is an Eigen-like image
892 * \tparam CfuncT is a function-pointer-to-member (of this class) with signature realT (*f)(realT, realT, bool)
893 */
894 template <typename imageT, typename CfuncT>
895 void C_Map( imageT &im, ///< [out] the map image to be filled in.
896 CfuncT Cfunc, ///< [in] the raw contrast function to use for filling in the map.
897 bool normStrehl ///< [in] flag controlling whether or not the map is normalized by Strehl
898 );
899
900 /// Calculate the residual variance due to uncorrected phase at a spatial frequency.
901 /** Used to calculate contrast \ref C0().
902 *
903 * \returns variance at (m,n).
904 */
905 realT C0var( realT m, ///< [in] is the spatial frequency index in u
906 realT n ///< [in] is the spatial frequency index in v
907 );
908
909 /// Calculate the contrast due to uncorrected phase, C0.
910 /** Contrast C0 is the uncorrected phase, with the effects of scintillation included. See Guyon (2005)
911 * \cite guyon_2005 and the updated derivation in Males \& Guyon (2018) \cite males_guyon_2018
912 *
913 * \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
914 *
915 * \returns C0.
916 */
917 realT C0( realT m, ///< [in] is the spatial frequency index in u
918 realT n, ///< [in] is the spatial frequency index in v
919 bool normStrehl = true ///< [in] flag controls whether the contrast is normalized by Strehl ratio
920 );
921
922 /// Calculate a 2D map of contrast C0
923 /**
924 * \tparam imageT is an Eigen-like image type.
925 */
926 template <typename imageT>
927 void C0Map( imageT &map, ///< [in] the map image to be filled in with contrast
928 bool normStrehl );
929
930 /// Calculate the residual variance due to uncorrected amplitude at a spatial frequency.
931 /** Used to calculate contrast \ref C1().
932 *
933 * \returns variance at (m,n).
934 */
935 realT C1var( realT m, ///< [in] is the spatial frequency index in u
936 realT n ///< [in] is the spatial frequency index in v
937 );
938
939 /// Calculate the contrast due to uncorrected amplitude, C1.
940 /** Contrast C1 is the uncorrected amplitude due to scintillation. See Guyon (2005) \cite guyon_2005, and the
941 * updated derivation in Males \& Guyon (2018) \cite males_guyon_2018.
942 *
943 * \returns C0.
944 */
945 realT C1( realT m, ///< [in] is the spatial frequency index in u
946 realT n, ///< [in] is the spatial frequency index in v
947 bool normStrehl = true ///< [in] flag controls whether the contrast is normalized by Strehl ratio
948 );
949
950 /// Calculate a 2D map of contrast C1.
951 /** The contrast is Strehl-normalized here.
952 *
953 * \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
954 *
955 * \tparam imageT is an Eigen-like image type.
956 */
957 template <typename imageT>
958 void C1Map( imageT &map, ///< [in] the map image to be filled in with contrast
959 bool normStrehl );
960
961 /// Calculate the residual variance due to measurement and time delay errors in phase/OPD at a spatial frequency.
962 /** Used to calculate contrast \ref C2().
963 *
964 * \returns variance at (m,n).
965 */
966 realT C2var( realT m, ///< [in] is the spatial frequency index in u
967 realT n ///< [in] is the spatial frequency index in v
968 );
969
970 /// Calculate the contrast due to measurement and time delay errors in phase/OPD at a spatial frequency.
971 /** Contrast C2 is just the total variance due to time delay and measurement errors,
972 * divided by the Strehl ratio. See Guyon (2005) \cite guyon_2005, and the updated
973 * derivation in Males \& Guyon (2018) \cite males_guyon_2018.
974 *
975 * \returns C2.
976 */
977 realT C2( realT m, ///< [in] is the spatial frequency index in u
978 realT n, ///< [in] is the spatial frequency index in v
979 bool normStrehl = true ///< [in] flag controls whether the contrast is normalized by Strehl ratio.
980 );
981
982 /// Calculate a 2D map of contrast \ref C2().
983 /** The contrast is Strehl-normalized here.
984 *
985 * \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
986 *
987 * \tparam imageT is an Eigen-like image type.
988 */
989 template <typename imageT>
990 void C2Map( imageT &map, ///< [in] the map image to be filled in with contrast
991 bool normStrehl );
992
993 /// Calculate the residual variance due to measurement and time delay errors in amplitude at a spatial frequency.
994 /** Used to calculate contrast \ref C3().
995 *
996 * \returns variance at (m,n).
997 */
998 realT C3var( realT m, ///< [in] is the spatial frequency index in u
999 realT n ///< [in] is the spatial frequency index in v
1000 );
1001
1002 /// Calculate the contrast due to measurement and time delay errors in amplitude at a spatial frequency.
1003 /** Contrast C3 is just the total variance due to time delay and measurement errors,
1004 * divided by the Strehl ratio. See Guyon (2005) \cite guyon_2005, and the updated
1005 * derivation in Males \& Guyon (2018) \cite males_guyon_2018.
1006 *
1007 * \returns C3.
1008 */
1009 realT C3( realT m, ///< [in] is the spatial frequency index in u
1010 realT n, ///< [in] is the spatial frequency index in v
1011 bool normStrehl = true ///< [in] flag controls whether the contrast is normalized by Strehl ratio.
1012 );
1013
1014 /// Calculate a 2D map of contrast C3.
1015 /** The contrast is Strehl-normalized here.
1016 *
1017 * \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
1018 *
1019 * \tparam imageT is an Eigen-like image type.
1020 */
1021 template <typename imageT>
1022 void C3Map( imageT &map, ///< [in] the map image to be filled in with contrast
1023 bool normStrehl );
1024
1025 /// Calculate the residual variance due to scintilation-OPD chromaticity.
1026 /** Used to calculate contrast \ref C4().
1027 *
1028 * \returns variance at (m,n).
1029 */
1030 realT C4var( realT m, ///< [in] is the spatial frequency index in u
1031 realT n ///< [in] is the spatial frequency index in v
1032 );
1033
1034 /// Calculate the contrast due to scintilation-OPD chromaticity.
1035 /** Contrast C4 is due to the chromaticity of scintillation, causing the ODP measurement to be slightly incorrect at
1036 * the science wavelength. See Guyon (2005) \cite guyon_2005, and the updated derivation in Males \& Guyon (2018)
1037 * \cite males_guyon_2018.
1038 *
1039 * \returns C4.
1040 */
1041 realT C4( realT m, ///< [in] is the spatial frequency index in u
1042 realT n, ///< [in] is the spatial frequency index in v
1043 bool normStrehl = true ///< [in] flag controls whether the contrast is normalized by Strehl ratio.
1044 );
1045
1046 /// Calculate a 2D map of contrast C4
1047 /** The contrast is Strehl-normalized here.
1048 *
1049 * \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
1050 *
1051 * \tparam imageT is an Eigen-like image type.
1052 */
1053 template <typename imageT>
1054 void C4Map( imageT &map, ///< [in] the map image to be filled in with contrast
1055 bool normStrehl );
1056
1057 /// Calculate the residual variance due to to scintilation-amplitude chromaticity.
1058 /** Used to calculate contrast \ref C5().
1059 *
1060 * \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
1061 *
1062 * \returns variance at (m,n).
1063 */
1064 realT C5var( realT m, ///< [in] is the spatial frequency index in u
1065 realT n ///< [in] is the spatial frequency index in v
1066 );
1067
1068 /// Calculate the contrast due to scintilation-amplitude chromaticity.
1069 /** Contrast C5 is due to the chromaticity of scintillation, causing the amplitude measurement to be slightly
1070 * incorrect at the science wavelength. See Guyon (2005) \cite guyon_2005, and the updated derivation in Males \&
1071 * Guyon (2018) \cite males_guyon_2018.
1072 *
1073 * \returns C4.
1074 */
1075 realT C5( realT m, ///< [in] is the spatial frequency index in u
1076 realT n, ///< [in] is the spatial frequency index in v
1077 bool normStrehl = true ///< [in] flag controls whether the contrast is normalized by Strehl ratio.
1078 );
1079
1080 /// Calculate a 2D map of contrast C5
1081 /** The contrast is Strehl-normalized here.
1082 *
1083 * \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
1084 *
1085 * \tparam imageT is an Eigen-like image type.
1086 */
1087 template <typename imageT>
1088 void C5Map( imageT &map, ///< [in] the map image to be filled in with contrast
1089 bool normStrehl );
1090
1091 /// Calculate the residual variance due to chromaticity of the index of refraction of air.
1092 /** Used to calculate contrast \ref C6().
1093 *
1094 * \returns variance at (m,n).
1095 */
1096 realT C6var( realT m, ///< [in] is the spatial frequency index in u
1097 realT n ///< [in] is the spatial frequency index in v
1098 );
1099
1100 /// Calculate the contrast due to chromaticity of the index of refraction of air.
1101 /** Contrast C6 is due to the index of refraction of air being wavelength dependent, causing the ODP measurement to
1102 * be slightly incorrect at the science wavelength. See Guyon (2005) \cite guyon_2005, and the updated derivation
1103 * in Males \& Guyon (2018) \cite males_guyon_2018.
1104 *
1105 * \returns C6.
1106 */
1107 realT C6( realT m, ///< [in] is the spatial frequency index in u
1108 realT n, ///< [in] is the spatial frequency index in v
1109 bool normStrehl = true ///< flag controls whether the contrast is normalized by Strehl ratio.
1110 );
1111
1112 /// Calculate a 2D map of contrast C6
1113 /** The contrast is Strehl-normalized here.
1114 *
1115 * \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
1116 *
1117 * \tparam imageT is an Eigen-like image type.
1118 */
1119 template <typename imageT>
1120 void C6Map( imageT &map, ///< [in] the map image to be filled in with contrast
1121 bool normStrehl );
1122
1123 /// Calculate the residual variance due to dispersive anisoplanatism.
1124 /** Used to calculate contrast \ref C7().
1125 *
1126 * \returns variance at (m,n).
1127 */
1128 realT C7var( realT m, ///< [in] is the spatial frequency index in u
1129 realT n ///< [in] is the spatial frequency index in v
1130 );
1131
1132 /// Calculate the contrast due to dispersive anisoplanatism.
1133 /** Contrast C7 is due to atmospheric dispersion causing light at different wavelengths to take different paths
1134 * through atmospheric turbulence. See Fitzgerald (2017, in prep).
1135 *
1136 * \returns C7.
1137 */
1138 realT C7( realT m, ///< [in] is the spatial frequency index in u
1139 realT n, ///< [in] is the spatial frequency index in v
1140 bool normStrehl = true ///< flag controls whether the contrast is normalized by Strehl ratio.
1141 );
1142
1143 /// Calculate a 2D map of contrast C7
1144 /** The contrast is Strehl-normalized here.
1145 *
1146 * \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
1147 *
1148 * \tparam imageT is an Eigen-like image type.
1149 */
1150 template <typename imageT>
1151 void C7Map( imageT &map, ///< [in] the map image to be filled in with contrast
1152 bool normStrehl );
1153
1154 ///@}
1155
1156 /// Output current parameters to a stream
1157 /** Outputs a formatted list of all current parameters.
1158 *
1159 */
1160 iosT &dumpAOSystem( iosT &ios /**< [in] a std::ostream-like stream. */ );
1161
1162 /// Setup the configurator to configure this class.
1163 void setupConfig( app::appConfigurator &config /**< [in] the app::configurator object*/ );
1164
1165 /// Load the configuration of this class from a configurator.
1166 /**
1167 * \returns `error_t::noerror` when the configured atmosphere is valid, or its typed validation error.
1168 */
1169 error_t loadConfig( app::appConfigurator &config /**< [in] the app::configurator object*/ );
1170};
1171
1172template <typename realT, class inputSpectT, typename iosT>
1174{
1175 wfsBeta<wfs<realT, iosT>>( nullptr ); // allocate a default ideal WFS.
1176}
1177
1178template <typename realT, class inputSpectT, typename iosT>
1180{
1181 if( m_wfsBeta && m_ownWfsBeta )
1182 {
1183 delete m_wfsBeta;
1184 }
1185}
1186
1187template <typename realT, class inputSpectT, typename iosT>
1189{
1190 atm.loadGuyon2005();
1191
1192 F0( 1.75e9 * 0.25 * math::pi<realT>() * 64. * 0.18 ); // Converting to photons/sec
1193 lam_wfs( 0.55e-6 );
1194 lam_sci( 1.6e-6 );
1195 D( 8. );
1196 starMag( 5 );
1197
1198 // The rest of Guyon 05 is very ideal
1199 npix_wfs( std::vector<realT>( { 12868 } ) );
1200 ron_wfs( std::vector<realT>( { 0.0 } ) );
1201 Fbg( std::vector<realT>( { 0.0 } ) );
1202
1203 d_min( 8.0 / 1e3 ); // Allow super fine sampling
1204 minTauWFS( (realT)( 1. / 1e9 ) ); // Just be super fast.
1205 tauWFS( 1. / 1e9 ); // Just be super fast
1206
1207 m_specsChanged = true;
1208 m_dminChanged = true;
1209}
1210
1211template <typename realT, class inputSpectT, typename iosT>
1213{
1214 atm.loadLCO();
1215 F0( 4.2e10 );
1216 lam_wfs( 0.791e-6 );
1217 lam_sci( 0.656e-6 );
1218
1219 npix_wfs( std::vector<realT>( { 9024 } ) );
1220 ron_wfs( std::vector<realT>( { 0.57 } ) );
1221 Fbg( std::vector<realT>( { 0.22 } ) );
1222
1223 d_min( std::vector<realT>( { 6.5 / 48.0 } ) );
1224 minTauWFS( std::vector<realT>( { 1. / 3622. } ) );
1225 tauWFS( 1. / 3622. );
1226
1227 D( 6.5 );
1228
1229 m_specsChanged = true;
1230 m_dminChanged = true;
1231}
1232
1233template <typename realT, class inputSpectT, typename iosT>
1235{
1236
1237 loadMagAOX();
1238
1239 F0( 7.6e10 * 368.0 / ( 0.25 * math::pi<realT>() * 6.5 * 6.5 * ( 1. - 0.29 * 0.29 ) ) ); // Scaled up.
1240
1241 D( 25.4 );
1242
1243 m_specsChanged = true;
1244 m_dminChanged = true;
1245}
1246
1247template <typename realT, class inputSpectT, typename iosT>
1249{
1250 m_D = nD;
1251 psd.D( m_D );
1252
1253 m_specsChanged = true;
1254 m_dminChanged = true;
1255}
1256
1257template <typename realT, class inputSpectT, typename iosT>
1259{
1260 return m_D;
1261}
1262
1263template <typename realT, class inputSpectT, typename iosT>
1264void aoSystem<realT, inputSpectT, iosT>::d_min( const std::vector<realT> &nd )
1265{
1266 m_d_min.assign( nd.begin(), nd.end() );
1267 m_specsChanged = true;
1268 m_dminChanged = true;
1269}
1270
1271template <typename realT, class inputSpectT, typename iosT>
1273{
1274 if( m_d_min.size() < idx + 1 )
1275 {
1276 throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_d_min" ) );
1277 }
1278
1279 m_d_min[idx] = nd;
1280
1281 m_specsChanged = true;
1282 m_dminChanged = true;
1283}
1284
1285template <typename realT, class inputSpectT, typename iosT>
1287{
1288 if( m_d_min.size() < idx + 1 )
1289 {
1290 throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_d_min" ) );
1291 }
1292
1293 return m_d_min[idx];
1294}
1295
1296template <typename realT, class inputSpectT, typename iosT>
1298{
1299 return m_d_min;
1300}
1301
1302template <typename realT, class inputSpectT, typename iosT>
1304{
1305 m_optd = od;
1306 m_specsChanged = true;
1307 m_dminChanged = true;
1308}
1309
1310template <typename realT, class inputSpectT, typename iosT>
1312{
1313 return m_optd;
1314}
1315
1316template <typename realT, class inputSpectT, typename iosT>
1318{
1319 m_optd_delta = odd;
1320 m_specsChanged = true;
1321 m_dminChanged = true;
1322}
1323
1324template <typename realT, class inputSpectT, typename iosT>
1329
1330template <typename realT, class inputSpectT, typename iosT>
1331template <typename wfsT>
1333{
1334 if( m_wfsBeta && m_ownWfsBeta )
1335 {
1336 delete m_wfsBeta;
1337 m_wfsBeta = nullptr;
1338 }
1339
1341 m_ownWfsBeta = false;
1342}
1343
1344template <typename realT, class inputSpectT, typename iosT>
1345template <typename wfsT>
1347{
1348 if( m_wfsBeta && m_ownWfsBeta )
1349 {
1350 delete m_wfsBeta;
1351 m_wfsBeta = nullptr;
1352 }
1353
1354 if( w )
1355 {
1357 m_ownWfsBeta = false;
1358 }
1359 else
1360 {
1361 m_wfsBeta = new wfsT;
1362 m_ownWfsBeta = true;
1363 }
1364}
1365
1366template <typename realT, class inputSpectT, typename iosT>
1371
1372template <typename realT, class inputSpectT, typename iosT>
1374{
1375 if( m_wfsBeta == 0 )
1376 {
1377 throw( mx::exception<verboseT>( error_t::paramnotset, "The WFS is not assigned." ) );
1378 }
1379
1380 return m_wfsBeta->beta_p( m, n, m_D, d_opt(), atm.r_0( m_lam_sci ) );
1381}
1382
1383template <typename realT, class inputSpectT, typename iosT>
1385{
1386 if( m_wfsBeta == 0 )
1387 {
1388 throw( mx::exception<verboseT>( error_t::paramnotset, "The WFS is not assigned." ) );
1389 }
1390 return m_wfsBeta->beta_r( m, n, m_D, d_opt(), atm.r_0( m_lam_sci ) );
1391}
1392
1393template <typename realT, class inputSpectT, typename iosT>
1395{
1396 m_opticalGain = og;
1397 m_specsChanged = true;
1398 m_dminChanged = true;
1399}
1400
1401template <typename realT, class inputSpectT, typename iosT>
1406
1407template <typename realT, class inputSpectT, typename iosT>
1409{
1410 m_lam_wfs = nlam;
1411 m_specsChanged = true;
1412 m_dminChanged = true;
1413}
1414
1415template <typename realT, class inputSpectT, typename iosT>
1420
1421template <typename realT, class inputSpectT, typename iosT>
1422void aoSystem<realT, inputSpectT, iosT>::npix_wfs( const std::vector<realT> &npix )
1423{
1424 m_npix_wfs.resize( npix.size() );
1425 for( size_t n = 0; n < npix.size(); ++n )
1426 {
1427 m_npix_wfs[n] = npix[n];
1428 }
1429
1430 m_specsChanged = true;
1431 m_dminChanged = true;
1432}
1433
1434template <typename realT, class inputSpectT, typename iosT>
1436{
1437 if( m_npix_wfs.size() < idx + 1 )
1438 {
1439 throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_npix_wfs" ) );
1440 }
1441
1442 m_npix_wfs[idx] = npix;
1443
1444 m_specsChanged = true;
1445 m_dminChanged = true;
1446}
1447
1448template <typename realT, class inputSpectT, typename iosT>
1450{
1451 if( m_npix_wfs.size() < idx + 1 )
1452 {
1453 throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_npix_wfs" ) );
1454 }
1455
1456 return m_npix_wfs[idx];
1457}
1458
1459template <typename realT, class inputSpectT, typename iosT>
1461{
1462 return m_npix_wfs;
1463}
1464
1465template <typename realT, class inputSpectT, typename iosT>
1466void aoSystem<realT, inputSpectT, iosT>::ron_wfs( const std::vector<realT> &nron )
1467{
1468 m_ron_wfs.resize( nron.size() );
1469 for( size_t n = 0; n < nron.size(); ++n )
1470 {
1471 m_ron_wfs[n] = nron[n];
1472 }
1473
1474 m_specsChanged = true;
1475 m_dminChanged = true;
1476}
1477
1478template <typename realT, class inputSpectT, typename iosT>
1480{
1481 if( m_ron_wfs.size() < idx + 1 )
1482 {
1483 throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_ron_wfs" ) );
1484 }
1485
1486 m_ron_wfs[idx] = nron;
1487
1488 m_specsChanged = true;
1489 m_dminChanged = true;
1490}
1491
1492template <typename realT, class inputSpectT, typename iosT>
1494{
1495 if( m_ron_wfs.size() < idx + 1 )
1496 {
1497 throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_ron_wfs" ) );
1498 }
1499
1500 return m_ron_wfs[idx];
1501}
1502
1503template <typename realT, class inputSpectT, typename iosT>
1505{
1506 return m_ron_wfs;
1507}
1508
1509template <typename realT, class inputSpectT, typename iosT>
1510void aoSystem<realT, inputSpectT, iosT>::Fbg( const std::vector<realT> &fbg )
1511{
1512 m_Fbg.resize( fbg.size() );
1513 for( size_t n = 0; n < fbg.size(); ++n )
1514 {
1515 m_Fbg[n] = fbg[n];
1516 }
1517
1518 m_specsChanged = true;
1519 m_dminChanged = true;
1520}
1521
1522template <typename realT, class inputSpectT, typename iosT>
1524{
1525 if( m_Fbg.size() < idx + 1 )
1526 {
1527 throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_Fbg" ) );
1528 }
1529
1530 m_Fbg[idx] = fbg;
1531
1532 m_specsChanged = true;
1533 m_dminChanged = true;
1534}
1535
1536template <typename realT, class inputSpectT, typename iosT>
1538{
1539 if( m_Fbg.size() < idx + 1 )
1540 {
1541 throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_Fbg" ) );
1542 }
1543
1544 return m_Fbg[idx];
1545}
1546
1547template <typename realT, class inputSpectT, typename iosT>
1549{
1550 return m_Fbg;
1551}
1552
1553template <typename realT, class inputSpectT, typename iosT>
1554void aoSystem<realT, inputSpectT, iosT>::minTauWFS( const std::vector<realT> &ntau )
1555{
1556 m_minTauWFS.resize( ntau.size() );
1557 for( size_t n = 0; n < ntau.size(); ++n )
1558 {
1559 m_minTauWFS[n] = ntau[n];
1560 }
1561
1562 m_specsChanged = true;
1563 m_dminChanged = true;
1564}
1565
1566template <typename realT, class inputSpectT, typename iosT>
1568{
1569 if( m_minTauWFS.size() < idx + 1 )
1570 {
1571 throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_ntau_wfs" ) );
1572 }
1573
1574 m_minTauWFS[idx] = ntau;
1575
1576 m_specsChanged = true;
1577 m_dminChanged = true;
1578}
1579
1580template <typename realT, class inputSpectT, typename iosT>
1582{
1583 if( m_minTauWFS.size() < idx + 1 )
1584 {
1585 throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_ntau_wfs" ) );
1586 }
1587
1588 return m_minTauWFS[idx];
1589}
1590
1591template <typename realT, class inputSpectT, typename iosT>
1593{
1594 return m_minTauWFS;
1595}
1596
1597template <typename realT, class inputSpectT, typename iosT>
1599{
1600 if( bnp != m_bin_npix )
1601 {
1602 m_bin_npix = bnp;
1603 m_specsChanged = true;
1604 m_dminChanged = true;
1605 }
1606}
1607
1608template <typename realT, class inputSpectT, typename iosT>
1613
1614template <typename realT, class inputSpectT, typename iosT>
1616{
1617 d_opt();
1618 return m_bin_opt;
1619}
1620
1621template <typename realT, class inputSpectT, typename iosT>
1623{
1624 m_tauWFS = ntau;
1625 m_specsChanged = true;
1626 m_dminChanged = true;
1627}
1628
1629template <typename realT, class inputSpectT, typename iosT>
1634
1635template <typename realT, class inputSpectT, typename iosT>
1637{
1638 m_deltaTau = ndel;
1639 m_specsChanged = true;
1640 m_dminChanged = true;
1641}
1642
1643template <typename realT, class inputSpectT, typename iosT>
1648
1649template <typename realT, class inputSpectT, typename iosT>
1651{
1652 m_optTau = ot;
1653 m_specsChanged = true;
1654 m_dminChanged = true;
1655}
1656
1657template <typename realT, class inputSpectT, typename iosT>
1662
1663template <typename realT, class inputSpectT, typename iosT>
1665{
1666 m_lam_sci = nlam;
1667 m_specsChanged = true;
1668 m_dminChanged = true;
1669}
1670
1671template <typename realT, class inputSpectT, typename iosT>
1676
1677template <typename realT, class inputSpectT, typename iosT>
1679{
1680 m_zeta = nz;
1681 m_secZeta = 1 / cos( m_zeta );
1682
1683 m_specsChanged = true;
1684 m_dminChanged = true;
1685}
1686
1687template <typename realT, class inputSpectT, typename iosT>
1689{
1690 return m_zeta;
1691}
1692
1693template <typename realT, class inputSpectT, typename iosT>
1698
1699template <typename realT, class inputSpectT, typename iosT>
1701{
1702 if( mnm < 0 )
1703 mnm = 0;
1704 m_fit_mn_max = mnm;
1705}
1706
1707template <typename realT, class inputSpectT, typename iosT>
1712
1713template <typename realT, class inputSpectT, typename iosT>
1715{
1716 m_circularLimit = cl;
1717 m_specsChanged = true;
1718 m_dminChanged = true;
1719}
1720
1721template <typename realT, class inputSpectT, typename iosT>
1726
1727template <typename realT, class inputSpectT, typename iosT>
1729{
1730 m_spatialFilter_ku = fabs( kx );
1731 m_specsChanged = true; // not sure if needed
1732 m_dminChanged = true; // not sure if needed
1733}
1734
1735template <typename realT, class inputSpectT, typename iosT>
1740
1741template <typename realT, class inputSpectT, typename iosT>
1743{
1744 m_spatialFilter_kv = fabs( ky );
1745 m_specsChanged = true; // not sure if needed
1746 m_dminChanged = true; // not sure if needed
1747}
1748
1749template <typename realT, class inputSpectT, typename iosT>
1754
1755template <typename realT, class inputSpectT, typename iosT>
1757{
1758 m_ncp_wfe = nwfe;
1759 m_specsChanged = true;
1760 m_dminChanged = true;
1761}
1762
1763template <typename realT, class inputSpectT, typename iosT>
1768
1769template <typename realT, class inputSpectT, typename iosT>
1771{
1772 m_ncp_alpha = alpha;
1773 m_specsChanged = true;
1774 m_dminChanged = true;
1775}
1776
1777template <typename realT, class inputSpectT, typename iosT>
1782
1783template <typename realT, class inputSpectT, typename iosT>
1785{
1786 m_F0 = nF0;
1787 m_specsChanged = true;
1788 m_dminChanged = true;
1789}
1790
1791template <typename realT, class inputSpectT, typename iosT>
1793{
1794 return m_F0;
1795}
1796
1797template <typename realT, class inputSpectT, typename iosT>
1799{
1800 m_starMag = nmag;
1801 m_specsChanged = true;
1802 m_dminChanged = true;
1803}
1804
1805template <typename realT, class inputSpectT, typename iosT>
1810
1811template <typename realT, class inputSpectT, typename iosT>
1813{
1814 return m_F0 * pow( 10.0, -0.4 * mag );
1815}
1816
1817template <typename realT, class inputSpectT, typename iosT>
1819{
1820 return Fg( m_starMag );
1821}
1822
1823template <typename realT, class inputSpectT, typename iosT>
1825{
1827 calcStrehl();
1828
1829 return m_controlledModes;
1830}
1831
1832template <typename realT, class inputSpectT, typename iosT>
1833realT aoSystem<realT, inputSpectT, iosT>::signal2Noise2( realT &Nph, realT tau_wfs, realT d, int b )
1834{
1835 Nph = Fg() * tau_wfs;
1836
1837 double binfact = 1.0;
1838 int binidx = 0;
1839 if( m_bin_npix )
1840 {
1841 if( m_npix_wfs.size() == 1 ) // Only if "true binning", otherwise the WFS mode vectors handle it.
1842 {
1843 binfact = 1. / pow( (realT)b + 1, 2 );
1844 }
1845 else
1846 binidx = b;
1847 }
1848
1849 return pow( Nph, 2 ) / ( m_npix_wfs[binidx] * binfact * ( m_Fbg[binidx] * tau_wfs + pow( m_ron_wfs[binidx], 2 ) ) );
1850}
1851
1852template <typename realT, class inputSpectT, typename iosT>
1853realT aoSystem<realT, inputSpectT, iosT>::measurementError( realT m, realT n, realT d, int b )
1854{
1855 if( m == 0 and n == 0 )
1856 return 0;
1857
1858 realT tau_wfs;
1859
1860 if( m_optTau )
1861 tau_wfs = optimumTauWFS( m, n, d, b );
1862 else
1863 tau_wfs = m_tauWFS;
1864
1865 if( m_wfsBeta == 0 )
1866 {
1867 throw( mx::exception<verboseT>( error_t::paramnotset, "The WFS is not assigned." ) );
1868 }
1869
1870 realT beta_p = m_wfsBeta->beta_p( m, n, m_D, d, atm.r_0( m_lam_wfs ) ) / sqrt( m_opticalGain );
1871 realT beta_r = m_wfsBeta->beta_r( m, n, m_D, d, atm.r_0( m_lam_wfs ) ); // sqrt(m_opticalGain);
1872 realT Nph = 0;
1873 realT snr2 = signal2Noise2( Nph, tau_wfs, d, b );
1874
1875 return ( pow( beta_r, 2 ) / snr2 + pow( beta_p, 2 ) / Nph ) * pow( m_lam_wfs / m_lam_sci, 2 );
1876}
1877
1878template <typename realT, class inputSpectT, typename iosT>
1885
1886template <typename realT, class inputSpectT, typename iosT>
1887realT aoSystem<realT, inputSpectT, iosT>::timeDelayError( realT m, realT n, realT d, int b )
1888{
1889 if( m == 0 and n == 0 )
1890 return 0;
1891
1892 realT k = sqrt( m * m + n * n ) / m_D;
1893
1894 realT tau_wfs;
1895
1896 if( m_optTau )
1897 tau_wfs = optimumTauWFS( m, n, d, b );
1898 else
1899 tau_wfs = m_tauWFS;
1900
1901 realT tau = tau_wfs + m_deltaTau;
1902
1903 ///\todo handle multiple layers
1904 return psd( atm, 0, k, m_secZeta ) / pow( m_D, 2 ) * pow( atm.lam_0() / m_lam_sci, 2 ) *
1905 sqrt( atm.X( k, m_lam_sci, m_secZeta ) ) * pow( math::two_pi<realT>() * atm.v_wind() * k, 2 ) *
1906 pow( tau, 2 );
1907}
1908
1909template <typename realT, class inputSpectT, typename iosT>
1916
1917template <typename realT, class inputSpectT, typename iosT>
1919{
1920 realT k = sqrt( m * m + n * n ) / m_D;
1921
1922 ///\todo handle multiple layers
1923 return psd( atm, 0, k, m_secZeta ) / pow( m_D, 2 ) * pow( atm.lam_0() / m_lam_sci, 2 );
1924}
1925
1926template <typename realT, class inputSpectT, typename iosT>
1933
1934template <typename realT, class inputSpectT, typename iosT>
1936{
1937 return C4var( m, n );
1938}
1939
1940template <typename realT, class inputSpectT, typename iosT>
1947
1948template <typename realT, class inputSpectT, typename iosT>
1950{
1951 return C5var( m, n );
1952
1953#if 0
1954 int mn_max = floor(0.5*m_D/d_opt());
1955
1956 realT sum = 0;
1957
1958 for(int m = -mn_max; m <= mn_max; ++m)
1959 {
1960 for(int n = -mn_max; n <= mn_max; ++n)
1961 {
1962 if(n == 0 && m == 0) continue;
1963
1964 sum += C5var(m,n);
1965 }
1966 }
1967
1968 return sum;
1969#endif
1970}
1971
1972template <typename realT, class inputSpectT, typename iosT>
1974{
1975 return C6var( m, n );
1976}
1977
1978template <typename realT, class inputSpectT, typename iosT>
1985
1986template <typename realT, class inputSpectT, typename iosT>
1988{
1989 return C7var( m, n );
1990}
1991
1992template <typename realT, class inputSpectT, typename iosT>
1999
2000template <typename realT, class inputSpectT, typename iosT>
2002{
2003 return 0;
2004
2005#if 0
2006 int mn_max = floor(0.5*m_D/d_opt());
2007
2008 realT sum = 0;
2009
2010 for(int m = -mn_max; m <= mn_max; ++m)
2011 {
2012 for(int n = -mn_max; n <= mn_max; ++n)
2013 {
2014 if(n == 0 && m == 0) continue;
2015
2016 if( m_circularLimit )
2017 {
2018 if( m*m + n*n > mn_max*mn_max ) continue;
2019 }
2020
2021 sum += C8var(m,n);
2022 }
2023 }
2024
2025 return sum;
2026#endif
2027}
2028
2029template <typename realT, class inputSpectT, typename iosT>
2031 realT m,
2032 realT n,
2033 realT dact, // here called dact due to parameter collision with root-finding
2034 int bbin )
2035{
2036 if( m_D == 0 )
2037 {
2038 internal::mxlib_error_report( error_t::paramnotset, "Diameter (D) not set." );
2039 return -1;
2040 }
2041
2042 if( m_F0 == 0 )
2043 {
2044 internal::mxlib_error_report( error_t::paramnotset, "0-mag photon flux (F0) not set." );
2045 return -1;
2046 }
2047
2048 double binfact = 1.0;
2049 int binidx = 0; // index into WFS configurations
2050 if( m_bin_npix )
2051 {
2052 if( m_npix_wfs.size() > 1 )
2053 {
2054 binidx = bbin;
2055 }
2056 else
2057 {
2058 binfact = 1.0 / pow( (realT)( bbin + 1 ), 2 );
2059 }
2060 }
2061
2062 realT k = sqrt( m * m + n * n ) / m_D;
2063
2064 realT F = Fg();
2065
2066 if( m_wfsBeta == 0 )
2067 {
2068 throw( mx::exception<verboseT>( error_t::paramnotset, "The WFS is not assigned." ) );
2069 }
2070
2071 realT beta_p = m_wfsBeta->beta_p( m, n, m_D, dact, atm.r_0( m_lam_wfs ) ) / sqrt( m_opticalGain );
2072 realT beta_r = m_wfsBeta->beta_r( m, n, m_D, dact, atm.r_0( m_lam_wfs ) ) / sqrt( m_opticalGain );
2073
2074 // Set up for root finding:
2075 realT a, b, c, d, e;
2076
2077 ///\todo handle multiple layers
2078 realT Atmp = 2 * pow( atm.lam_0(), 2 ) * psd( atm, 0, k, m_secZeta ) / pow( m_D, 2 ) *
2079 ( atm.X( k, m_lam_wfs, m_secZeta ) ) * pow( math::two_pi<realT>() * atm.v_wind() * k, 2 );
2080
2081 a = Atmp;
2082 b = Atmp * m_deltaTau;
2083 c = 0;
2084 d = -1 * ( pow( m_lam_wfs, 2 ) / F ) *
2085 ( ( m_npix_wfs[binidx] * binfact * m_Fbg[binidx] / F ) * pow( beta_r, 2 ) + pow( beta_p, 2 ) );
2086 e = -2 * pow( m_lam_wfs, 2 ) * ( m_npix_wfs[binidx] * binfact ) * pow( m_ron_wfs[binidx], 2 ) * pow( beta_r, 2 ) /
2087 pow( F, 2 );
2088
2089 std::vector<std::complex<realT>> x;
2090
2091 // Get the roots
2092 math::quarticRoots( x, a, b, c, d, e );
2093
2094 // Now pick the largest positive real root
2095 realT tauopt = 0.0;
2096
2097 for( int i = 0; i < 4; i++ )
2098 {
2099 if( real( x[i] ) > 0 && imag( x[i] ) == 0 && real( x[i] ) > tauopt )
2100 tauopt = real( x[i] );
2101 }
2102
2103 if( tauopt < m_minTauWFS[binidx] )
2104 tauopt = m_minTauWFS[binidx];
2105
2106 return tauopt;
2107}
2108
2109template <typename realT, class inputSpectT, typename iosT>
2111{
2112 d_opt(); // also gets m_bin_opt;
2113 return optimumTauWFS( m, n, m_d_opt, m_bin_opt );
2114}
2115
2116template <typename realT, class inputSpectT, typename iosT>
2118{
2119 if( !m_dminChanged )
2120 return m_d_opt;
2121
2122 // Validate the modes
2123 ///\todo this should be in a separate valideModes function
2124 if( m_npix_wfs.size() < 1 )
2125 {
2126 throw( mx::exception<verboseT>( error_t::sizeerr, "npix_wfs must have at least one entry" ) );
2127 }
2128
2129 if( m_d_min.size() != m_npix_wfs.size() )
2130 {
2131 throw( mx::exception<verboseT>( error_t::sizeerr, "d_min must be the same size as npix_wfs" ) );
2132 }
2133
2134 if( m_ron_wfs.size() != m_npix_wfs.size() )
2135 {
2136 throw( mx::exception<verboseT>( error_t::sizeerr, "ron_wfs must be the same size as npix_wfs" ) );
2137 }
2138
2139 if( m_Fbg.size() != m_npix_wfs.size() )
2140 {
2141 throw( mx::exception<verboseT>( error_t::sizeerr, "F_bg must be the same size as npix_wfs" ) );
2142 }
2143
2144 if( m_minTauWFS.size() != m_npix_wfs.size() )
2145 {
2146 throw( mx::exception<verboseT>( error_t::sizeerr, "minTauWFS must be the same size as npix_wfs" ) );
2147 }
2148
2149 ///\todo investigate why we tolerate m_d_min = 0 here. Is this just a config error?
2150 if( m_d_min.size() == 1 && m_d_min[0] == 0 )
2151 {
2152 m_d_opt = 1e-50;
2153 m_bin_opt = 0;
2154 m_dminChanged = false;
2155 return m_d_opt;
2156 }
2157
2158 if( !m_optd )
2159 {
2160 m_d_opt = m_d_min[0];
2161 m_bin_opt = 0;
2162 m_dminChanged = false;
2163
2164 return m_d_opt;
2165 }
2166
2167 realT best_d = 0;
2168
2169 if( m_bin_npix )
2170 {
2171 if( m_npix_wfs.size() > 1 ) // Optimize over the WFS modes
2172 {
2173 int best_idx = 0;
2174 best_d = m_d_min[0];
2175
2176 realT bestStrehlOverall = 0;
2177
2178 for( int b = 0; b < m_npix_wfs.size(); ++b )
2179 {
2180 realT d = m_d_min[b];
2181 realT s = strehl( d, b );
2182 realT bestStrehl = s;
2183
2184 // Find the actuator pitch at which AO error is less than fitting error at Nyquist
2185 while( d < m_D / 2 )
2186 {
2187 d += m_d_min[b] / m_optd_delta;
2188 s = strehl( d, b );
2189
2190 if( s < bestStrehl ) // Strehl got worse. Can't be <= otherwise small m_optd_deltas will
2191 // immediately out
2192 {
2193 d -= m_d_min[b] / m_optd_delta; // go back to previous d
2194 break;
2195 }
2196 else
2197 bestStrehl = s;
2198 }
2199
2200 // Check if this is optimum so far
2201 if( bestStrehl >= bestStrehlOverall ) // >= to favor fewer actuators
2202 {
2203 bestStrehlOverall = bestStrehl;
2204 best_idx = b;
2205 best_d = d;
2206 }
2207 }
2208
2209 m_bin_opt = best_idx;
2210 }
2211 else
2212 {
2213 realT d = m_d_min[0];
2214 m_bin_opt = 0;
2215
2216 realT s = strehl( d, m_bin_opt );
2217 realT bestStrehl = s;
2218
2219 while( d < m_D / 2 )
2220 {
2221 d += m_d_min[0] / m_optd_delta;
2222 m_bin_opt = d / m_d_min[0] - 1;
2223 if( m_bin_opt < 0 )
2224 m_bin_opt = 0;
2225
2226 s = strehl( d, m_bin_opt );
2227
2228 if( s < bestStrehl ) // Strehl got worse. Can't be <= otherwise small m_optd_deltas will immediately
2229 // out
2230 {
2231 d -= m_d_min[0] / m_optd_delta; // go back to previous d
2232 m_bin_opt = d / m_d_min[0] - 1;
2233 if( m_bin_opt < 0 )
2234 m_bin_opt = 0;
2235 break;
2236 }
2237 else
2238 bestStrehl = s;
2239 }
2240 best_d = d;
2241 }
2242 }
2243 else
2244 {
2245 realT d = m_d_min[0];
2246 m_bin_opt = 0;
2247
2248 realT s = strehl( d, m_bin_opt );
2249 realT bestStrehl = s;
2250
2251 // Find the actuator pitch which minimizes total error (AO error is less than fitting error at Nyquist)
2252 while( d < m_D / 2 )
2253 {
2254 d += m_d_min[0] / m_optd_delta;
2255
2256 s = strehl( d, m_bin_opt );
2257 if( s < bestStrehl ) // Strehl got worse. Can't be <= otherwise small m_optd_deltas will immediately out
2258 {
2259 d -= m_d_min[0] / m_optd_delta; // go back to previous d
2260 break;
2261 }
2262 else
2263 bestStrehl = s;
2264 }
2265 best_d = d;
2266 }
2267
2268 m_d_opt = best_d;
2269 m_dminChanged = false;
2270
2271 return m_d_opt;
2272}
2273
2274template <typename realT, class inputSpectT, typename iosT>
2276{
2277 if( m == 0 and n == 0 )
2278 return 0;
2279
2280 realT k = sqrt( m * m + n * n ) / m_D;
2281
2282 realT kmax = m_fit_mn_max / m_D;
2283 realT kmin = 1. / m_D;
2284
2285 realT beta;
2286 if( m_ncp_alpha != 2 )
2287 {
2288 beta = ( m_ncp_alpha - 2 ) / ( math::two_pi<realT>() ) * 1. /
2289 ( pow( kmin, -m_ncp_alpha + 2 ) - pow( kmax, -m_ncp_alpha + 2 ) );
2290 }
2291 else
2292 {
2293 beta = 1. / ( math::two_pi<realT>() * log( kmax / kmin ) );
2294 }
2295 return beta * ncpError() * pow( k, -m_ncp_alpha ) * pow( kmin, 2 );
2296}
2297
2298template <typename realT, class inputSpectT, typename iosT>
2300{
2301 return pow( m_ncp_wfe, 2 ) * pow( math::two_pi<realT>() / m_lam_sci, 2 );
2302}
2303
2304template <typename realT, class inputSpectT, typename iosT>
2306{
2307
2308 int mn_max = floor( 0.5 * m_D / d );
2309
2310 realT wfeVar = 0;
2311
2312 bool controlled;
2313 for( int m = -m_fit_mn_max; m <= m_fit_mn_max; ++m )
2314 {
2315 for( int n = -m_fit_mn_max; n <= m_fit_mn_max; ++n )
2316 {
2317 controlled = false;
2318 if( m_circularLimit )
2319 {
2320 if( m * m + n * n <= mn_max * mn_max )
2321 controlled = true;
2322 }
2323 else
2324 {
2325 if( abs( m ) <= mn_max && abs( n ) <= mn_max )
2326 controlled = true;
2327 }
2328
2329 if( controlled )
2330 {
2331 realT wfeMeasurement = measurementError( m, n, d, b );
2332 realT wfeTimeDelay = timeDelayError( m, n, d, b );
2333 realT wfeChromScintOPD = chromScintOPDError( m, n );
2334 realT wfeChromIndex = chromIndexError( m, n );
2335 realT wfeAnisoOPD = dispAnisoOPDError( m, n );
2336
2337 realT wfeFitting = fittingError( m, n );
2338
2339 if( wfeFitting < wfeMeasurement + wfeTimeDelay + wfeChromScintOPD + wfeChromIndex + wfeAnisoOPD )
2340 {
2341 wfeVar += wfeFitting;
2342 }
2343 else
2344 {
2345 wfeVar += wfeMeasurement + wfeTimeDelay + wfeChromScintOPD + wfeChromIndex + wfeAnisoOPD;
2346 }
2347 }
2348 else
2349 {
2350 wfeVar += fittingError( m, n );
2351 }
2352 }
2353 }
2354
2355 wfeVar += ncpError();
2356 return exp( -1 * wfeVar );
2357}
2358
2359template <typename realT, class inputSpectT, typename iosT>
2361{
2362 realT d = d_opt(); // have to run this to get m_bin_opt set.
2363 int b = m_bin_opt;
2364
2365 int mn_max = floor( 0.5 * m_D / d );
2366
2367 m_wfeMeasurement = 0;
2368 m_wfeTimeDelay = 0;
2370 m_wfeChromIndex = 0;
2371 m_wfeAnisoOPD = 0;
2372
2373 m_wfeFitting = 0;
2374
2375 m_wfeNCP = 0;
2376
2377 m_controlledModes.resize( 2 * m_fit_mn_max + 1, 2 * m_fit_mn_max + 1 );
2378 m_controlledModes.setZero();
2379
2380 bool controlled;
2381 for( int m = -m_fit_mn_max; m <= m_fit_mn_max; ++m )
2382 {
2383 for( int n = -m_fit_mn_max; n <= m_fit_mn_max; ++n )
2384 {
2385 if( m == 0 && n == 0 )
2386 {
2387 continue;
2388 }
2389
2390 controlled = false;
2391 if( m_circularLimit )
2392 {
2393 if( m * m + n * n <= mn_max * mn_max )
2394 controlled = true;
2395 }
2396 else
2397 {
2398 if( abs( m ) <= mn_max && abs( n ) <= mn_max )
2399 controlled = true;
2400 }
2401
2402 if( controlled )
2403 {
2404 realT wfeMeasurement = measurementError( m, n, d, b );
2405 realT wfeTimeDelay = timeDelayError( m, n, d, b );
2406 realT wfeChromScintOPD = chromScintOPDError( m, n );
2407 realT wfeChromIndex = chromIndexError( m, n );
2408 realT wfeAnisoOPD = dispAnisoOPDError( m, n );
2409
2410 realT wfeFitting = fittingError( m, n );
2411
2412 if( wfeFitting < wfeMeasurement + wfeTimeDelay + wfeChromScintOPD + wfeChromIndex + wfeAnisoOPD )
2413 {
2414 m_wfeFitting += wfeFitting;
2415 }
2416 else // it's worth controlling this mode.
2417 {
2418 m_wfeMeasurement += wfeMeasurement;
2419 m_wfeTimeDelay += wfeTimeDelay;
2420 m_wfeChromScintOPD += wfeChromScintOPD;
2421 m_wfeChromIndex += wfeChromIndex;
2422 m_wfeAnisoOPD += wfeAnisoOPD;
2424 }
2425 }
2426 else
2427 {
2428 m_wfeFitting += fittingError( m, n );
2429 }
2430 }
2431 }
2432
2433 m_wfeNCP = ncpError();
2434
2436 m_wfeNCP;
2437
2438 m_strehl = exp( -1 * m_wfeVar );
2439
2440 m_specsChanged = false;
2441}
2442
2443template <typename realT, class inputSpectT, typename iosT>
2445{
2447 calcStrehl();
2448
2449 return m_wfeVar;
2450}
2451
2452template <typename realT, class inputSpectT, typename iosT>
2454{
2456 calcStrehl();
2457
2458 return m_strehl;
2459}
2460
2461template <typename realT, class inputSpectT, typename iosT>
2462template <typename varFuncT>
2463realT aoSystem<realT, inputSpectT, iosT>::C_( int m, int n, bool normStrehl, varFuncT varFunc, int doFittingError )
2464{
2465 if( m == 0 && n == 0 )
2466 return 0;
2467
2468 // we run this to optimize regardless of whether we use it
2469 // if already optimized then this is a minor hit
2470 realT S = strehl();
2471
2472 if( !normStrehl )
2473 S = 1;
2474
2475 if( doFittingError != FITTING_ERROR_NO )
2476 {
2477 int mn_max = m_D / ( 2. * d_opt() );
2478
2479 if( m_controlledModes( m_fit_mn_max + m, m_fit_mn_max + n ) == false )
2480 {
2481 if( doFittingError == FITTING_ERROR_ZERO )
2482 return 0;
2483
2484 realT fe = fittingError( m, n );
2485
2486 realT k = sqrt( m * m + n * n ) / m_D;
2487
2488 if( doFittingError == FITTING_ERROR_X )
2489 fe *= ( atm.X( k, m_lam_sci, m_secZeta ) );
2490 else if( doFittingError == FITTING_ERROR_Y )
2491 fe *= ( atm.Y( k, m_lam_sci, m_secZeta ) );
2492 else
2493 {
2494 std::cerr << "Unknown doFittingError\n";
2495 exit( -1 );
2496 }
2497
2498 return fe / S;
2499 }
2500 }
2501 // get if not doing fitting error or if inside control region:
2502
2503 realT var = ( this->*varFunc )( m, n );
2504
2505 return var / S;
2506}
2507
2508template <typename realT, class inputSpectT, typename iosT>
2509template <typename imageT, typename CfuncT>
2510void aoSystem<realT, inputSpectT, iosT>::C_Map( imageT &im, CfuncT Cfunc, bool normStrehl )
2511{
2512 int dim1 = im.rows();
2513 int dim2 = im.cols();
2514
2515 int mc = 0.5 * ( dim1 - 1 );
2516 int nc = 0.5 * ( dim2 - 1 );
2517
2518 for( int i = 0; i < dim1; ++i )
2519 {
2520 int m = i - mc;
2521
2522 for( int j = 0; j < dim2; ++j )
2523 {
2524 int n = j - nc;
2525
2526 im( i, j ) = ( this->*Cfunc )( m, n, normStrehl );
2527 }
2528 }
2529}
2530
2531template <typename realT, class inputSpectT, typename iosT>
2533{
2534 realT k = sqrt( m * m + n * n ) / m_D;
2535 ///\todo handle multiple layers
2536 return psd( atm, 0, k, m_secZeta ) / pow( m_D, 2 ) * pow( atm.lam_0() / m_lam_sci, 2 ) *
2537 ( atm.X( k, m_lam_sci, m_secZeta ) );
2538}
2539
2540template <typename realT, class inputSpectT, typename iosT>
2541realT aoSystem<realT, inputSpectT, iosT>::C0( realT m, realT n, bool normStrehl )
2542{
2543
2544 return C_( m, n, normStrehl, &aoSystem<realT, inputSpectT, iosT>::C0var, FITTING_ERROR_NO );
2545}
2546
2547template <typename realT, class inputSpectT, typename iosT>
2548template <typename imageT>
2549void aoSystem<realT, inputSpectT, iosT>::C0Map( imageT &im, bool normStrehl )
2550{
2552}
2553
2554template <typename realT, class inputSpectT, typename iosT>
2556{
2557 realT k = sqrt( m * m + n * n ) / m_D;
2558
2559 ///\todo handle multiple layers
2560 return psd( atm, 0, k, m_secZeta ) / pow( m_D, 2 ) * pow( atm.lam_0() / m_lam_sci, 2 ) *
2561 ( atm.Y( k, m_lam_sci, m_secZeta ) );
2562}
2563
2564template <typename realT, class inputSpectT, typename iosT>
2565realT aoSystem<realT, inputSpectT, iosT>::C1( realT m, realT n, bool normStrehl )
2566{
2567 return C_( m, n, normStrehl, &aoSystem<realT, inputSpectT, iosT>::C1var, FITTING_ERROR_NO );
2568}
2569
2570template <typename realT, class inputSpectT, typename iosT>
2571template <typename imageT>
2572void aoSystem<realT, inputSpectT, iosT>::C1Map( imageT &im, bool normStrehl )
2573{
2575}
2576
2577template <typename realT, class inputSpectT, typename iosT>
2579{
2580 d_opt();
2581 return measurementError( m, n, d_opt(), m_bin_opt ) + timeDelayError( m, n, d_opt(), m_bin_opt );
2582}
2583
2584template <typename realT, class inputSpectT, typename iosT>
2585realT aoSystem<realT, inputSpectT, iosT>::C2( realT m, realT n, bool normStrehl )
2586{
2587 return C_( m, n, normStrehl, &aoSystem<realT, inputSpectT, iosT>::C2var, FITTING_ERROR_X );
2588}
2589
2590template <typename realT, class inputSpectT, typename iosT>
2591template <typename imageT>
2592void aoSystem<realT, inputSpectT, iosT>::C2Map( imageT &im, bool normStrehl )
2593{
2595}
2596
2597template <typename realT, class inputSpectT, typename iosT>
2599{
2600 return 0; // measurementError(m, n) + timeDelayError(m,n);
2601}
2602
2603template <typename realT, class inputSpectT, typename iosT>
2604realT aoSystem<realT, inputSpectT, iosT>::C3( realT m, realT n, bool normStrehl )
2605{
2606 return C_( m, n, normStrehl, &aoSystem<realT, inputSpectT, iosT>::C3var, FITTING_ERROR_ZERO ); // FITTING_ERROR_Y);
2607}
2608
2609template <typename realT, class inputSpectT, typename iosT>
2610template <typename imageT>
2611void aoSystem<realT, inputSpectT, iosT>::C3Map( imageT &im, bool normStrehl )
2612{
2614}
2615
2616template <typename realT, class inputSpectT, typename iosT>
2618{
2619 realT k = sqrt( m * m + n * n ) / m_D;
2620
2621 ///\todo handle multiple layers
2622 // This does not need to be divided by X b/c we haven't multiplied by it, this is is C0/X.
2623 return psd( atm, 0, k, m_secZeta ) / pow( m_D, 2 ) * pow( atm.lam_0() / m_lam_sci, 2 ) *
2624 atm.dX( k, m_lam_sci, m_lam_wfs );
2625}
2626
2627template <typename realT, class inputSpectT, typename iosT>
2628realT aoSystem<realT, inputSpectT, iosT>::C4( realT m, realT n, bool normStrehl )
2629{
2630 return C_( m, n, normStrehl, &aoSystem<realT, inputSpectT, iosT>::C4var, FITTING_ERROR_ZERO );
2631}
2632
2633template <typename realT, class inputSpectT, typename iosT>
2634template <typename imageT>
2635void aoSystem<realT, inputSpectT, iosT>::C4Map( imageT &im, bool normStrehl )
2636{
2638}
2639
2640template <typename realT, class inputSpectT, typename iosT>
2642{
2643 realT k = sqrt( m * m + n * n ) / m_D;
2644
2645 ///\todo handle multiple layers
2646 // This does not need to be divided by Y b/c we haven't multiplied by it, this is is C1/Y.
2647 return psd( atm, 0, k, m_secZeta ) / pow( m_D, 2 ) * pow( atm.lam_0() / m_lam_sci, 2 ) *
2648 atm.dY( k, m_lam_sci, m_lam_wfs );
2649}
2650
2651template <typename realT, class inputSpectT, typename iosT>
2652realT aoSystem<realT, inputSpectT, iosT>::C5( realT m, realT n, bool normStrehl )
2653{
2654 return C_( m, n, normStrehl, &aoSystem<realT, inputSpectT, iosT>::C5var, FITTING_ERROR_ZERO );
2655}
2656
2657template <typename realT, class inputSpectT, typename iosT>
2658template <typename imageT>
2659void aoSystem<realT, inputSpectT, iosT>::C5Map( imageT &im, bool normStrehl )
2660{
2662}
2663
2664template <typename realT, class inputSpectT, typename iosT>
2666{
2667 realT ni = atm.n_air( m_lam_sci );
2668 realT nw = atm.n_air( m_lam_wfs );
2669
2670 return C0var( m, n ) * pow( ( ni - nw ) / ( ni - 1 ), 2 ); // Fixes error in Eqn 29
2671}
2672
2673template <typename realT, class inputSpectT, typename iosT>
2674realT aoSystem<realT, inputSpectT, iosT>::C6( realT m, realT n, bool normStrehl )
2675{
2676 return C_( m, n, normStrehl, &aoSystem<realT, inputSpectT, iosT>::C6var, FITTING_ERROR_ZERO );
2677}
2678
2679template <typename realT, class inputSpectT, typename iosT>
2680template <typename imageT>
2681void aoSystem<realT, inputSpectT, iosT>::C6Map( imageT &im, bool normStrehl )
2682{
2684}
2685
2686template <typename realT, class inputSpectT, typename iosT>
2688{
2689 realT k = sqrt( m * m + n * n ) / m_D;
2690
2691 ///\todo this needs to handle multiple layers -- do we violate an assumption of L_0 isn't constant?
2692 return psd( atm, 0, k, m_secZeta ) / pow( m_D, 2 ) * pow( atm.lam_0() / m_lam_sci, 2 ) *
2693 atm.X_Z( k, m_lam_wfs, m_lam_sci, m_secZeta );
2694}
2695
2696template <typename realT, class inputSpectT, typename iosT>
2697realT aoSystem<realT, inputSpectT, iosT>::C7( realT m, realT n, bool normStrehl )
2698{
2699 return C_( m, n, normStrehl, &aoSystem<realT, inputSpectT, iosT>::C7var, FITTING_ERROR_ZERO );
2700}
2701
2702template <typename realT, class inputSpectT, typename iosT>
2703template <typename imageT>
2704void aoSystem<realT, inputSpectT, iosT>::C7Map( imageT &im, bool normStrehl )
2705{
2707}
2708
2709template <typename realT, class inputSpectT, typename iosT>
2711{
2712 ios << "# AO Params:\n";
2713 ios << "# D = " << D() << '\n';
2714
2715 if( m_d_min.size() > 0 )
2716 {
2717 ios << "# d_min = " << d_min( (size_t)0 );
2718 for( size_t n = 1; n < m_d_min.size(); ++n )
2719 ios << ',' << d_min( n );
2720 ios << '\n';
2721 }
2722 else
2723 ios << "# d_min = null\n";
2724
2725 ios << "# optd = " << std::boolalpha << m_optd << '\n';
2726 ios << "# d_opt_delta = " << optd_delta() << '\n';
2727 ios << "# lam_sci = " << lam_sci() << '\n';
2728 ios << "# F0 = " << F0() << '\n';
2729 ios << "# starMag = " << starMag() << '\n';
2730 ios << "# lam_sci = " << lam_sci() << '\n';
2731 ios << "# zeta = " << zeta() << '\n';
2732 ios << "# lam_wfs = " << lam_wfs() << '\n';
2733
2734 if( npix_wfs().size() > 0 )
2735 {
2736 ios << "# npix_wfs = " << npix_wfs( (size_t)0 );
2737 for( size_t n = 1; n < npix_wfs().size(); ++n )
2738 ios << ',' << npix_wfs( n );
2739 ios << '\n';
2740 }
2741 else
2742 ios << "# npix_wfs = null\n";
2743
2744 if( ron_wfs().size() > 0 )
2745 {
2746 ios << "# ron_wfs = " << ron_wfs( (size_t)0 );
2747 for( size_t n = 1; n < ron_wfs().size(); ++n )
2748 ios << ',' << ron_wfs( n );
2749 ios << '\n';
2750 }
2751 else
2752 ios << "# ron_wfs = null\n";
2753
2754 if( Fbg().size() > 0 )
2755 {
2756 ios << "# Fbg = " << Fbg( (size_t)0 );
2757 for( size_t n = 1; n < Fbg().size(); ++n )
2758 ios << ',' << Fbg( n );
2759 ios << '\n';
2760 }
2761 else
2762 ios << "# Fbg = null\n";
2763
2764 if( minTauWFS().size() > 0 )
2765 {
2766 ios << "# minTauWFS = " << minTauWFS( (size_t)0 );
2767 for( size_t n = 1; n < minTauWFS().size(); ++n )
2768 ios << ',' << minTauWFS( n );
2769 ios << '\n';
2770 }
2771 else
2772 ios << "# minTauWFS = null\n";
2773
2774 ios << "# bin_npix = " << std::boolalpha << m_bin_npix << '\n';
2775 ios << "# tauWFS = " << tauWFS() << '\n';
2776 ios << "# optTau = " << std::boolalpha << m_optTau << '\n';
2777 ios << "# deltaTau = " << deltaTau() << '\n';
2778 ios << "# fit_mn_max = " << m_fit_mn_max << '\n';
2779 ios << "# spatialFilter_ku = " << m_spatialFilter_ku << '\n';
2780 ios << "# spatialFilter_kv = " << m_spatialFilter_kv << '\n';
2781 ios << "# ncp_wfe = " << m_ncp_wfe << '\n';
2782 ios << "# ncp_alpha = " << m_ncp_alpha << '\n';
2783
2784 if( m_wfsBeta == 0 )
2785 {
2786 throw( mx::exception<verboseT>( error_t::paramnotset, "The WFS is not assigned." ) );
2787 }
2788
2789 m_wfsBeta->dumpWFS( ios );
2790 psd.dumpPSD( ios );
2791 atm.dumpAtmosphere( ios );
2792
2793 dumpGitStatus( ios );
2794
2795 return ios;
2796}
2797
2798template <typename realT, class inputSpectT, typename iosT>
2800{
2801 using namespace mx::app;
2802
2803 // AO System configuration
2804 config.add( "aosys.wfs",
2805 "",
2806 "aosys.wfs",
2807 argType::Required,
2808 "aosys",
2809 "wfs",
2810 false,
2811 "string",
2812 "The WFS type: idealWFS, unmodPyWFS, asympModPyWFS, shwfs, calculatedWFS" );
2813 config.add( "aosys.wfs_beta_p",
2814 "",
2815 "aosys.wfs_beta_p",
2816 argType::Required,
2817 "aosys",
2818 "wfs_beta_p",
2819 false,
2820 "string",
2821 "The beta_p file path for calcualtedWFS" );
2822 config.add( "aosys.wfs_beta_r",
2823 "",
2824 "aosys.wfs_beta_r",
2825 argType::Required,
2826 "aosys",
2827 "wfs_beta_r",
2828 false,
2829 "string",
2830 "The beta_r file path for calcualtedWFS" );
2831 config.add( "aosys.wfs_sensitivity",
2832 "",
2833 "aosys.wfs_sensitivity",
2834 argType::Required,
2835 "aosys",
2836 "wfs_sensitivity",
2837 false,
2838 "bool",
2839 "Flag indicating that beta_p/beta_r are sensitivities (inverse) [default false]" );
2840 config
2841 .add( "aosys.D", "", "aosys.D", argType::Required, "aosys", "D", false, "real", "The telescope diameter [m]" );
2842 config.add( "aosys.d_min",
2843 "",
2844 "aosys.d_min",
2845 argType::Required,
2846 "aosys",
2847 "d_min",
2848 false,
2849 "real",
2850 "The minimum actuator spacing [m]" );
2851 config.add( "aosys.optd",
2852 "",
2853 "aosys.optd",
2854 argType::Optional,
2855 "aosys",
2856 "optd",
2857 false,
2858 "bool",
2859 "Whether or not the actuator spacing is optimized" );
2860 config.add( "aosys.optd_delta",
2861 "",
2862 "aosys.optd_delta",
2863 argType::Required,
2864 "aosys",
2865 "optd_delta",
2866 false,
2867 "bool",
2868 "The fractional change from d_min used in optimization. Set to 1 (default) for integer binnings, > 1 "
2869 "for finer sampling." );
2870 config.add( "aosys.F0",
2871 "",
2872 "aosys.F0",
2873 argType::Required,
2874 "aosys",
2875 "F0",
2876 false,
2877 "real",
2878 "Zero-mag photon flux, [photons/sec]" );
2879 config.add( "aosys.lam_wfs",
2880 "",
2881 "aosys.lam_wfs",
2882 argType::Required,
2883 "aosys",
2884 "lam_wfs",
2885 false,
2886 "real",
2887 "WFS wavelength [m]" );
2888 config.add( "aosys.npix_wfs",
2889 "",
2890 "aosys.npix_wfs",
2891 argType::Required,
2892 "aosys",
2893 "npix_wfs",
2894 false,
2895 "vector<real>",
2896 "The number of pixels in the WFS" );
2897 config.add( "aosys.ron_wfs",
2898 "",
2899 "aosys.ron_wfs",
2900 argType::Required,
2901 "aosys",
2902 "ron_wfs",
2903 false,
2904 "vector<real>",
2905 "WFS readout noise [photons/read]" );
2906 config.add( "aosys.bin_npix",
2907 "",
2908 "aosys.bin_npix",
2909 argType::Required,
2910 "aosys",
2911 "bin_npix",
2912 false,
2913 "bool",
2914 "Whether or not WFS pixels are re-binned along with actuator spacing optimization" );
2915 config.add( "aosys.Fbg",
2916 "",
2917 "aosys.Fbg",
2918 argType::Required,
2919 "aosys",
2920 "Fbg",
2921 false,
2922 "vector<real>",
2923 "Background counts, [counts/pix/sec]" );
2924 config.add( "aosys.tauWFS",
2925 "",
2926 "aosys.tauWFS",
2927 argType::Required,
2928 "aosys",
2929 "tauWFS",
2930 false,
2931 "real",
2932 "WFS integration time [s]" );
2933 config.add( "aosys.minTauWFS",
2934 "",
2935 "aosys.minTauWFS",
2936 argType::Required,
2937 "aosys",
2938 "minTauWFS",
2939 false,
2940 "vector<real>",
2941 "Minimum WFS integration time [s]" );
2942 config.add( "aosys.deltaTau",
2943 "",
2944 "aosys.deltaTau",
2945 argType::Required,
2946 "aosys",
2947 "deltaTau",
2948 false,
2949 "real",
2950 "Loop delay [s]" );
2951 config.add( "aosys.optTau",
2952 "",
2953 "aosys.optTau",
2954 argType::Optional,
2955 "aosys",
2956 "optTau",
2957 false,
2958 "bool",
2959 "Whether or not the integration time is optimized" );
2960 config.add( "aosys.lam_sci",
2961 "",
2962 "aosys.lam_sci",
2963 argType::Required,
2964 "aosys",
2965 "lam_sci",
2966 false,
2967 "real",
2968 "Science wavelength [m]" );
2969 config.add( "aosys.zeta",
2970 "",
2971 "aosys.zeta",
2972 argType::Required,
2973 "aosys",
2974 "zeta",
2975 false,
2976 "real",
2977 "Zenith distance [rad]" );
2978 config.add( "aosys.fit_mn_max",
2979 "",
2980 "aosys.fit_mn_max",
2981 argType::Required,
2982 "aosys",
2983 "fit_mn_max",
2984 false,
2985 "real",
2986 "Maximum spatial frequency index to use for analysis" );
2987 config.add( "aosys.circularLimit",
2988 "",
2989 "aosys.circularLimit",
2990 argType::Optional,
2991 "aosys",
2992 "circularLimit",
2993 false,
2994 "bool",
2995 " Flag to indicate that the spatial frequency limit is circular, not square." );
2996 config.add( "aosys.spatialFilter_ku",
2997 "",
2998 "aosys.spatialFilter_ku",
2999 argType::Required,
3000 "aosys",
3001 "spatialFilter_ku",
3002 false,
3003 "real",
3004 "Spatial filter cutoff frequency in u [m^-1]" );
3005 config.add( "aosys.spatialFilter_kv",
3006 "",
3007 "aosys.spatialFilter_kv",
3008 argType::Required,
3009 "aosys",
3010 "spatialFilter_kv",
3011 false,
3012 "real",
3013 "Spatial filter cutoff frequency in v [m^-1]" );
3014 config.add( "aosys.ncp_wfe",
3015 "",
3016 "aosys.ncp_wfe",
3017 argType::Required,
3018 "aosys",
3019 "ncp_wfe",
3020 false,
3021 "real",
3022 "NCP WFE between 1 lambda/D and fit_mn_max [rad^2]" );
3023 config.add( "aosys.ncp_alpha",
3024 "",
3025 "aosys.ncp_alpha",
3026 argType::Required,
3027 "aosys",
3028 "ncp_alpha",
3029 false,
3030 "real",
3031 "PSD index for NCP WFE" );
3032 config.add( "aosys.starMag",
3033 "",
3034 "aosys.starMag",
3035 argType::Required,
3036 "aosys",
3037 "starMag",
3038 false,
3039 "real",
3040 "Star magnitude" );
3041 config.add( "aosys.starMags",
3042 "",
3043 "aosys.starMags",
3044 argType::Required,
3045 "aosys",
3046 "starMags",
3047 false,
3048 "real vector",
3049 "A vector of star magnitudes" );
3050
3051 atm.setupConfig( config );
3052 psd.setupConfig( config );
3053}
3054
3055template <typename realT, class inputSpectT, typename iosT>
3057{
3058 // WFS
3059 if( config.isSet( "aosys.wfs" ) )
3060 {
3061 std::string wfsStr;
3062 config( wfsStr, "aosys.wfs" );
3063
3064 if( wfsStr == "ideal" )
3065 {
3066 wfsBeta<wfs<realT, iosT>>( nullptr );
3067 }
3068 else if( wfsStr == "unmodPyWFS" )
3069 {
3071 }
3072 else if( wfsStr == "asympModPyWFS" )
3073 {
3075 }
3076 else if( wfsStr == "SHWFS" )
3077 {
3078 wfsBeta<shwfs<realT, iosT>>( nullptr );
3079 }
3080 else if( wfsStr == "calculatedWFS" )
3081 {
3083
3085 config( cwfs->m_beta_p_file, "aosys.wfs_beta_p" );
3086 config( cwfs->m_beta_r_file, "aosys.wfs_beta_r" );
3087 bool sens = cwfs->m_sensitivity;
3088 config( sens, "aosys.wfs_sensitivity" );
3089 if( config.isSet( "aosys.wfs_sensitivity" ) )
3090 {
3091 cwfs->m_sensitivity = sens;
3092 }
3093 }
3094 else
3095 {
3096 throw( mx::exception<verboseT>( error_t::invalidarg, "unknown WFS " + wfsStr + " specified" ) );
3097 }
3098 }
3099
3100 // We load the default value, get the config value (which will be the default if not set), and
3101 // then if it was set, call the setter function so any side effects are captured.
3102
3103 // diameter
3104 realT nD = D();
3105 config( nD, "aosys.D" );
3106 if( config.isSet( "aosys.D" ) )
3107 D( nD );
3108
3109 // d_min
3110 std::vector<realT> nd_min = d_min();
3111 config( nd_min, "aosys.d_min" );
3112 if( config.isSet( "aosys.d_min" ) )
3113 d_min( nd_min );
3114
3115 bool noptd = optd();
3116 config( noptd, "aosys.optd" );
3117 if( config.isSet( "aosys.optd" ) )
3118 optd( noptd );
3119
3120 realT noptd_delta = optd_delta();
3121 config( noptd_delta, "aosys.optd_delta" );
3122 if( config.isSet( "aosys.optd_delta" ) )
3123 optd_delta( noptd_delta );
3124
3125 realT nlam_wfs = lam_wfs();
3126 config( nlam_wfs, "aosys.lam_wfs" );
3127 if( config.isSet( "aosys.lam_wfs" ) )
3128 lam_wfs( nlam_wfs );
3129
3130 // npix_wfs
3131 std::vector<realT> nnpix_wfs = npix_wfs();
3132 config( nnpix_wfs, "aosys.npix_wfs" );
3133 if( config.isSet( "aosys.npix_wfs" ) )
3134 npix_wfs( nnpix_wfs );
3135
3136 // ron_wfs
3137 std::vector<realT> nron_wfs = ron_wfs();
3138 config( nron_wfs, "aosys.ron_wfs" );
3139 if( config.isSet( "aosys.ron_wfs" ) )
3140 ron_wfs( nron_wfs );
3141
3142 // Fbg
3143 std::vector<realT> nFbg = Fbg();
3144 config( nFbg, "aosys.Fbg" );
3145 if( config.isSet( "aosys.Fbg" ) )
3146 Fbg( nFbg );
3147
3148 // minTauWFS
3149 std::vector<realT> nminTauWFS = minTauWFS();
3150 config( nminTauWFS, "aosys.minTauWFS" );
3151 if( config.isSet( "aosys.minTauWFS" ) )
3152 minTauWFS( nminTauWFS );
3153
3154 // bin_npix
3155 bool nbin_npix = bin_npix();
3156 config( nbin_npix, "aosys.bin_npix" );
3157 if( config.isSet( "aosys.bin_npix" ) )
3158 bin_npix( nbin_npix );
3159
3160 // tauWFS
3161 realT ntauWFS = tauWFS();
3162 config( ntauWFS, "aosys.tauWFS" );
3163 if( config.isSet( "aosys.tauWFS" ) )
3164 tauWFS( ntauWFS );
3165
3166 // deltaTau
3167 realT ndeltaTau = deltaTau();
3168 config( ndeltaTau, "aosys.deltaTau" );
3169 if( config.isSet( "aosys.deltaTau" ) )
3170 deltaTau( ndeltaTau );
3171
3172 // optTau
3173 bool noptTau = optTau();
3174 config( noptTau, "aosys.optTau" );
3175 if( config.isSet( "aosys.optTau" ) )
3176 optTau( noptTau );
3177
3178 // lam_sci
3179 realT nlam_sci = lam_sci();
3180 config( nlam_sci, "aosys.lam_sci" );
3181 if( config.isSet( "aosys.lam_sci" ) )
3182 lam_sci( nlam_sci );
3183
3184 // zeta
3185 realT nzeta = zeta();
3186 config( nzeta, "aosys.zeta" );
3187 if( config.isSet( "aosys.zeta" ) )
3188 zeta( nzeta );
3189
3190 // fit_mn_max
3191 realT fmnm = fit_mn_max();
3192 config( fmnm, "aosys.fit_mn_max" );
3193 if( config.isSet( "aosys.fit_mn_max" ) )
3194 fit_mn_max( fmnm );
3195
3196 // circularLimit
3197 bool cl = circularLimit();
3198 config( cl, "aosys.circularLimit" );
3199 if( config.isSet( "aosys.circularLimit" ) )
3200 circularLimit( cl );
3201
3202 // spatialFilter_ku
3203 realT ku = spatialFilter_ku();
3204 config( ku, "aosys.spatialFilter_ku" );
3205 if( config.isSet( "aosys.spatialFilter_ku" ) )
3206 spatialFilter_ku( ku );
3207
3208 // spatialFilter_kv
3209 realT kv = spatialFilter_kv();
3210 config( kv, "aosys.spatialFilter_kv" );
3211 if( config.isSet( "aosys.spatialFilter_kv" ) )
3212 spatialFilter_kv( kv );
3213
3214 // ncp_wfe
3215 realT nwfe = ncp_wfe();
3216 config( nwfe, "aosys.ncp_wfe" );
3217 if( config.isSet( "aosys.ncp_wfe" ) )
3218 ncp_wfe( nwfe );
3219
3220 // ncp_alpha
3221 realT na = ncp_alpha();
3222 config( na, "aosys.ncp_alpha" );
3223 if( config.isSet( "aosys.ncp_alpha" ) )
3224 ncp_alpha( na );
3225
3226 // F0
3227 realT nF0 = F0();
3228 config( nF0, "aosys.F0" );
3229 if( config.isSet( "aosys.F0" ) )
3230 F0( nF0 );
3231
3232 // star_mag
3233 realT smag = starMag();
3234 config( smag, "aosys.starMag" );
3235 if( config.isSet( "aosys.starMag" ) )
3236 starMag( smag );
3237
3238 const error_t atmosphereStatus = atm.loadConfig( config );
3239 psd.loadConfig( config );
3240
3241 return atmosphereStatus;
3242}
3243
3244extern template class aoSystem<float, vonKarmanSpectrum<float>, std::ostream>;
3245
3246extern template class aoSystem<double, vonKarmanSpectrum<double>, std::ostream>;
3247
3248extern template class aoSystem<long double, vonKarmanSpectrum<long double>, std::ostream>;
3249
3250#ifdef HASQUAD
3251extern template class aoSystem<__float128, vonKarmanSpectrum<__float128>, std::ostream>;
3252#endif
3253
3254} // namespace analysis
3255} // namespace AO
3256} // namespace mx
3257
3258#endif // aoSystem_hpp
Provides a class to specify atmosphere parameters.
Calculate and provide constants related to adaptive optics.
Spatial power spectra used in adaptive optics.
Definitions of various analytic wavefront sensors.
A class to specify atmosphere parameters and perform related calculations.
Describes an analytic adaptive optics (AO) system.
Definition aoSystem.hpp:64
bool circularLimit()
Get the value of the circularLimit flag.
void optTau(bool ot)
Set the value of m_optTau.
realT Fbg(size_t idx)
Get the value of the background flux for a given WFS mode.
void optd_delta(realT odd)
Set the fractional change in actuator spacing for optimization.
void loadMagAOX()
Load parameters corresponding to the MagAO-X system.
void starMag(realT nmag)
Set the value of the Star's magnitude.
void wfsBeta(const wfsT &w)
Set the WFS Beta pointer.
bool bin_npix()
Get the value of the pixel binngin flag.
void wfsBeta(const wfsT *w)
Set the WFS Beta pointer.
void loadGuyon2005()
Load the default parameters from Guyon, 2005 guyon_2005.
bool optd()
Get the value of m_optd.
void circularLimit(bool cl)
Set the value of the circularLimit flag.
void C1Map(imageT &map, bool normStrehl)
Calculate a 2D map of contrast C1.
realT F0()
Get the value of the 0 magnitude photon rate.
realT C5(realT m, realT n, bool normStrehl=true)
Calculate the contrast due to scintilation-amplitude chromaticity.
realT chromScintOPDErrorTotal()
Calculate the wavefront error due to scintillation chromaticity in the OPD over all spatial frequenci...
void C5Map(imageT &map, bool normStrehl)
Calculate a 2D map of contrast C5.
error_t loadConfig(app::appConfigurator &config)
Load the configuration of this class from a configurator.
realT C1(realT m, realT n, bool normStrehl=true)
Calculate the contrast due to uncorrected amplitude, C1.
realT C0var(realT m, realT n)
Calculate the residual variance due to uncorrected phase at a spatial frequency.
realT C_(int m, int n, bool normStrehl, varFuncT varFunc, int doFittingError)
Worker function for raw contrast fuctions.
void ncp_alpha(realT alpha)
Set the value of the non-common path WFE PSD index.
realT d_opt()
Calculate the optimum actuator spacing.
std::vector< realT > d_min()
Get the minimum subaperture sampling for all WFS modes.
realT ncpError()
Calculate the total NCP variance in rad^2.
realT spatialFilter_kv()
Get the value of spatialFilter_kv.
realT zeta()
Get the zenith angle.
void tauWFS(realT ntau)
Set the value of the WFS exposure time.
realT Fg()
Get the photon rate at the current Star magnitude.
void deltaTau(realT ndel)
Set the value of m_deltaTau.
std::vector< realT > minTauWFS()
Get the values of the minimum WFS exposure time.
void calcStrehl()
Calculate the component WFE, total WFE, and Strehl ratio.
realT lam_sci()
Get the science wavelength.
realT deltaTau()
Get the value of m_deltaTau.
realT npix_wfs(size_t idx)
Get the number of pixels in the WFS for a given WFS mode.
realT wfeVar()
Get the current value of the total WFE variance.
realT ncp_wfe()
Get the value of the non-common path WFE.
realT tauWFS()
Get the value of the minimum WFS exposure time.
void npix_wfs(int idx, realT npix)
Set the number of pixels in the WFS for a given WFS mode.
realT C4(realT m, realT n, bool normStrehl=true)
Calculate the contrast due to scintilation-OPD chromaticity.
realT timeDelayError(realT m, realT n, realT d, int b)
Calculate the time delay at a spatial frequency at the optimum exposure time and the specified actuat...
realT chromIndexErrorTotal()
Calculate the wavefront error due to chromaticity in the index of refraction at a specific spatial fr...
realT strehl(realT d, int b)
Get the Strehl ratio for a given actuator pitch and WFS WFS mode.
realT chromIndexError(int m, int n)
Calculate the wavefront error due to chromaticity in the index of refraction at a given spatial frequ...
void minTauWFS(const std::vector< realT > &ntau)
Set the value of the minimum WFS exposure times.
realT optimumTauWFS(realT m, realT n)
Calculate the optimum exposure time for a given spatial frequency at the optimum actuator spacing.
void D(realT nD)
Set the value of the primary mirror diameter.
void spatialFilter_ku(realT ku)
Set the value of spatialFilter_ku.
realT ncpError(int m, int n)
Calculate the NCP variance at a spatial frequency.
realT C4var(realT m, realT n)
Calculate the residual variance due to scintilation-OPD chromaticity.
realT minTauWFS(size_t idx)
Get the value of the minimum WFS exposure time for a given binning.
realT beta_r(realT m, realT n)
Get the value of beta_r for a spatial frequency.
realT spatialFilter_ku()
Get the value of spatialFilter_ku.
void minTauWFS(size_t idx, realT ntau)
Set a single value of the minimum WFS exposure time for a given WFS mode.
realT ron_wfs(size_t idx)
Get the value of the WFS readout noise for a given WFS mode.
void C7Map(imageT &map, bool normStrehl)
Calculate a 2D map of contrast C7.
realT C7var(realT m, realT n)
Calculate the residual variance due to dispersive anisoplanatism.
void npix_wfs(const std::vector< realT > &npix)
Set the number of pixels in the WFS for each WFS mode.
realT fittingErrorTotal()
Calculate the total fitting error over all uncorrected spatial frequencies.
void C0Map(imageT &map, bool normStrehl)
Calculate a 2D map of contrast C0.
void optd(bool od)
Set whether or not the value of d is optimized or just set to m_d_min.
bool optTau()
Get the value of m_optTau.
void opticalGain(realT og)
Set the value of the Optical Gain.
realT beta_p(realT m, realT n)
Get the value of beta_p for a spatial frequency.
realT C5var(realT m, realT n)
Calculate the residual variance due to to scintilation-amplitude chromaticity.
realT secZeta()
Get the zecant of the zenith angle.
void lam_wfs(realT nlam)
Set the value of the WFS wavelength.
realT measurementError(realT m, realT n, realT d, int b)
Calculate the measurement noise at a spatial frequency and specified actuator spacing.
void Fbg(int idx, realT fbg)
Set a single value of the background flux for a given WFS mode.
realT dispAnisoOPDErrorTotal()
Calculate the wavefront error due to dispersive anisoplanatism in the OPD over all specific spatial f...
void bin_npix(bool bnp)
Set the value of the pixel binning flag.
void ron_wfs(const std::vector< realT > &nron)
Set the value of the WFS readout noise for each WFS mode.
realT chromScintAmpError(int m, int n)
Calculate the wavefront error due to scintillation chromaticity in amplitude for a given spatial freq...
void C3Map(imageT &map, bool normStrehl)
Calculate a 2D map of contrast C3.
realT C2var(realT m, realT n)
Calculate the residual variance due to measurement and time delay errors in phase/OPD at a spatial fr...
realT Fg(realT mag)
The photon flux at a given star magnitude.
std::vector< realT > Fbg()
Get the value of the background flux for all WFS modes.
realT timeDelayErrorTotal()
Calculate the time delay error over all corrected spatial frequencies.
realT dispAnisoOPDError(int m, int n)
Calculate the wavefront error due to dispersive anisoplanatism in the OPD at a given spatial frequenc...
realT signal2Noise2(realT &Nph, realT tau_wfs, realT d, int b)
Calculate the terms of the signal to noise ratio squared (S/N)^2 for the WFS measurement.
realT C3var(realT m, realT n)
Calculate the residual variance due to measurement and time delay errors in amplitude at a spatial fr...
realT starMag()
Get the value of the Star's magnitude.
realT d_min(size_t idx)
Get the minimum subaperture sampling for a given WFS mode.
void loadGMagAOX()
Load parameters corresponding to the G-MagAO-X system.
void F0(realT nF0)
Set the value of the 0 magnitude photon rate.
realT chromScintAmpError()
Calculate the wavefront error due to scintillation chromaticity in amplitude over all spatial frequen...
std::vector< realT > ron_wfs()
Get the value of the WFS readout noise for all WFS modes.
std::vector< realT > npix_wfs()
Get the number of pixels in the WFS for all WFS modes.
void d_min(const std::vector< realT > &nd)
Set the minimum subaperture sampling for each WFS mode.
realT C7(realT m, realT n, bool normStrehl=true)
Calculate the contrast due to dispersive anisoplanatism.
realT measurementErrorTotal()
Calculate the total measurement error over all corrected spatial frequencies.
void setupConfig(app::appConfigurator &config)
Setup the configurator to configure this class.
realT strehl()
Get the current Strehl ratio.
void ron_wfs(int idx, realT nron)
Set the value of the WFS readout noise for a given bining mode.
void lam_sci(realT nlam)
Set the science wavelength.
void zeta(realT nz)
Set the zenith angle, and its secant.
realT C0(realT m, realT n, bool normStrehl=true)
Calculate the contrast due to uncorrected phase, C0.
realT C1var(realT m, realT n)
Calculate the residual variance due to uncorrected amplitude at a spatial frequency.
void fit_mn_max(int mnm)
Set the value of m_fit_mn_max.
realT ncp_alpha()
Get the value of the non-common path WFE PSD index.
const Eigen::Array< int, -1, -1 > & controlledModes()
Access the array of controlledModes.
realT chromScintOPDError(int m, int n)
Calculate the wavefront error due to scintillation chromaticity in the OPD at a spatial frequency.
void C_Map(imageT &im, CfuncT Cfunc, bool normStrehl)
Worker function for the contrast-map functions.
void ncp_wfe(realT nwfe)
Set the value of the non-common path WFE.
iosT & dumpAOSystem(iosT &ios)
Output current parameters to a stream.
void C4Map(imageT &map, bool normStrehl)
Calculate a 2D map of contrast C4.
realT C6var(realT m, realT n)
Calculate the residual variance due to chromaticity of the index of refraction of air.
realT C2(realT m, realT n, bool normStrehl=true)
Calculate the contrast due to measurement and time delay errors in phase/OPD at a spatial frequency.
void C2Map(imageT &map, bool normStrehl)
Calculate a 2D map of contrast C2().
void Fbg(const std::vector< realT > &fbg)
Set the value of the background fluxes.
realT dispAnisoAmpError()
Calculate the wavefront error due to dispersive anisoplanatism in the amplitude at a specific spatial...
void spatialFilter_kv(realT kv)
Set the value of spatialFilter_kv.
void d_min(int idx, realT nd)
Set the minimum subaperture sampling for a given WFS mode.
realT C6(realT m, realT n, bool normStrehl=true)
Calculate the contrast due to chromaticity of the index of refraction of air.
int fit_mn_max()
Get the value of m_fit_mn_max.
realT opticalGain()
Get the value of the optical gain.
int bin_opt()
Get the value of the optimum binning factor/index.
realT optimumTauWFS(realT m, realT n, realT d, int b)
Calculate the optimum exposure time for a given spatial frequency at a specified actuator spacing.
void C6Map(imageT &map, bool normStrehl)
Calculate a 2D map of contrast C6.
realT lam_wfs()
Get the value of the WFS wavelength.
realT D()
Get the value of the primary mirror diamter.
realT C3(realT m, realT n, bool normStrehl=true)
Calculate the contrast due to measurement and time delay errors in amplitude at a spatial frequency.
realT fittingError(realT m, realT n)
Calculate the fitting error at a specific spatial frequency.
wfs< realT, iosT > * wfsBeta()
Get the WFS Beta pointer.
realT optd_delta()
Get the value of the fractional change in actuator spacing for optimization..
Augments an exception with the source file and line.
Definition exception.hpp:42
error_t
The mxlib error codes.
Definition error_t.hpp:26
@ sizeerr
A size was invalid or calculated incorrectly.
Definition error_t.hpp:35
@ paramnotset
A parameter was not set.
Definition error_t.hpp:32
@ invalidarg
An argument was invalid.
Definition error_t.hpp:29
error_t mxlib_error_report(const error_t &code, const std::string &expl, const std::source_location &loc=std::source_location::current())
Print a report to stderr given an mxlib error_t code and explanation and return the code.
Definition error.hpp:331
void quarticRoots(std::vector< std::complex< realT > > &x, realT a, realT b, realT c, realT d, realT e)
Find the roots of the general quartic equation.
Definition roots.hpp:126
constexpr T pi()
Get the value of pi.
Definition constants.hpp:62
constexpr T two_pi()
Get the value of 2pi.
Declarations of some libarary wide utilities.
MXLIB_DEFAULT_VERBOSITY d
The default verbosity.
Definition error.hpp:202
The mxlib c++ namespace.
Definition mxlib.hpp:37
iosT & dumpGitStatus(iosT &ios, const std::string &repoName, const std::string &branch, const std::string &sha1, const bool &modified, const std::string &section="")
Dump the git status of a repository to a stream.
Definition mxlib.hpp:53
Declares and defines functions for finding roots.
The calculated WFS uses sensitivities provided by FITS files.
Definition aoWFS.hpp:275
The ideal wavefront sensor sensitivity function.
Definition aoWFS.hpp:36
Class to manage a set of configurable values, and read their values from config/ini files and the com...
void add(const configTarget &tgt)
Add a configTarget.
bool isSet(const std::string &name, std::unordered_map< std::string, configTarget > &targets)
Check if a target has been set by the configuration.