mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
Loading...
Searching...
No Matches
pyramidSensor.hpp
Go to the documentation of this file.
1/** \file pyramidSensor.hpp
2 * \author Jared R. Males (jaredmales@gmail.com)
3 * \brief Declaration and definition of a standard 4 quadrant pyramid WFS.
4 * \ingroup mxAO_sim_files
5 *
6 */
7
8#ifndef mx_AO_sim_pyramidSensor_hpp
9#define mx_AO_sim_pyramidSensor_hpp
10
11#include <omp.h>
12
13#include "../../mxException.hpp"
14
15#include "../../wfp/imagingUtils.hpp"
16#include "../../wfp/fraunhoferPropagator.hpp"
17#include "../../sys/timeUtils.hpp"
18
19#include "../../improc/eigenImage.hpp"
20#include "../../improc/imageMasks.hpp"
21
22#include "../../math/constants.hpp"
23#include "../../math/geo.hpp"
24
25#include "wavefront.hpp"
26
27#ifdef DEBUG
28#define BREAD_CRUMB std::cout << "DEBUG: " << __FILE__ << " " << __LINE__ << "\n";
29#else
30#define BREAD_CRUMB
31#endif
32
33namespace mx
34{
35namespace AO
36{
37namespace sim
38{
39
40template <typename _realT>
41struct wfsImageT
42{
43 typedef _realT realT;
44
45 unsigned iterNo;
46
47 /// The wavefront sensor detector image type
48 typedef Eigen::Array<realT, Eigen::Dynamic, Eigen::Dynamic> imageT;
49
50 imageT image;
51
52 imageT tipImage;
53};
54
55/// A Pyramid Sensor Simulation
56/**
57 *
58 * \tparam _realT is the real floating point type used for calculations
59 * \tparam _detectorT is the detector used to record the PyWFS image. Must conform to the mx::AO::sim detector
60 * interface specifications.
61 */
62template <typename _realT, typename _detectorT>
64{
65 public:
66 /// The real floating point type used for calculations
67 typedef _realT realT;
68
69 /// The complex floating point type used for calculations
70 typedef std::complex<realT> complexT;
71
72 /// The wavefront data type
74
75 /// The wavefront complex field type
76 typedef wfp::imagingArray<std::complex<realT>, wfp::fftwAllocator<std::complex<realT>>, 0> complexFieldT;
77
78 /// The wavefront sensor detector image type
79 typedef _detectorT detectorT;
80
81 public:
82 /// Default c'tor
84
85 /** \name Standard WFS Interface
86 *
87 * @{
88 */
89
90 protected:
91 /* Standard WFS Interface: */
92 uint32_t m_wfSz{ 0 }; ///< Size of the wavefront in pixels
93
94 uint32_t m_detRows{ 0 }; ///< The number of rows of the WFS detector. After forming the image the WFS detector
95 ///< plane is re-binned to this.
96
97 uint32_t m_detCols{ 0 }; ///< The number of columns of the WFS detector. After forming the image the WFS detector
98 ///< plane is re-binned to this.
99
100 realT m_lambda{ 0 }; ///< Central wavelength, in meters
101
102 /// \todo when the filter should be set with astrospectrum, and should re-calculate the central wavelength.
103 /// \todo need to verify that the wavefront propagation is appropriately chromatic
104 std::vector<realT> m_wavelengths; ///< Vector of wavelengths in the WFS bandpass
105 std::vector<realT> _wavelengthWeights; ///< The relative weights of the wavelengths
106
107 int m_iTime{ 1 }; ///< Integration time in loop steps
108
109 int m_roTime{ 1 }; ///< Readout time in loop steps
110
111 realT m_simStep{ 0.001 }; ///< The simulation stepsize in seconds.
112
113 public:
114 /// Get the wavefront size in pixels
115 /**
116 * \returns the wavefront size in pixels
117 */
118 int wfSz();
119
120 /// Set the wavefront size in pixels.
121 /**
122 */
123 void wfSz(const uint32_t & sz /**< the new size*/);
124
125 /// Get the detector rows in pixels
126 /**
127 * \returns m_detRows
128 */
129 uint32_t detRows();
130
131 /// Get the detector columns in pixels
132 /**
133 * \returns m_detCols
134 */
135 uint32_t detCols();
136
137 /// Set the detector columns in pixels.
138 /**
139 */
140 void detSize( const uint32_t &nrows, ///< The number of rows
141 const uint32_t &ncols ///< The number of columns
142 );
143
144 /// Get the PyWFS central wavelength
145 /**
146 * \returns the central wavelength in meters
147 */
148 realT lambda();
149
150 /// Set the PyWFS central wavelength
151 /**
152 */
153 void lambda( const realT &l /**< The central wavelength, in meters*/ );
154
155 /// Get the PyWFS integration time, in time steps
156 int iTime();
157
158 /// Set the PyWFS integration time, in time steps
159 void iTime( const uint32_t &it /**< the new integration time*/ );
160
161 /// Get the PyWFS detector readout time, in time steps
162 int roTime();
163
164 /// Set the PyWFS detector readout time, in time steps
165 void roTime( const uint32_t &rt /**< the new readout time*/ );
166
167 /// Get the simulation step-size, in seconds.
168 realT simStep();
169
170 /// Set the simulation step-size, in seconds.
171 void simStep( const realT &st /**< the new simulation step size*/ );
172
173 /// Link this WFS to an AO system simulation
174 template <typename AOSysT>
175 void linkSystem( AOSysT &AOSys /**< The AO system simulation to link to*/ );
176
177 /// Sense the wavefront aberrations
178 /** \returns true if a new wavefront measurement is ready.
179 * \returns false if still integrating.
180 */
181 bool senseWavefront( wavefrontT &pupilPlane /**< The input wavefront to be sensed*/ );
182
183 /// Sense the wavefront aberrations in calibration mode
184 /** Allows for faster calibrations.
185 */
186 bool senseWavefrontCal( wavefrontT &pupilPlane /**< The input wavefront to be sensed*/ );
187
188 public:
189 /// The WFS detector
191
192 /// The image on the detector, resized from m_wfsImage
194
195 /// @}
196
197 /** \name Pyramid Sensor Interface
198 *
199 * @{
200 */
201protected:
202 uint32_t m_nSides {4}; ///< Number of sides in the pyramid
203
204 /// The size of the pupil in wavefront pixels.
205 /** This is the maximum diameter of the pupil in wavefront pixels.
206 *
207 */
208 uint32_t m_pupilSz{ 0 };
209
210 /// The separation of the pupil images in fraction of a pupil. 0 <= m_pupilSep, default 1.
211 /** This sets the center-to-center separation of the pupils images in the focal plane wavefront.
212 * Note that the separation in detector pixels then depends on the scaling between wavefront pixels
213 * (m_wfSz) and detector pixels (m_detRows and m_detCols).
214 *
215 * This sets the size of the region in the pre-detection image that each pupil image
216 * takes up, and therefore the size of the pre-detection image.
217 * If the pupil (as defined in the input wavefront) is 60 pixels across
218 * and m_pupilSep is set to 1.06667, then there will be a 2 pixel pad around each pupil image,
219 * resulting in 4 pixels between each geometric pupil image.
220 *
221 * For a standard 4-sided pyramid, the pre-detection image will be
222 * 2*m_pupilSep*m_pupilSz across. For other n-sided pyramids, m_pupilSep still specifies the size of the pupil
223 * image region, but the total image size will be a function of the resultant pupil positions.
224 *
225 * If m_pupilSep is less than 1, this will produce the "flattened pyramid", with overlap between
226 * the pupil images. In this case, image size will also be set by pupilSz to ensure that there are enough
227 * pixels included to show all pupils.
228 */
230
231 /// The angle by which to offset the pupils, in degrees. Default is 0.
232 /** If this is 0, then a 4-sided pyramid makes a square as usual. If this is set
233 * to 45 degrees, then a 4-sided pyramid makes a diamond.
234 *
235 */
237
238 /// The size of the resulting PyWFS image in wavefront pixels.
239 /** If \ref m_imageSzAuto is true, this is determined by number of sides (\ref m_nSides), the pupil size (\ref m_pupilSz), and the
240 * pupil separation (\ref m_pupilSep). For a 4 sided pyramid this will be the larger of
241 * 2*m_pupilSep*m_pupilSz and 2*m_pupilSz.
242 *
243 * If , then this is used regardless of the optimum size.
244 */
245 uint32_t m_imageSz {0};
246
247 bool m_imageSzAuto{ true }; ///< Flag to track if \ref m_imageSz should be set to 0.
248
249 realT m_wfPS {0}; ///< Wavefront pixel scale, in meters/pixel
250
251 realT m_D {0}; ///< Telescope diameter, in meters
252
253 uint32_t m_modSteps{ 20 }; ///< Number of modulation steps in one integration. Can be set explicitly, but will be
254 ///< calculated if \ref m_perStep is set.
255
256 realT m_perStep{ 1 }; /**< The minimum number of lamba/D per step in the modulation.
257 Smaller will result in more steps.*/
258
259 realT m_modRadius{ 3.0 }; ///< Radius of the modulation in pixels
260
261 public:
262 /// Get the number of pyramid sides
263 /**
264 * \returns the number of sides on the pyramid
265 */
266 int nSides();
267
268 /// Set the number of sides on the pyramid
269 /**
270 */
271 void nSides(const uint32_t & ns /**< The new number of sides on the pyramid*/);
272
273 /// Get the minimum number of modulation steps
274 /**
275 * \returns m_perStep;
276 */
277 realT perStep();
278
279 /// Set the minimum number of modulation steps
280 /**
281 * \param mSt is the new number of modulation steps
282 */
283 void perStep( const realT &prStp /**< The minimum number of lamba/D per step to take*/ );
284
285 /// Get the number of modulation steps
286 /**
287 * \returns m_modSteps, which is defined by perStep.
288 */
289 int modSteps();
290
291 /// Get the radius of modulation
292 /**
293 * \returns m_modRadius;
294 */
295 realT modRadius();
296
297 /// Set the modulation radius
298 /**
299 * \param mR is the new modulation radius in lambda/D
300 */
301 void modRadius( const realT &mR /**< [in] the new value of modulation radius */ );
302
303 /// Get the wavefront pixel scale in meters per pixel
304 /**
305 * \returns the wavefront pixel scale in meters/pixel
306 */
307 realT wfPS();
308
309 /// Get the telescope diameter
310 /**
311 * \returns the telescope diameter in meters
312 */
313 realT D();
314
315 /// Set the telescope diameter
316 /**
317 * \param d is the new size in meters
318 */
319 void D( const realT &d /**< */ );
320
321 /// Get the pupil size in pixels
322 /** This is the pupil size in un-binned wavefront space
323 *
324 * \returns m_pupilSz
325 * \returns m_pupilSz
326 */
327 uint32_t pupilSz();
328
329 /// Set the pupil size in pixels.
330 /** This is the size of the pupils in un-binned wavefront space.
331 * See \ref m_pupilSz.
332 *
333 */
334 void pupilSz( const uint32_t &sz /**< the new pupil size.*/ );
335
336 /// Get the pupil separation as a fraction of pupil size
337 /// Get the pupil separation as a fraction of pupil size
338 /** This is the pupil separation in un-binned wavefront space
339 *
340 * \returns m_pupilSep
341 */
342 realT pupilSep();
343
344 /// Set the pupil separation as a fraction of pupil size
345 /// Set the pupil separation as a fraction of pupil size
346 /** This is the separation of the pupils in un-binned wavefront space.
347 * See \ref m_pupilSep.
348 *
349 */
350 void pupilSep(const realT & sz /**< the new pupil separation.*/);
351
352 /// Get the angle offset
353 /** See \ref m_angleOffset
354 *
355 * \returns m_angleOffset
356 */
357 realT angleOffset();
358
359 /// Set the angle offset
360 /** See \ref m_angleOffset.
361 *
362 */
363 void angleOffset(const realT & ao /**< the new angle offset.*/);
364
365 /// Get the image size in wavefront pixels
366 /** This is the size of the image in un-binned wavefront space
367 *
368 * \returns m_imageSz
369 */
370 uint32_t imageSz();
371
372 /// Set the image size in wavefront pixels
373 /** This is the size of the image in un-binned wavefront space
374 * Setting a non-zero value also sets m_imageSizeAuto to false.
375 * Setting 0 also sets m_imageSizeAuto to true.
376 */
377 void imageSz( const uint32_t &is );
378
379 /// Get the value of the image size auto flag
380 /** This controls whether image size is set automatically
381 *
382 * \returns m_imageSz
383 */
384 bool imageSzAuto();
385
386 /// Set the value of the image size auto flag
387 /** This controls whether image size is set automatically
388 *
389 */
390 void imageSzAuto( const bool &ia );
391 ///@}
392
394
395 bool m_opdMaskMade{ false };
396 complexFieldT m_opdMask;
397
398 bool m_tiltsMade{ false };
399 std::vector<complexFieldT> m_tilts;
400
401 bool m_preAllocated{ false };
402 complexFieldT m_pupilPlaneCF;
403
404 // Pre-allocated working memory:
405
406 std::vector<complexFieldT> m_th_tiltedPlane; ///< Thread-local modulated wavefront
407
408 std::vector<complexFieldT> m_th_focalPlane; ///< Thread-local tip wavefront, used for FFT tilting
409
410 std::vector<typename wfsImageT<realT>::imageT> m_th_focalImage; ///< Thread-local tip image
411
412 std::vector<complexFieldT> m_th_sensorPlane; ///< Thread-local sensor-pupil-plane wavefront
413
414 std::vector<typename wfsImageT<realT>::imageT>
415 m_th_sensorImage; ///< Thread-local sensor-pupil-plane intensity image
416
417 int m_iTime_counter{ 0 };
418
419 int m_reading{ 0 };
420
421 int m_roTime_counter{ 0 };
422
423 std::vector<wavefrontT> _wavefronts;
424
425 int m_lastWavefront{ 0 };
426
427 /// The image formed by the WFS
429
430 public:
431 wfsImageT<realT> wfsTipImage;
432
433 protected:
434 void makeOpdMask();
435
436 void makeTilts();
437
438 void allocThreadMem();
439
440 void preAllocate();
441
442 void doSenseWavefront();
443 void doSenseWavefront( wavefrontT & /**< */ );
444 void doSenseWavefrontNoMod( wavefrontT & /**< */ );
445
446 bool m_firstRun{ true };
447};
448
449template <typename realT, typename detectorT>
451{
452 iTime( m_iTime );
453
454 m_frProp.wholePixel( 0 );
455}
456
457template <typename realT, typename detectorT>
459{
460 return m_wfSz;
461}
462
463template <typename realT, typename detectorT>
465{
466 if( m_wfSz == sz )
467 {
468 return;
469 }
470
471 m_wfSz = sz;
472
473 m_tiltsMade = false;
474 m_opdMaskMade = false;
475 m_preAllocated = false;
476}
477
478template <typename realT, typename detectorT>
480{
481 return m_detRows;
482}
483
484template <typename realT, typename detectorT>
486{
487 return m_detCols;
488}
489
490template <typename realT, typename detectorT>
491void pyramidSensor<realT, detectorT>::detSize( const uint32_t &nrows, const uint32_t &ncols )
492{
493 if( m_detRows == nrows && m_detCols == ncols )
494 return;
495
496 m_detRows = nrows;
497 m_detCols = ncols;
498
499 detector.setSize(m_detRows, m_detCols);
500 detectorImage.image.resize(m_detRows, m_detCols);
501
502 m_opdMaskMade = false; //make sure size check is run on current settings
503
504 m_opdMaskMade = false; //make sure size check is run on current settings
505}
506
507template <typename realT, typename detectorT>
509{
510 return m_lambda;
511}
512
513template <typename realT, typename detectorT>
515{
516 m_lambda = l;
517
518 //---------------------------------------
519 // Check if wavelength vector is filled
520 //---------------------------------------
521 if( m_wavelengths.size() == 0 )
522 {
523 m_wavelengths.resize( 1, m_lambda );
524 _wavelengthWeights.resize( 1, 1.0 );
525 }
526}
527
528template <typename realT, typename detectorT>
530{
531 return m_iTime;
532}
533
534template <typename realT, typename detectorT>
536{
537 if( it < 1 )
538 {
539 mxThrowException( mx::err::invalidconfig, "pyramidSensor::iTime", "iTime must be >= 1" );
540 }
541
542 m_iTime = it;
543
544 _wavefronts.resize( m_iTime + 2 );
545 m_lastWavefront = -1;
546
547 detector.expTime( m_simStep * m_iTime );
548}
549
550template <typename realT, typename detectorT>
552{
553 return roTime;
554}
555
556template <typename realT, typename detectorT>
558{
559 if( rt < 1 )
560 {
561 mxThrowException( mx::err::invalidconfig, "pyramidSensor::roTime", "roTime must be >= 1" );
562 }
563
564 m_roTime = rt;
565}
566
567template <typename realT, typename detectorT>
569{
570 return m_simStep;
571}
572
573template <typename realT, typename detectorT>
575{
576
577 m_simStep = st;
578
579 detector.expTime( m_simStep * m_iTime );
580}
581
582template <typename realT, typename detectorT>
583template <typename AOSysT>
585{
586 AOSys.wfs.wfPS( AOSys.m_wfPS );
587 AOSys.wfs.D( AOSys.m_D );
588}
589
590template <typename realT, typename detectorT>
592{
593
594 ++m_lastWavefront;
595 if( m_lastWavefront >= _wavefronts.size() )
596 m_lastWavefront = 0;
597 _wavefronts[m_lastWavefront].amplitude = pupilPlane.amplitude;
598 _wavefronts[m_lastWavefront].phase = pupilPlane.phase;
599 _wavefronts[m_lastWavefront].iterNo = pupilPlane.iterNo;
600
601 // Always skip the first one for averaging to center of iTime.
602 if( m_firstRun )
603 {
604 m_firstRun = false;
605 return false;
606 }
607
608 ++m_iTime_counter;
609
610 bool rv = false;
611
612 if( m_reading )
613 {
614 ++m_roTime_counter;
615
616 if( m_roTime_counter >= m_roTime )
617 {
618 detector.exposeImage( detectorImage.image, m_wfsImage.image );
619
620 detectorImage.tipImage = wfsTipImage.image;
621 detectorImage.iterNo = m_wfsImage.iterNo;
622
623 m_roTime_counter = 0;
624 m_reading = 0;
625 rv = true;
626 }
627 }
628
629 if( m_iTime_counter >= m_iTime )
630 {
631 doSenseWavefront();
632 m_iTime_counter = 0;
633
634 m_reading = 1;
635 m_roTime_counter = 0;
636 }
637
638 return rv;
639}
640
641template <typename realT, typename detectorT>
643{
644
645 BREAD_CRUMB;
646
647 doSenseWavefront(pupilPlane);
648
649
650 BREAD_CRUMB;
651
652 detector.exposeImage( detectorImage.image, m_wfsImage.image );
653
654 BREAD_CRUMB;
655
656 detectorImage.tipImage = wfsTipImage.image;
657
658 BREAD_CRUMB;
659
660 return true;
661}
662
663/* Pyramid Specifics */
664
665template <typename realT, typename detectorT>
667{
668 return m_nSides;
669}
670
671template <typename realT, typename detectorT>
673 m_nSides = ns;
674 m_opdMaskMade = false;
675}
676
677template <typename realT, typename detectorT>
679{
680 return m_wfPS;
681}
682
683template <typename realT, typename detectorT>
685{
686 return m_D;
687}
688
689template <typename realT, typename detectorT>
691{
692 m_D = d;
693
694 if(m_pupilSz > 0) //Avoid making inf or nan so m_wfPS remains unset. note that fast-math make detecting inf and nan hard.
695 {
696 m_wfPS = m_D/m_pupilSz;
697 }
698 else
699 {
700 m_wfPS = 0;
701 }
702
703 m_tiltsMade = false;
704 m_opdMaskMade = false;
705 m_preAllocated = false;
706 m_opdMaskMade = false;
707 m_preAllocated = false;
708}
709
710template <typename realT, typename detectorT>
712{
713 return m_perStep;
714}
715
716template <typename realT, typename detectorT>
718{
719 m_perStep = prStp;
720
721 if( m_modRadius <= 0 )
722 {
723 m_modSteps = 0;
724 return;
725 }
726
727 realT radPerStep = m_perStep / m_modRadius;
728
729 // Get the minimum number of steps to meet m_perStep while having symmetry for the quadrants
730 m_modSteps = 1;
731 while( math::half_pi<realT>() / m_modSteps > radPerStep )
732 {
733 ++m_modSteps;
734 }
735
736 m_modSteps *= 4;
737
738 m_tiltsMade = false;
739}
740
741template <typename realT, typename detectorT>
743{
744 return m_modSteps;
745}
746
747template <typename realT, typename detectorT>
749{
750 return m_modRadius;
751}
752
753template <typename realT, typename detectorT>
755{
756 m_modRadius = mR;
757 perStep( m_perStep ); // to calculate m_modSteps;
758
759 m_tiltsMade = false;
760}
761
762template <typename realT, typename detectorT>
764{
765 return m_pupilSz;
766}
767
768template <typename realT, typename detectorT>
770{
771 if( m_pupilSz == sz )
772 {
773 return;
774 }
775
776 m_pupilSz = sz;
777
778 if(m_pupilSz > 0) //Avoid making inf or nan so m_wfPS remains unset. note that fast-math make detecting inf and nan hard.
779 {
780 m_wfPS = m_D/m_pupilSz;
781 }
782 else
783 {
784 m_wfPS = 0;
785 }
786
787 if(m_pupilSz > 0) //Avoid making inf or nan so m_wfPS remains unset. note that fast-math make detecting inf and nan hard.
788 {
789 m_wfPS = m_D/m_pupilSz;
790 }
791 else
792 {
793 m_wfPS = 0;
794 }
795
796 m_tiltsMade = false;
797 m_opdMaskMade = false;
798 m_preAllocated = false;
799}
800
801template <typename realT, typename detectorT>
803{
804 return m_pupilSep;
805}
806
807template <typename realT, typename detectorT>
809{
810 if( m_pupilSep == sz )
811 {
812 return;
813 }
814
815 m_pupilSep = sz;
816
817 m_opdMaskMade = false;
818 m_preAllocated = false;
819}
820
821template <typename realT, typename detectorT>
823{
824 return m_angleOffset;
825}
826
827template <typename realT, typename detectorT>
829{
830 if (m_angleOffset == ao)
831 {
832 return;
833 }
834
835 m_angleOffset = ao;
836
837 m_opdMaskMade = false;
838 m_preAllocated = false;
839}
840
841template <typename realT, typename detectorT>
843{
844 if( !m_opdMaskMade )
845 {
846 makeOpdMask();
847 }
848
849 return m_imageSz;
850}
851
852template <typename realT, typename detectorT>
854{
855 if( m_imageSz == sz )
856 {
857 return;
858 }
859
860 m_imageSz = sz;
861
862 if( m_imageSz == 0 )
863 {
864 m_imageSzAuto = true;
865 }
866 else
867 {
868 m_imageSzAuto = false;
869 }
870
871 m_opdMaskMade = false;
872 m_preAllocated = false;
873}
874
875template <typename realT, typename detectorT>
877{
878 return m_imageSzAuto;
879}
880
881template <typename realT, typename detectorT>
883{
884 m_imageSzAuto = ia;
885
886 m_opdMaskMade = false;
887
888 m_preAllocated = false;
889}
890
891template <typename realT, typename detectorT>
893{
894 complexFieldT opdMaskQ;
895
896 if(m_wfPS == 0)
897 {
898 mxThrowException(mx::err::invalidconfig, "pyramidSensor::makeOpdMask()", "wavefront platescale (m_wfPS) is 0. Must set pupilSz and D first.");
899 }
900
901 if(!std::isfinite(m_wfPS) || !std::isnormal(m_wfPS))
902 {
903 mxThrowException(mx::err::invalidconfig, "pyramidSensor::makeOpdMask()", "wavefront platescale (m_wfPS) is infinite. Must set pupilSz and D first.");
904 }
905
906 std::cerr << m_wfPS << " " << m_D << "\n";
907
908 if(m_D == 0)
909 {
910 mxThrowException(mx::err::invalidconfig, "pyramidSensor::makeOpdMask()", "pupil diameter is 0. Must set D > 0 first.");
911 }
912
913 //Setup the Fraunhoffer Propagator
914 m_frProp.setWavefrontSizePixels(m_wfSz);
915
916 m_opdMask.resize(m_wfSz, m_wfSz);
917 opdMaskQ.resize(m_wfSz, m_wfSz);
918
920
921 mask.resize(m_opdMask.rows(), m_opdMask.cols());
922 realT dang = mx::math::two_pi<realT>()/m_nSides;
923
924 realT minx = 0;
925 realT maxx = 0;
926 realT miny = 0;
927 realT maxy = 0;
928
929 realT pupilRad = m_pupilSep*m_pupilSz /(2*sin(dang/2.0));
930
931 for(int n = 0; n < m_nSides; ++n)
932 {
933 realT ang = m_angleOffset*math::degreesT<realT>::radians + 0.5*dang + n * dang;
934
935 realT dx = pupilRad * cos(ang);
936
937 if(dx < minx) minx = dx;
938 if(dx > maxx) maxx = dx;
939
940 realT dy = pupilRad * sin(ang);
941
942 if(dy < miny) miny = dy;
943 if(dy > maxy) maxy = dy;
944
945 opdMaskQ.set(std::complex<realT>(0, 1));
946 wfp::tiltWavefront(opdMaskQ, dx, dy);
947 mask.setZero();
948 improc::maskWedge(mask, 0.5*(mask.rows()-1), 0.5*(mask.cols()-1), math::rtod(ang), 0.5*math::rtod(dang), 1);
949 wfp::extractMaskedPixels(m_opdMask, opdMaskQ, mask);
950 }
951
952 int xsz = 2*std::max( {fabs(maxx), fabs(minx)} ) + 2*std::max({(pupilRad/2), ((realT)m_pupilSz/2)});
953
954 int ysz = 2*std::max( {fabs(maxy), fabs(miny)} ) + 2*std::max({(pupilRad/2), ((realT)m_pupilSz/2)});
955
956 if(m_imageSzAuto)
957 {
958 m_imageSz = std::max(xsz, ysz);
959
960 if(m_pupilSep > 1)
961 {
962 m_imageSz += (m_pupilSep-1.0)*m_pupilSz;
963 }
964 }
965
966 if(m_imageSz > m_wfSz)
967 {
968 std::string msg = "image size (m_imageSz = " + std::to_string(m_imageSz) + ") ";
969 msg += "> wavefront size (m_wfSz = " + std::to_string(m_wfSz) + "). ";
970 msg += "Decrease number of sides (m_nSides = " + std::to_string(m_nSides) + ") or increase wavefront size. ";
971 mxThrowException(mx::err::invalidconfig, "pyramidSensor::makeOpdMask", msg);
972 }
973
974 m_wfsImage.image.resize(m_imageSz, m_imageSz);
975
976 if(m_detRows == 0 || m_detCols == 0)
977 {
978 mxThrowException(mx::err::invalidconfig, "pyramidSensor::makeOpdMask", "must set detector size");
979 }
980
981 if(m_detRows > m_imageSz || m_detCols > m_imageSz)
982 {
983 std::string msg = "image size (m_imageSz = " + std::to_string(m_imageSz) + ") ";
984 msg += "< detector size size (m_detRows = " + std::to_string(m_detRows) ;
985 msg += " m_detCols = " + std::to_string(m_detCols) + "). ";
986 mxThrowException(mx::err::invalidconfig, "pyramidSensor::makeOpdMask", msg);
987 }
988
989 m_opdMaskMade = true;
990}
991
992template <typename realT, typename detectorT>
993void pyramidSensor<realT, detectorT>::makeTilts()
994{
995 constexpr realT pi = math::pi<realT>();
996
997 if( m_modSteps == 0 )
998 {
1000 "pyramidSensor::makeTilts()",
1001 "number of modulation steps (m_modSteps) has not been set." );
1002 }
1003
1004 if(m_wfPS == 0)
1005 {
1007 "pyramidSensor::makeTilts()",
1008 "wavefront platescale (m_wfPS) is 0. Must set pupilSz and D first." );
1009 }
1010
1011 if( !std::isfinite( m_wfPS ) )
1012 {
1014 "pyramidSensor::makeTilts()",
1015 "wavefront platescale (m_wfPS) is infinite. Must set pupilSz and D first." );
1016 }
1017
1018 if(m_D == 0)
1019 {
1020 mxThrowException(mx::err::invalidconfig, "pyramidSensor::makeTilts()", "pupil diameter is 0. Must set D > 0 first.");
1021 }
1022
1023 realT dang = 2 * pi / ( m_modSteps );
1024 realT dx, dy;
1025
1026 m_tilts.resize( m_modSteps );
1027
1028 std::cout << "WF Size: " << m_wfSz << "\n";
1029 std::cout << "WF PS: " << m_wfPS << "\n";
1030 std::cout << "Lambda: " << m_lambda << "\n";
1031 std::cout << "Pyr. PS: " << wfp::fftPlateScale<realT>( m_wfSz, m_wfPS, m_lambda ) * 206265. << " (mas/pix)\n";
1032 std::cout << "Mod. steps: " << m_modSteps << "\n";
1033 std::cout << "Mod rad: " << m_modRadius * ( m_lambda / m_D ) / wfp::fftPlateScale<realT>( m_wfSz, m_wfPS, m_lambda )
1034 << " (pixels)\n";
1035
1036 for( int i = 0; i < m_modSteps; ++i )
1037 {
1038 dx = m_modRadius * ( m_lambda / m_D ) / wfp::fftPlateScale<realT>( m_wfSz, m_wfPS, m_lambda ) *
1039 cos( 0.0 * dang + dang * i );
1040 dy = m_modRadius * ( m_lambda / m_D ) / wfp::fftPlateScale<realT>( m_wfSz, m_wfPS, m_lambda ) *
1041 sin( 0.0 * dang + dang * i );
1042
1043 m_tilts[i].resize( m_wfSz, m_wfSz );
1044 m_tilts[i].set( std::complex<realT>( 0, 1 ) );
1045
1046 wfp::tiltWavefront( m_tilts[i], dx, dy );
1047 }
1048
1049 m_tiltsMade = true;
1050}
1051
1052template <typename realT, typename detectorT>
1053void pyramidSensor<realT, detectorT>::allocThreadMem()
1054{
1055 if( !m_opdMaskMade )
1056 {
1057 makeOpdMask(); // Needed for m_imageSz
1058 }
1059
1060 m_pupilPlaneCF.resize( m_wfSz, m_wfSz );
1061
1062 int maxTh = omp_get_max_threads();
1063 m_th_tiltedPlane.resize( maxTh );
1064
1065 m_th_focalPlane.resize( maxTh );
1066
1067 m_th_focalImage.resize( maxTh );
1068
1069 m_th_sensorPlane.resize( maxTh );
1070
1071 m_th_sensorImage.resize( maxTh );
1072
1073 for( int nTh = 0; nTh < maxTh; ++nTh )
1074 {
1075 m_th_tiltedPlane[nTh].resize( m_wfSz, m_wfSz );
1076
1077 m_th_focalPlane[nTh].resize( m_wfSz, m_wfSz );
1078
1079 m_th_focalImage[nTh].resize( m_wfSz, m_wfSz );
1080
1081 m_th_sensorPlane[nTh].resize( m_wfSz, m_wfSz );
1082
1083 m_th_sensorImage[nTh].resize( m_imageSz, m_imageSz );
1084 }
1085
1086 m_preAllocated = true;
1087}
1088
1089template <typename realT, typename detectorT>
1090void pyramidSensor<realT, detectorT>::preAllocate()
1091{
1092 if( !m_tiltsMade )
1093 {
1094 makeTilts();
1095 }
1096
1097 if( !m_opdMaskMade )
1098 {
1099 makeOpdMask();
1100 }
1101
1102 if( !m_preAllocated )
1103 {
1104 allocThreadMem();
1105 }
1106}
1107
1108template <typename realT, typename detectorT>
1109void pyramidSensor<realT, detectorT>::doSenseWavefront()
1110{
1111 BREAD_CRUMB;
1112
1113 wavefrontT pupilPlane;
1114
1115 /* Here make average wavefront for now */
1116 int _firstWavefront = m_lastWavefront - m_iTime;
1117 if( _firstWavefront < 0 )
1118 _firstWavefront += _wavefronts.size();
1119
1120 pupilPlane.amplitude = _wavefronts[_firstWavefront].amplitude;
1121 pupilPlane.phase = _wavefronts[_firstWavefront].phase;
1122
1123 realT avgIt = _wavefronts[_firstWavefront].iterNo;
1124
1125 BREAD_CRUMB;
1126
1127 for( int i = 0; i < m_iTime; ++i )
1128 {
1129 ++_firstWavefront;
1130 if( (size_t)_firstWavefront >= _wavefronts.size() )
1131 _firstWavefront = 0;
1132
1133 pupilPlane.amplitude += _wavefronts[_firstWavefront].amplitude;
1134 pupilPlane.phase += _wavefronts[_firstWavefront].phase;
1135 avgIt += _wavefronts[_firstWavefront].iterNo;
1136 }
1137
1138 BREAD_CRUMB;
1139
1140 pupilPlane.amplitude /= ( m_iTime + 1 );
1141 pupilPlane.phase /= ( m_iTime + 1 );
1142
1143 avgIt /= ( m_iTime + 1.0 );
1144
1145 /*=====================================*/
1146 doSenseWavefront( pupilPlane );
1147
1148 m_wfsImage.iterNo = avgIt;
1149}
1150
1151template <typename realT, typename detectorT>
1152void pyramidSensor<realT, detectorT>::doSenseWavefront( wavefrontT &pupilPlane )
1153{
1154 if( m_modRadius == 0 )
1155 {
1156 return doSenseWavefrontNoMod( pupilPlane );
1157 }
1158
1159 if( !m_preAllocated )
1160 {
1161 preAllocate();
1162 }
1163
1164 BREAD_CRUMB;
1165
1166 m_wfsImage.image.resize( m_imageSz, m_imageSz );
1167 m_wfsImage.image.setZero();
1168
1169 wfsTipImage.image.resize( m_wfSz, m_wfSz );
1170 wfsTipImage.image.setZero();
1171
1172 for( size_t l = 0; l < m_wavelengths.size(); ++l )
1173 {
1174 pupilPlane.lambda = m_lambda;
1175 pupilPlane.getWavefront( m_pupilPlaneCF, m_wavelengths[l], m_wfSz );
1176
1177#pragma omp parallel
1178 {
1179 int nTh = omp_get_thread_num();
1180
1181 m_th_sensorImage[nTh].setZero();
1182 m_th_focalImage[nTh].setZero();
1183
1184 complexT *tpm_Data;
1185 complexT *ppm_Data;
1186 complexT *tim_Data;
1187 complexT *fpm_Data;
1188 complexT *opdm_Data;
1189
1190 ppm_Data = m_pupilPlaneCF.data();
1191 tpm_Data = m_th_tiltedPlane[nTh].data();
1192 fpm_Data = m_th_focalPlane[nTh].data();
1193 opdm_Data = m_opdMask.data();
1194
1195 int nelem = m_wfSz * m_wfSz;
1196
1197#pragma omp for
1198 for( int i = 0; i < m_modSteps; ++i )
1199 {
1200
1201 tim_Data = m_tilts[i].data();
1202
1203 //---------------------------------------------
1204 // Apply the modulating tip
1205 //---------------------------------------------
1206 for( int ii = 0; ii < nelem; ++ii )
1207 {
1208 tpm_Data[ii] = ppm_Data[ii] * tim_Data[ii];
1209 }
1210
1211 //---------------------------------------------
1212 // Propagate to Pyramid tip
1213 //---------------------------------------------
1214 m_frProp.propagatePupilToFocal( m_th_focalPlane[nTh], m_th_tiltedPlane[nTh], true );
1215
1216 //---------------------------------------------
1217 // Extract the tip image.
1218 //---------------------------------------------
1219 wfp::extractIntensityImageAccum(
1220 m_th_focalImage[nTh], 0, m_wfSz, 0, m_wfSz, m_th_focalPlane[nTh], 0, 0 );
1221
1222 //---------------------------------------------
1223 // Now apply the pyramid OPD
1224 //---------------------------------------------
1225 for( int ii = 0; ii < nelem; ++ii )
1226 {
1227 fpm_Data[ii] = fpm_Data[ii] * opdm_Data[ii];
1228 }
1229
1230 //---------------------------------------------
1231 // Propagate to sensor plane
1232 //---------------------------------------------
1233 m_frProp.propagateFocalToPupil( m_th_sensorPlane[nTh], m_th_focalPlane[nTh], true );
1234
1235 //---------------------------------------------
1236 // Extract the image.
1237 //---------------------------------------------
1238 wfp::extractIntensityImageAccum( m_th_sensorImage[nTh],
1239 0,
1240 m_imageSz,
1241 0,
1242 m_imageSz,
1243 m_th_sensorPlane[nTh],
1244 0.5 * m_wfSz - m_imageSz / 2,
1245 0.5 * m_wfSz - m_imageSz / 2 );
1246
1247 } // for
1248
1249 BREAD_CRUMB;
1250
1251#pragma omp critical
1252 {
1253 m_wfsImage.image += m_th_sensorImage[nTh] * _wavelengthWeights[l];
1254 wfsTipImage.image += m_th_focalImage[nTh] * _wavelengthWeights[l];
1255 }
1256 } // #pragma omp parallel
1257
1258 } // l for wavelength
1259
1260 BREAD_CRUMB;
1261
1262 m_wfsImage.image /= m_modSteps;
1263 wfsTipImage.image /= m_modSteps;
1264}
1265
1266template <typename realT, typename detectorT>
1267void pyramidSensor<realT, detectorT>::doSenseWavefrontNoMod( wavefrontT &pupilPlane )
1268{
1269 BREAD_CRUMB;
1270
1271 if( !m_opdMaskMade )
1272 {
1273 makeOpdMask();
1274 }
1275
1276 m_wfsImage.image.resize( m_imageSz, m_imageSz );
1277 m_wfsImage.image.setZero();
1278
1279 wfsTipImage.image.resize( m_wfSz, m_wfSz );
1280 wfsTipImage.image.setZero();
1281
1282 complexFieldT m_pupilPlaneCF;
1283
1284 pupilPlane.getWavefront( m_pupilPlaneCF, m_wfSz );
1285
1286 complexFieldT tiltedPlane;
1287 complexFieldT focalPlane;
1288 complexFieldT sensorPlane;
1289
1290 tiltedPlane.resize( m_wfSz, m_wfSz );
1291 focalPlane.resize( m_wfSz, m_wfSz );
1292 sensorPlane.resize( m_wfSz, m_wfSz );
1293
1294 int nelem = m_wfSz * m_wfSz;
1295
1296 complexT *tpm_Data = tiltedPlane.data();
1297 complexT *ppm_Data = m_pupilPlaneCF.data();
1298 complexT *opdm_Data = m_opdMask.data();
1299 complexT *fpm_Data = focalPlane.data();
1300
1301 BREAD_CRUMB;
1302
1303 //---------------------------------------------
1304 // Apply NO modulator tilt
1305 //---------------------------------------------
1306 for( int ii = 0; ii < nelem; ++ii )
1307 {
1308 tpm_Data[ii] = ppm_Data[ii];
1309 }
1310
1311 BREAD_CRUMB;
1312
1313 //---------------------------------------------
1314 // Propagate to Pyramid tip
1315 //---------------------------------------------
1316 m_frProp.propagatePupilToFocal( focalPlane, tiltedPlane, true );
1317
1318 BREAD_CRUMB;
1319
1320 //---------------------------------------------
1321 // Extract the tip image.
1322 //---------------------------------------------
1323 wfp::extractIntensityImageAccum( wfsTipImage.image, 0, m_wfSz, 0, m_wfSz, focalPlane, 0, 0 );
1324
1325 BREAD_CRUMB;
1326
1327 //---------------------------------------------
1328 // Now apply the pyramid OPD
1329 //---------------------------------------------
1330 for( int ii = 0; ii < nelem; ++ii )
1331 {
1332 fpm_Data[ii] = fpm_Data[ii] * opdm_Data[ii];
1333 }
1334
1335 BREAD_CRUMB;
1336
1337 //---------------------------------------------
1338 // Propagate to sensor plane
1339 //---------------------------------------------
1340 m_frProp.propagateFocalToPupil( sensorPlane, focalPlane, true );
1341
1342 BREAD_CRUMB;
1343
1344 //---------------------------------------------
1345 // Extract the image.
1346 //---------------------------------------------
1347 wfp::extractIntensityImageAccum( m_wfsImage.image,
1348 0,
1349 m_imageSz,
1350 0,
1351 m_imageSz,
1352 sensorPlane,
1353 0.5 * m_wfSz - m_imageSz / 2,
1354 0.5 * m_wfSz - m_imageSz / 2 );
1355
1356 BREAD_CRUMB;
1357}
1358
1359} // namespace sim
1360} // namespace AO
1361} // namespace mx
1362
1363#endif // mx_AO_sim_pyramidSensor_hpp
A Pyramid Sensor Simulation.
std::vector< realT > _wavelengthWeights
The relative weights of the wavelengths.
wfsImageT< realT > m_wfsImage
The image formed by the WFS.
std::vector< typename wfsImageT< realT >::imageT > m_th_focalImage
Thread-local tip image.
realT lambda()
Get the PyWFS central wavelength.
std::vector< complexFieldT > m_th_focalPlane
Thread-local tip wavefront, used for FFT tilting.
wfp::imagingArray< std::complex< realT >, wfp::fftwAllocator< std::complex< realT > >, 0 > complexFieldT
The wavefront complex field type.
int wfSz()
Get the wavefront size in pixels.
uint32_t pupilSz()
Get the pupil size in pixels.
realT m_D
Telescope diameter, in meters.
realT simStep()
Get the simulation step-size, in seconds.
realT m_angleOffset
The angle by which to offset the pupils, in degrees. Default is 0.
_realT realT
The real floating point type used for calculations.
realT m_pupilSep
The separation of the pupil images in fraction of a pupil. 0 <= m_pupilSep, default 1.
int nSides()
Get the number of pyramid sides.
void detSize(const uint32_t &nrows, const uint32_t &ncols)
Set the detector columns in pixels.
int iTime()
Get the PyWFS integration time, in time steps.
void linkSystem(AOSysT &AOSys)
Link this WFS to an AO system simulation.
realT m_lambda
Central wavelength, in meters.
realT m_wfPS
Wavefront pixel scale, in meters/pixel.
bool imageSzAuto()
Get the value of the image size auto flag.
realT wfPS()
Get the wavefront pixel scale in meters per pixel.
int m_roTime
Readout time in loop steps.
uint32_t detCols()
Get the detector columns in pixels.
int modSteps()
Get the number of modulation steps.
uint32_t imageSz()
Get the image size in wavefront pixels.
int roTime()
Get the PyWFS detector readout time, in time steps.
uint32_t m_wfSz
Size of the wavefront in pixels.
std::vector< complexFieldT > m_th_sensorPlane
Thread-local sensor-pupil-plane wavefront.
uint32_t m_imageSz
The size of the resulting PyWFS image in wavefront pixels.
wavefront< realT > wavefrontT
The wavefront data type.
realT m_modRadius
Radius of the modulation in pixels.
uint32_t detRows()
Get the detector rows in pixels.
std::vector< complexFieldT > m_th_tiltedPlane
Thread-local modulated wavefront.
std::vector< typename wfsImageT< realT >::imageT > m_th_sensorImage
Thread-local sensor-pupil-plane intensity image.
uint32_t m_pupilSz
The size of the pupil in wavefront pixels.
realT m_simStep
The simulation stepsize in seconds.
realT modRadius()
Get the radius of modulation.
realT perStep()
Get the minimum number of modulation steps.
int m_iTime
Integration time in loop steps.
std::complex< realT > complexT
The complex floating point type used for calculations.
bool senseWavefrontCal(wavefrontT &pupilPlane)
Sense the wavefront aberrations in calibration mode.
bool senseWavefront(wavefrontT &pupilPlane)
Sense the wavefront aberrations.
realT D()
Get the telescope diameter.
_detectorT detectorT
The wavefront sensor detector image type.
uint32_t m_nSides
Number of sides in the pyramid.
detectorT detector
The WFS detector.
std::vector< realT > m_wavelengths
Vector of wavelengths in the WFS bandpass.
Eigen::Array< floatT, Eigen::Dynamic, Eigen::Dynamic > wfsImageT
The wavefront sensor detector image type.
bool m_imageSzAuto
Flag to track if m_imageSz should be set to 0.
wfsImageT< realT > detectorImage
The image on the detector, resized from m_wfsImage.
realT angleOffset()
Get the angle offset.
mxException for invalid config settings
Class to perform Fraunhofer propagation between pupil and focal planes.
Eigen::Array< scalarT, -1, -1 > eigenImage
Definition of the eigenImage type, which is an alias for Eigen::Array.
#define mxThrowException(extype, src, expl)
Throw an exception. This macro takes care of the file and line.
constexpr T pi()
Get the value of pi.
Definition constants.hpp:55
constexpr floatT six_fifths()
Return 6/5 in the specified precision.
realT rtod(realT q)
Convert from radians to degrees.
Definition geo.hpp:153
void maskWedge(arrayT &m, typename arrayT::Scalar xcen, typename arrayT::Scalar ycen, typename arrayT::Scalar angCen, typename arrayT::Scalar angHW, typename arrayT::Scalar val=0)
Mask a wedge in an image.
void tiltWavefront(wavefrontT &complexWavefront, typename wavefrontT::Scalar::value_type xTilt, typename wavefrontT::Scalar::value_type yTilt)
Apply a tilt to a wavefront.
The mxlib c++ namespace.
Definition mxError.hpp:40
Structure containing the phase and amplitude of a wavefront.
Definition wavefront.hpp:24
realImageT amplitude
The wavefront amplitude.
Definition wavefront.hpp:35
realT iterNo
The iteration number of this wavefront.
Definition wavefront.hpp:44
realImageT phase
The wavefront phase.
Definition wavefront.hpp:38