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 "../../mxlib.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/milkImage.hpp"
21#include "../../improc/imageMasks.hpp"
22
23#include "../../math/constants.hpp"
24#include "../../math/geo.hpp"
25
26#ifdef MXLIB_CUDA
27#include "../../math/cuda/cublasHandle.hpp"
28#endif
29
30#include "wavefront.hpp"
31
32#ifdef DEBUG
33#define BREAD_CRUMB std::cout << "DEBUG: " << __FILE__ << " " << __LINE__ << "\n";
34#else
35#define BREAD_CRUMB
36#endif
37
38namespace mx
39{
40namespace AO
41{
42namespace sim
43{
44
45template <typename _realT>
46struct wfsImageT
47{
48 typedef _realT realT;
49
50 unsigned iterNo;
51
52 /// The wavefront sensor detector image type
53 //typedef Eigen::Array<realT, Eigen::Dynamic, Eigen::Dynamic> imageT;
54 typedef improc::milkImage<realT> imageT;
55
56 imageT image;
57
58 imageT tipImage;
59};
60
61/// A Pyramid Sensor Simulation
62/**
63 *
64 * \tparam _realT is the real floating point type used for calculations
65 * \tparam _detectorT is the detector used to record the PyWFS image. Must conform to the mx::AO::sim detector
66 * interface specifications.
67 */
68template <typename _realT, typename _detectorT, int _cudaGPU = 0>
70{
71 public:
72 static constexpr int cudaGPU = _cudaGPU;
73
74 /// The real floating point type used for calculations
75 typedef _realT realT;
76
77 /// The complex floating point type used for calculations
78 typedef std::complex<realT> complexT;
79
80 /// The wavefront data type
82
83 typedef improc::eigenImage<realT> realImageT;
84
85 /// The wavefront complex field type
87
88 /// The real array data type
89 typedef typename wfp::fraunhoferPropagatorArrayT<realImageT, cudaGPU>::arrayT realArrayT;
90
91 /// The complex array data type
92 typedef typename wfp::fraunhoferPropagatorArrayT<complexFieldT, cudaGPU>::arrayT complexArrayT;
93
94 /// The wavefront sensor detector image type
95 typedef _detectorT detectorT;
96
97 public:
98 /// Default constructor
100
101 /// Destructor
103
104 /** \name Standard WFS Interface
105 *
106 * @{
107 */
108
109 protected:
110 /* Standard WFS Interface: */
111 uint32_t m_wfSz{ 0 }; ///< Size of the wavefront in pixels
112
113 uint32_t m_detRows{ 0 }; ///< The number of rows of the WFS detector. After forming the image the WFS detector
114 ///< plane is re-binned to this.
115
116 uint32_t m_detCols{ 0 }; ///< The number of columns of the WFS detector. After forming the image the WFS detector
117 ///< plane is re-binned to this.
118
119 realT m_lambda{ 0 }; ///< Central wavelength, in meters
120
121 /// \todo when the filter should be set with astrospectrum, and should re-calculate the central wavelength.
122 /// \todo need to verify that the wavefront propagation is appropriately chromatic
123 std::vector<realT> m_wavelengths; ///< Vector of wavelengths in the WFS bandpass
124 std::vector<realT> _wavelengthWeights; ///< The relative weights of the wavelengths
125
126 int m_iTime{ 1 }; ///< Integration time in loop steps
127
128 int m_roTime{ 1 }; ///< Readout time in loop steps
129
130 realT m_simStep{ 0.001 }; ///< The simulation stepsize in seconds.
131
132 public:
133 /// Get the wavefront size in pixels
134 /**
135 * \returns the wavefront size in pixels
136 */
137 int wfSz();
138
139 /// Set the wavefront size in pixels.
140 /**
141 */
142 void wfSz( const uint32_t &sz /**< the new size*/ );
143
144 /// Get the detector rows in pixels
145 /**
146 * \returns m_detRows
147 */
148 uint32_t detRows();
149
150 /// Get the detector columns in pixels
151 /**
152 * \returns m_detCols
153 */
154 uint32_t detCols();
155
156 /// Set the detector columns in pixels.
157 /**
158 */
159 void detSize( const uint32_t &nrows, ///< The number of rows
160 const uint32_t &ncols ///< The number of columns
161 );
162
163 /// Get the PyWFS central wavelength
164 /**
165 * \returns the central wavelength in meters
166 */
167 realT lambda();
168
169 /// Set the PyWFS central wavelength
170 /**
171 */
172 void lambda( const realT &l /**< The central wavelength, in meters*/ );
173
174 /// Get the PyWFS integration time, in time steps
175 int iTime();
176
177 /// Set the PyWFS integration time, in time steps
178 void iTime( const uint32_t &it /**< the new integration time*/ );
179
180 /// Get the PyWFS detector readout time, in time steps
181 int roTime();
182
183 /// Set the PyWFS detector readout time, in time steps
184 void roTime( const uint32_t &rt /**< the new readout time*/ );
185
186 /// Get the simulation step-size, in seconds.
187 realT simStep();
188
189 /// Set the simulation step-size, in seconds.
190 void simStep( const realT &st /**< the new simulation step size*/ );
191
192 /// Link this WFS to an AO system simulation
193 template <typename AOSysT>
194 void linkSystem( AOSysT &AOSys /**< The AO system simulation to link to*/ );
195
196 /// Sense the wavefront aberrations
197 /** \returns true if a new wavefront measurement is ready.
198 * \returns false if still integrating.
199 */
200 bool senseWavefront( wavefrontT &pupilPlane /**< The input wavefront to be sensed*/ );
201
202 /// Sense the wavefront aberrations in calibration mode
203 /** Allows for faster calibrations.
204 */
205 bool senseWavefrontCal( wavefrontT &pupilPlane /**< The input wavefront to be sensed*/ );
206
207 public:
208 /// The WFS detector
210
211 /// The image on the detector, resized from m_wfsImage
213
214 /// @}
215
216 /** \name Pyramid Sensor Interface
217 *
218 * @{
219 */
220 protected:
221 uint32_t m_nSides{ 4 }; ///< Number of sides in the pyramid
222
223 /// The size of the pupil in wavefront pixels.
224 /** This is the maximum diameter of the pupil in wavefront pixels.
225 *
226 */
227 uint32_t m_pupilSz{ 0 };
228
229 /// The separation of the pupil images in fraction of a pupil. 0 <= m_pupilSep, default 1.
230 /** This sets the center-to-center separation of the pupils images in the focal plane wavefront.
231 * Note that the separation in detector pixels then depends on the scaling between wavefront pixels
232 * (m_wfSz) and detector pixels (m_detRows and m_detCols).
233 *
234 * This sets the size of the region in the pre-detection image that each pupil image
235 * takes up, and therefore the size of the pre-detection image.
236 * If the pupil (as defined in the input wavefront) is 60 pixels across
237 * and m_pupilSep is set to 1.06667, then there will be a 2 pixel pad around each pupil image,
238 * resulting in 4 pixels between each geometric pupil image.
239 *
240 * For a standard 4-sided pyramid, the pre-detection image will be
241 * 2*m_pupilSep*m_pupilSz across. For other n-sided pyramids, m_pupilSep still specifies the size of the pupil
242 * image region, but the total image size will be a function of the resultant pupil positions.
243 *
244 * If m_pupilSep is less than 1, this will produce the "flattened pyramid", with overlap between
245 * the pupil images. In this case, image size will also be set by pupilSz to ensure that there are enough
246 * pixels included to show all pupils.
247 */
249
250 /// The angle by which to offset the pupils, in degrees. Default is 0.
251 /** If this is 0, then a 4-sided pyramid makes a square as usual. If this is set
252 * to 45 degrees, then a 4-sided pyramid makes a diamond.
253 *
254 */
256
257 /// The size of the resulting PyWFS image in wavefront pixels.
258 /** If \ref m_imageSzAuto is true, this is determined by number of sides (\ref m_nSides), the pupil size (\ref
259 * m_pupilSz), and the pupil separation (\ref m_pupilSep). For a 4 sided pyramid this will be the larger of
260 * 2*m_pupilSep*m_pupilSz and 2*m_pupilSz.
261 *
262 * If , then this is used regardless of the optimum size.
263 */
264 uint32_t m_imageSz{ 0 };
265
266 bool m_imageSzAuto{ true }; ///< Flag to track if \ref m_imageSz should be set to 0.
267
268 realT m_wfPS{ 0 }; ///< Wavefront pixel scale, in meters/pixel
269
270 realT m_D{ 0 }; ///< Telescope diameter, in meters
271
272 uint32_t m_modSteps{ 20 }; ///< Number of modulation steps in one integration. Can be set explicitly, but will be
273 ///< calculated if \ref m_perStep is set.
274
275 realT m_perStep{ 1 }; /**< The minimum number of lamba/D per step in the modulation.
276 Smaller will result in more steps.*/
277
278 realT m_modRadius{ 3.0 }; ///< Radius of the modulation in pixels
279
280 public:
281 /// Get the number of pyramid sides
282 /**
283 * \returns the number of sides on the pyramid
284 */
285 int nSides();
286
287 /// Set the number of sides on the pyramid
288 /**
289 */
290 void nSides( const uint32_t &ns /**< The new number of sides on the pyramid*/ );
291
292 /// Get the minimum number of modulation steps
293 /**
294 * \returns m_perStep;
295 */
296 realT perStep();
297
298 /// Set the minimum number of modulation steps
299 /**
300 * \param mSt is the new number of modulation steps
301 */
302 void perStep( const realT &prStp /**< The minimum number of lamba/D per step to take*/ );
303
304 /// Get the number of modulation steps
305 /**
306 * \returns m_modSteps, which is defined by perStep.
307 */
308 int modSteps();
309
310 /// Get the radius of modulation
311 /**
312 * \returns m_modRadius;
313 */
314 realT modRadius();
315
316 /// Set the modulation radius
317 /**
318 * \param mR is the new modulation radius in lambda/D
319 */
320 void modRadius( const realT &mR /**< [in] the new value of modulation radius */ );
321
322 /// Get the wavefront pixel scale in meters per pixel
323 /**
324 * \returns the wavefront pixel scale in meters/pixel
325 */
326 realT wfPS();
327
328 /// Get the telescope diameter
329 /**
330 * \returns the telescope diameter in meters
331 */
332 realT D();
333
334 /// Set the telescope diameter
335 /**
336 * \param d is the new size in meters
337 */
338 void D( const realT &d /**< */ );
339
340 /// Get the pupil size in pixels
341 /** This is the pupil size in un-binned wavefront space
342 *
343 * \returns m_pupilSz
344 * \returns m_pupilSz
345 */
346 uint32_t pupilSz();
347
348 /// Set the pupil size in pixels.
349 /** This is the size of the pupils in un-binned wavefront space.
350 * See \ref m_pupilSz.
351 *
352 */
353 void pupilSz( const uint32_t &sz /**< the new pupil size.*/ );
354
355 /// Get the pupil separation as a fraction of pupil size
356 /// Get the pupil separation as a fraction of pupil size
357 /** This is the pupil separation in un-binned wavefront space
358 *
359 * \returns m_pupilSep
360 */
361 realT pupilSep();
362
363 /// Set the pupil separation as a fraction of pupil size
364 /// Set the pupil separation as a fraction of pupil size
365 /** This is the separation of the pupils in un-binned wavefront space.
366 * See \ref m_pupilSep.
367 *
368 */
369 void pupilSep( const realT &sz /**< the new pupil separation.*/ );
370
371 /// Get the angle offset
372 /** See \ref m_angleOffset
373 *
374 * \returns m_angleOffset
375 */
376 realT angleOffset();
377
378 /// Set the angle offset
379 /** See \ref m_angleOffset.
380 *
381 */
382 void angleOffset( const realT &ao /**< the new angle offset.*/ );
383
384 /// Get the image size in wavefront pixels
385 /** This is the size of the image in un-binned wavefront space
386 *
387 * \returns m_imageSz
388 */
389 uint32_t imageSz();
390
391 /// Set the image size in wavefront pixels
392 /** This is the size of the image in un-binned wavefront space
393 * Setting a non-zero value also sets m_imageSizeAuto to false.
394 * Setting 0 also sets m_imageSizeAuto to true.
395 */
396 void imageSz( const uint32_t &is );
397
398 /// Get the value of the image size auto flag
399 /** This controls whether image size is set automatically
400 *
401 * \returns m_imageSz
402 */
403 bool imageSzAuto();
404
405 /// Set the value of the image size auto flag
406 /** This controls whether image size is set automatically
407 *
408 */
409 void imageSzAuto( const bool &ia );
410 ///@}
411
413
414 bool m_opdMaskMade{ false };
415 complexArrayT m_opdMask;
416
417 bool m_tiltsMade{ false };
418 std::vector<complexArrayT> m_tilts;
419
420 bool m_preAllocated{ false };
421 complexFieldT m_pupilPlaneCF;
422
423 complexArrayT m_pupilPlaneCF_gpu;
424
425 // Pre-allocated working memory:
426
427 std::vector<complexArrayT> m_th_tiltedPlane; ///< Thread-local modulated wavefront
428
429 std::vector<complexArrayT> m_th_focalPlane; ///< Thread-local tip wavefront, used for FFT tilting
430
431 std::vector<realArrayT> m_th_focalImage; ///< Thread-local tip image
432
433 std::vector<complexArrayT> m_th_sensorPlane; ///< Thread-local sensor-pupil-plane wavefront
434
435 std::vector<realArrayT> m_th_sensorImage; /**< Thread-local sensor-pupil-plane
436 intensity image*/
437
438 int m_iTime_counter{ 0 };
439
440 int m_reading{ 0 };
441
442 int m_roTime_counter{ 0 };
443
444 std::vector<wavefrontT> _wavefronts;
445
446 int m_lastWavefront{ 0 };
447
448 realArrayT m_wfsTipImageAccum;
449 realArrayT m_wfsImageAccum;
450
451 /// The image formed by the WFS
453
454 protected:
455 void makeOpdMask();
456
457 // These are called upload tilt but they are also used for opdMask. Maybe uploadField?
458 template <int ccudaGPU = cudaGPU>
459 error_t uploadTilt( complexArrayT &tilt, complexFieldT &ltilt, typename std::enable_if<ccudaGPU == 0>::type * = 0 );
460
461 template <int ccudaGPU = cudaGPU>
462 error_t uploadTilt( complexArrayT &tilt, complexFieldT &ltilt, typename std::enable_if<ccudaGPU == 1>::type * = 0 );
463
464 void makeTilts();
465
466 void allocThreadMem();
467
468 void preAllocate();
469
470 /// Convert a cpu complex field to a pointer to its CPU memory
471 /** This just returns a pointer to the input array
472 *
473 */
474 template <int ccudaGPU = cudaGPU>
475 complexArrayT *uploadPupilPlaneCF( complexFieldT &cf /**< [in] the CPU complex field */,
476 typename std::enable_if<ccudaGPU == 0>::type * = 0 );
477
478 /// Convert a cpu complex field to a pointer to its GPU memory
479 /** This uploads the input array to the device and returns a cudaPtr.
480 *
481 */
482 template <int ccudaGPU = cudaGPU>
483 complexArrayT *uploadPupilPlaneCF( complexFieldT &cf /**< [in] the CPU complex field */,
484 typename std::enable_if<ccudaGPU == 1>::type * = 0 );
485
486 /// Accumulate an image with a weight applied on the CPU
487 /**
488 * aim += im*w
489 */
490 template <int ccudaGPU = cudaGPU>
491 void accumWeightedImage( realArrayT &aim, /**< [out] the image in which to accumulate the results*/
492 realArrayT &im, /**< [in] the image to weight and then add to output*/
493 realT w, /**< [in] the weight*/
494 typename std::enable_if<ccudaGPU == 0>::type * = 0 );
495
496 /// Accumulate an image with a weight applied on the GPU
497 /**
498 * aim += im*w
499 */
500 template <int ccudaGPU = cudaGPU>
501 void accumWeightedImage( realArrayT &aim, /**< [out] the image in which to accumulate the results*/
502 realArrayT &im, /**< [in] the image to weight and then add to output*/
503 realT w, /**< [in] the weight*/
504 typename std::enable_if<ccudaGPU == 1>::type * = 0 );
505
506 /// Scale the accumulated image by number of accumulations and assign it to the output image. CPU version.
507 /**
508 * im = aim / naccums
509 */
510 template <int ccudaGPU = cudaGPU>
512 realArrayT &aim,
513 uint32_t naccums,
514 typename std::enable_if<ccudaGPU == 0>::type * = 0 );
515
516 /// Scale the accumulated image by number of accumulations and assign it to the output image. GPU version.
517 /**
518 * im = aim / naccums
519 */
520 template <int ccudaGPU = cudaGPU>
522 realArrayT &aim,
523 uint32_t naccums,
524 typename std::enable_if<ccudaGPU == 1>::type * = 0 );
525
526 void doSenseWavefront();
527 void doSenseWavefront( wavefrontT & /**< */ );
528 void doSenseWavefrontNoMod( wavefrontT & /**< */ );
529
530 bool m_firstRun{ true };
531
532 protected:
533 // clang-format off
534 #ifdef MXLIB_CUDA
535 std::vector<mx::cuda::cublasHandle *> m_cublasHandle;
536 #endif
537 // clang-format on
538};
539
540template <typename realT, typename detectorT, int cudaGPU>
542{
543 iTime( m_iTime );
544
545 m_frProp.wholePixel( 0 );
546}
547
548template <typename realT, typename detectorT, int cudaGPU>
550{
551 // clang-format off
552 #ifdef MXLIB_CUDA
553 if(cudaGPU)
554 {
555 for(size_t nTh = 0; nTh < m_cublasHandle.size(); ++nTh)
556 {
557 delete m_cublasHandle[nTh];
558 }
559 }
560 #endif // clang-format on
561}
562
563template <typename realT, typename detectorT, int cudaGPU>
565{
566 return m_wfSz;
567}
568
569template <typename realT, typename detectorT, int cudaGPU>
571{
572 if( m_wfSz == sz )
573 {
574 return;
575 }
576
577 m_wfSz = sz;
578
579 m_tiltsMade = false;
580 m_opdMaskMade = false;
581 m_preAllocated = false;
582}
583
584template <typename realT, typename detectorT, int cudaGPU>
586{
587 return m_detRows;
588}
589
590template <typename realT, typename detectorT, int cudaGPU>
592{
593 return m_detCols;
594}
595
596template <typename realT, typename detectorT, int cudaGPU>
597void pyramidSensor<realT, detectorT, cudaGPU>::detSize( const uint32_t &nrows, const uint32_t &ncols )
598{
599 if( m_detRows == nrows && m_detCols == ncols )
600 {
601 return;
602 }
603
604 m_detRows = nrows;
605 m_detCols = ncols;
606
607 detector.setSize( m_detRows, m_detCols );
608
609 detectorImage.image.create("camwfs", m_detRows, m_detCols );
610 detectorImage.tipImage.create("camtip", m_wfSz, m_wfSz);
611
612 m_opdMaskMade = false; // make sure size check is run on current settings
613}
614
615template <typename realT, typename detectorT, int cudaGPU>
620
621template <typename realT, typename detectorT, int cudaGPU>
623{
624 m_lambda = l;
625
626 //---------------------------------------
627 // Check if wavelength vector is filled
628 //---------------------------------------
629 if( m_wavelengths.size() == 0 )
630 {
631 m_wavelengths.resize( 1, m_lambda );
632 _wavelengthWeights.resize( 1, 1.0 );
633 }
634}
635
636template <typename realT, typename detectorT, int cudaGPU>
638{
639 return m_iTime;
640}
641
642template <typename realT, typename detectorT, int cudaGPU>
644{
645 if( it < 1 )
646 {
647 throw mx::exception( error_t::invalidconfig, "iTime must be >= 1" );
648 }
649
650 m_iTime = it;
651
652 _wavefronts.resize( m_iTime + 2 );
653 m_lastWavefront = -1;
654
655 detector.expTime( m_simStep * m_iTime );
656}
657
658template <typename realT, typename detectorT, int cudaGPU>
660{
661 return roTime;
662}
663
664template <typename realT, typename detectorT, int cudaGPU>
666{
667 if( rt < 1 )
668 {
669 throw mx::exception( error_t::invalidconfig, "roTime must be >= 1" );
670 }
671
672 m_roTime = rt;
673}
674
675template <typename realT, typename detectorT, int cudaGPU>
680
681template <typename realT, typename detectorT, int cudaGPU>
683{
684
685 m_simStep = st;
686
687 detector.expTime( m_simStep * m_iTime );
688}
689
690template <typename realT, typename detectorT, int cudaGPU>
691template <typename AOSysT>
693{
694 AOSys.wfs.wfPS( AOSys.m_wfPS );
695 AOSys.wfs.D( AOSys.m_D );
696}
697
698template <typename realT, typename detectorT, int cudaGPU>
700{
701
702 ++m_lastWavefront;
703 if( m_lastWavefront >= _wavefronts.size() )
704 m_lastWavefront = 0;
705 _wavefronts[m_lastWavefront].amplitude = pupilPlane.amplitude;
706 _wavefronts[m_lastWavefront].phase = pupilPlane.phase;
707 _wavefronts[m_lastWavefront].iterNo = pupilPlane.iterNo;
708
709 // Always skip the first one for averaging to center of iTime.
710 if( m_firstRun )
711 {
712 m_firstRun = false;
713 return false;
714 }
715
716 ++m_iTime_counter;
717
718 bool rv = false;
719
720 if( m_reading )
721 {
722 ++m_roTime_counter;
723
724 if( m_roTime_counter >= m_roTime )
725 {
726 detector.exposeImage( detectorImage.image(), m_wfsImage.image() );
727 detectorImage.image.post();
728
729 detectorImage.tipImage() = m_wfsImage.tipImage();
730 detectorImage.tipImage.post();
731
732 detectorImage.iterNo = m_wfsImage.iterNo;
733
734 m_roTime_counter = 0;
735 m_reading = 0;
736 rv = true;
737 }
738 }
739
740 if( m_iTime_counter >= m_iTime )
741 {
742 doSenseWavefront();
743 m_iTime_counter = 0;
744
745 m_reading = 1;
746 m_roTime_counter = 0;
747 }
748
749 return rv;
750}
751
752template <typename realT, typename detectorT, int cudaGPU>
754{
755
756 BREAD_CRUMB;
757
758 doSenseWavefront( pupilPlane );
759
760 BREAD_CRUMB;
761
762 detector.exposeImage( detectorImage.image(), m_wfsImage.image() );
763 detectorImage.image.post();
764
765 BREAD_CRUMB;
766
767 detectorImage.tipImage() = m_wfsImage.tipImage();
768 detectorImage.tipImage.post();
769
770 BREAD_CRUMB;
771
772 return true;
773}
774
775/* Pyramid Specifics */
776
777template <typename realT, typename detectorT, int cudaGPU>
779{
780 return m_nSides;
781}
782
783template <typename realT, typename detectorT, int cudaGPU>
785{
786 m_nSides = ns;
787 m_opdMaskMade = false;
788}
789
790template <typename realT, typename detectorT, int cudaGPU>
795
796template <typename realT, typename detectorT, int cudaGPU>
801
802template <typename realT, typename detectorT, int cudaGPU>
804{
805 m_D = d;
806
807 if( m_pupilSz >
808 0 ) // Avoid making inf or nan so m_wfPS remains unset. note that fast-math make detecting inf and nan hard.
809 {
810 m_wfPS = m_D / m_pupilSz;
811 }
812 else
813 {
814 m_wfPS = 0;
815 }
816
817 m_tiltsMade = false;
818 m_opdMaskMade = false;
819 m_preAllocated = false;
820 m_opdMaskMade = false;
821 m_preAllocated = false;
822}
823
824template <typename realT, typename detectorT, int cudaGPU>
829
830template <typename realT, typename detectorT, int cudaGPU>
832{
833 m_perStep = prStp;
834
835 if( m_modRadius <= 0 )
836 {
837 m_modSteps = 0;
838 return;
839 }
840
841 realT radPerStep = m_perStep / m_modRadius;
842
843 // Get the minimum number of steps to meet m_perStep while having symmetry for the quadrants
844 m_modSteps = 1;
845 while( math::half_pi<realT>() / m_modSteps > radPerStep )
846 {
847 ++m_modSteps;
848 }
849
850 m_modSteps *= 4;
851
852 m_tiltsMade = false;
853}
854
855template <typename realT, typename detectorT, int cudaGPU>
857{
858 return m_modSteps;
859}
860
861template <typename realT, typename detectorT, int cudaGPU>
866
867template <typename realT, typename detectorT, int cudaGPU>
869{
870 m_modRadius = mR;
871 perStep( m_perStep ); // to calculate m_modSteps;
872
873 m_tiltsMade = false;
874}
875
876template <typename realT, typename detectorT, int cudaGPU>
878{
879 return m_pupilSz;
880}
881
882template <typename realT, typename detectorT, int cudaGPU>
884{
885 if( m_pupilSz == sz )
886 {
887 return;
888 }
889
890 m_pupilSz = sz;
891
892 if( m_pupilSz >
893 0 ) // Avoid making inf or nan so m_wfPS remains unset. note that fast-math make detecting inf and nan hard.
894 {
895 m_wfPS = m_D / m_pupilSz;
896 }
897 else
898 {
899 m_wfPS = 0;
900 }
901
902 if( m_pupilSz >
903 0 ) // Avoid making inf or nan so m_wfPS remains unset. note that fast-math make detecting inf and nan hard.
904 {
905 m_wfPS = m_D / m_pupilSz;
906 }
907 else
908 {
909 m_wfPS = 0;
910 }
911
912 m_tiltsMade = false;
913 m_opdMaskMade = false;
914 m_preAllocated = false;
915}
916
917template <typename realT, typename detectorT, int cudaGPU>
922
923template <typename realT, typename detectorT, int cudaGPU>
925{
926 if( m_pupilSep == sz )
927 {
928 return;
929 }
930
931 m_pupilSep = sz;
932
933 m_opdMaskMade = false;
934 m_preAllocated = false;
935}
936
937template <typename realT, typename detectorT, int cudaGPU>
942
943template <typename realT, typename detectorT, int cudaGPU>
945{
946 if( m_angleOffset == ao )
947 {
948 return;
949 }
950
951 m_angleOffset = ao;
952
953 m_opdMaskMade = false;
954 m_preAllocated = false;
955}
956
957template <typename realT, typename detectorT, int cudaGPU>
959{
960 if( !m_opdMaskMade )
961 {
962 makeOpdMask();
963 }
964
965 return m_imageSz;
966}
967
968template <typename realT, typename detectorT, int cudaGPU>
970{
971 if( m_imageSz == sz )
972 {
973 return;
974 }
975
976 m_imageSz = sz;
977
978 if( m_imageSz == 0 )
979 {
980 m_imageSzAuto = true;
981 }
982 else
983 {
984 m_imageSzAuto = false;
985 }
986
987 m_opdMaskMade = false;
988 m_preAllocated = false;
989}
990
991template <typename realT, typename detectorT, int cudaGPU>
993{
994 return m_imageSzAuto;
995}
996
997template <typename realT, typename detectorT, int cudaGPU>
999{
1000 m_imageSzAuto = ia;
1001
1002 m_opdMaskMade = false;
1003
1004 m_preAllocated = false;
1005}
1006
1007template <typename realT, typename detectorT, int cudaGPU>
1009{
1010
1011 if( m_wfPS == 0 )
1012 {
1014 "wavefront platescale (m_wfPS) is 0. Must set pupilSz and D first." );
1015 }
1016
1017 if( !std::isfinite( m_wfPS ) || !std::isnormal( m_wfPS ) )
1018 {
1020 "wavefront platescale (m_wfPS) is infinite. Must set pupilSz and D first." );
1021 }
1022
1023 std::cerr << m_wfPS << " " << m_D << "\n";
1024
1025 if( m_D == 0 )
1026 {
1027 throw mx::exception( error_t::invalidconfig, "pupil diameter is 0. Must set D > 0 first." );
1028 }
1029
1030 // Setup the Fraunhoffer Propagator
1031 m_frProp.setWavefrontSizePixels( m_wfSz );
1032
1033 m_opdMask.resize( m_wfSz, m_wfSz );
1034
1035 complexFieldT opdMask;
1036 opdMask.resize( m_wfSz, m_wfSz );
1037
1038 complexFieldT opdMaskQ;
1039 opdMaskQ.resize( m_wfSz, m_wfSz );
1040
1042
1043 mask.resize( m_opdMask.rows(), m_opdMask.cols() );
1044 realT dang = mx::math::two_pi<realT>() / m_nSides;
1045
1046 realT minx = 0;
1047 realT maxx = 0;
1048 realT miny = 0;
1049 realT maxy = 0;
1050
1051 realT pupilRad = m_pupilSep * m_pupilSz / ( 2 * sin( dang / 2.0 ) );
1052
1053 for( int n = 0; n < m_nSides; ++n )
1054 {
1055 realT ang = m_angleOffset * math::degreesT<realT>::radians + 0.5 * dang + n * dang;
1056
1057 realT dx = pupilRad * cos( ang );
1058
1059 if( dx < minx )
1060 minx = dx;
1061 if( dx > maxx )
1062 maxx = dx;
1063
1064 realT dy = pupilRad * sin( ang );
1065
1066 if( dy < miny )
1067 miny = dy;
1068 if( dy > maxy )
1069 maxy = dy;
1070
1071 opdMaskQ.setConstant( std::complex<realT>( 0, 1 ) );
1072 wfp::tiltWavefront( opdMaskQ, dx, dy );
1073 mask.setZero();
1074 improc::maskWedge( mask,
1075 0.5 * ( mask.rows() - 1 ),
1076 0.5 * ( mask.cols() - 1 ),
1077 math::rtod( ang ),
1078 0.5 * math::rtod( dang ),
1079 1 );
1080 wfp::extractMaskedPixels( opdMask, opdMaskQ, mask );
1081 }
1082
1083 uploadTilt( m_opdMask, opdMask );
1084
1085 int xsz =
1086 2 * std::max( { fabs( maxx ), fabs( minx ) } ) + 2 * std::max( { ( pupilRad / 2 ), ( (realT)m_pupilSz / 2 ) } );
1087
1088 int ysz =
1089 2 * std::max( { fabs( maxy ), fabs( miny ) } ) + 2 * std::max( { ( pupilRad / 2 ), ( (realT)m_pupilSz / 2 ) } );
1090
1091 if( m_imageSzAuto )
1092 {
1093 m_imageSz = std::max( xsz, ysz );
1094
1095 if( m_pupilSep > 1 )
1096 {
1097 m_imageSz += ( m_pupilSep - 1.0 ) * m_pupilSz;
1098 }
1099 }
1100
1101 if( m_imageSz > m_wfSz )
1102 {
1103 std::string msg = "image size (m_imageSz = " + std::to_string( m_imageSz ) + ") ";
1104 msg += "> wavefront size (m_wfSz = " + std::to_string( m_wfSz ) + "). ";
1105 msg += "Decrease number of sides (m_nSides = " + std::to_string( m_nSides ) + ") or increase wavefront size. ";
1107 }
1108
1109 m_wfsImage.image.create("camwfs_raw", m_imageSz, m_imageSz );
1110 m_wfsImage.tipImage.create( "camtip_raw", m_wfSz, m_wfSz );
1111
1112 if( m_detRows == 0 || m_detCols == 0 )
1113 {
1114 detSize( m_imageSz, m_imageSz );
1115 }
1116
1117 else if( m_detRows > m_imageSz || m_detCols > m_imageSz )
1118 {
1119 std::string msg = "image size (m_imageSz = " + std::to_string( m_imageSz ) + ") ";
1120 msg += "< detector size size (m_detRows = " + std::to_string( m_detRows );
1121 msg += " m_detCols = " + std::to_string( m_detCols ) + "). ";
1123 }
1124
1125 m_opdMaskMade = true;
1126}
1127
1128template <typename realT, typename detectorT, int cudaGPU>
1129template <int ccudaGPU>
1130error_t pyramidSensor<realT, detectorT, cudaGPU>::uploadTilt( complexArrayT &tilt,
1131 complexFieldT &ltilt,
1132 typename std::enable_if<ccudaGPU == 0>::type * )
1133{
1134 tilt = ltilt;
1135 return error_t::noerror;
1136}
1137
1138template <typename realT, typename detectorT, int cudaGPU>
1139template <int ccudaGPU>
1140error_t pyramidSensor<realT, detectorT, cudaGPU>::uploadTilt( complexArrayT &tilt,
1141 complexFieldT &ltilt,
1142 typename std::enable_if<ccudaGPU == 1>::type * )
1143{
1144 return tilt.upload( ltilt.data(), ltilt.size() );
1145}
1146
1147template <typename realT, typename detectorT, int cudaGPU>
1148void pyramidSensor<realT, detectorT, cudaGPU>::makeTilts()
1149{
1150 constexpr realT pi = math::pi<realT>();
1151
1152 if( m_modSteps == 0 )
1153 {
1154 throw mx::exception( error_t::invalidconfig, "number of modulation steps (m_modSteps) has not been set." );
1155 }
1156
1157 if( m_wfPS == 0 )
1158 {
1160 "wavefront platescale (m_wfPS) is 0. "
1161 "Must set pupilSz and D first." );
1162 }
1163
1164 if( !std::isfinite( m_wfPS ) )
1165 {
1167 "wavefront platescale (m_wfPS) is infinite. "
1168 "Must set pupilSz and D first." );
1169 }
1170
1171 if( m_D == 0 )
1172 {
1173 throw mx::exception( error_t::invalidconfig, "pupil diameter is 0. Must set D > 0 first." );
1174 }
1175
1176 realT dang = 2 * pi / ( m_modSteps );
1177 realT dx, dy;
1178
1179 m_tilts.resize( m_modSteps );
1180
1181 std::cout << "WF Size: " << m_wfSz << "\n";
1182 std::cout << "WF PS: " << m_wfPS << "\n";
1183 std::cout << "Lambda: " << m_lambda << "\n";
1184 std::cout << "Pyr. PS: " << wfp::fftPlateScale<realT>( m_wfSz, m_wfPS, m_lambda ) * 206265.*1000 << " (mas/pix)\n";
1185 std::cout << "Mod. steps: " << m_modSteps << "\n";
1186 std::cout << "Mod rad: " << m_modRadius * ( m_lambda / m_D ) / wfp::fftPlateScale<realT>( m_wfSz, m_wfPS, m_lambda )
1187 << " (pixels)\n";
1188
1189 for( int i = 0; i < m_modSteps; ++i )
1190 {
1191 dx = m_modRadius * ( m_lambda / m_D ) / wfp::fftPlateScale<realT>( m_wfSz, m_wfPS, m_lambda ) *
1192 cos( 0.0 * dang + dang * i );
1193 dy = m_modRadius * ( m_lambda / m_D ) / wfp::fftPlateScale<realT>( m_wfSz, m_wfPS, m_lambda ) *
1194 sin( 0.0 * dang + dang * i );
1195
1196 complexFieldT tilt;
1197 tilt.resize( m_wfSz, m_wfSz );
1198 tilt.setConstant( std::complex<realT>( 0, 1 ) );
1199
1200 wfp::tiltWavefront( tilt, dx, dy );
1201
1202 uploadTilt( m_tilts[i], tilt );
1203 }
1204
1205 m_tiltsMade = true;
1206}
1207
1208template <typename realT, typename detectorT, int cudaGPU>
1209void pyramidSensor<realT, detectorT, cudaGPU>::allocThreadMem()
1210{
1211 if( !m_opdMaskMade )
1212 {
1213 makeOpdMask(); // Needed for m_imageSz
1214 }
1215
1216 m_pupilPlaneCF.resize( m_wfSz, m_wfSz );
1217
1218 if( cudaGPU )
1219 {
1220 m_pupilPlaneCF_gpu.resize( m_wfSz, m_wfSz );
1221 }
1222
1223 int maxTh = omp_get_max_threads();
1224 m_th_tiltedPlane.resize( maxTh );
1225
1226 m_th_focalPlane.resize( maxTh );
1227
1228 m_th_focalImage.resize( maxTh );
1229
1230 m_th_sensorPlane.resize( maxTh );
1231
1232 m_th_sensorImage.resize( maxTh );
1233
1234 // clang-format off
1235 #ifdef MXLIB_CUDA
1236 if(cudaGPU)
1237 {
1238 m_cublasHandle.resize(maxTh);
1239 }
1240 #endif // clang-format on
1241
1242 for( int nTh = 0; nTh < maxTh; ++nTh )
1243 {
1244 m_th_tiltedPlane[nTh].resize( m_wfSz, m_wfSz );
1245
1246 m_th_focalPlane[nTh].resize( m_wfSz, m_wfSz );
1247
1248 m_th_focalImage[nTh].resize( m_wfSz, m_wfSz );
1249
1250 m_th_sensorPlane[nTh].resize( m_wfSz, m_wfSz );
1251
1252 m_th_sensorImage[nTh].resize( m_imageSz, m_imageSz );
1253
1254 // clang-format off
1255 #ifdef MXLIB_CUDA
1256 if(cudaGPU)
1257 {
1258 m_cublasHandle[nTh] = new mx::cuda::cublasHandle(true);
1259 }
1260 #endif // clang-format on
1261 }
1262
1263 m_wfsTipImageAccum.resize( m_wfSz, m_wfSz );
1264 m_wfsImageAccum.resize( m_imageSz, m_imageSz );
1265
1266 m_preAllocated = true;
1267}
1268
1269template <typename realT, typename detectorT, int cudaGPU>
1270void pyramidSensor<realT, detectorT, cudaGPU>::preAllocate()
1271{
1272 if( !m_tiltsMade )
1273 {
1274 makeTilts();
1275 }
1276
1277 if( !m_opdMaskMade )
1278 {
1279 makeOpdMask();
1280 }
1281
1282 if( !m_preAllocated )
1283 {
1284 allocThreadMem();
1285 }
1286}
1287
1288template <typename realT, typename detectorT, int cudaGPU>
1289void pyramidSensor<realT, detectorT, cudaGPU>::doSenseWavefront()
1290{
1291 BREAD_CRUMB;
1292
1293 wavefrontT pupilPlane;
1294
1295 /* Here make average wavefront for now */
1296 int _firstWavefront = m_lastWavefront - m_iTime;
1297 if( _firstWavefront < 0 )
1298 _firstWavefront += _wavefronts.size();
1299
1300 pupilPlane.amplitude = _wavefronts[_firstWavefront].amplitude;
1301 pupilPlane.phase = _wavefronts[_firstWavefront].phase;
1302
1303 realT avgIt = _wavefronts[_firstWavefront].iterNo;
1304
1305 BREAD_CRUMB;
1306
1307 for( int i = 0; i < m_iTime; ++i )
1308 {
1309 ++_firstWavefront;
1310 if( (size_t)_firstWavefront >= _wavefronts.size() )
1311 _firstWavefront = 0;
1312
1313 pupilPlane.amplitude += _wavefronts[_firstWavefront].amplitude;
1314 pupilPlane.phase += _wavefronts[_firstWavefront].phase;
1315 avgIt += _wavefronts[_firstWavefront].iterNo;
1316 }
1317
1318 BREAD_CRUMB;
1319
1320 pupilPlane.amplitude /= ( m_iTime + 1 );
1321 pupilPlane.phase /= ( m_iTime + 1 );
1322
1323 avgIt /= ( m_iTime + 1.0 );
1324
1325 /*=====================================*/
1326 doSenseWavefront( pupilPlane );
1327
1328 m_wfsImage.iterNo = avgIt;
1329}
1330
1331template <typename complexT>
1332void elWiseProduct_cpu( complexT *out, complexT *in1, complexT *in2, size_t nelem )
1333{
1334 for( int ii = 0; ii < nelem; ++ii )
1335 {
1336 out[ii] = in1[ii] * in2[ii];
1337 }
1338}
1339
1340template <typename complexT>
1341void elWiseProduct_gpu( complexT *out, complexT *in1, complexT *in2, size_t nelem )
1342{
1343 BREAD_CRUMB;
1344 cuda::elementwiseXxY( out, in1, in2, nelem );
1345 BREAD_CRUMB;
1346
1347 cudaError_t ce = cudaDeviceSynchronize();
1348
1349 if( ce != cudaSuccess )
1350 {
1351 std::cerr << "cudaError " << cudaGetErrorName( ce ) << ": " << cudaGetErrorString( ce ) << '\n';
1352 return;
1353 }
1354 BREAD_CRUMB;
1355}
1356
1357template <typename complexT, int cudaGPU>
1358void elWiseProduct( complexT *out, complexT *in1, complexT *in2, size_t nelem );
1359
1360template <>
1361void elWiseProduct<std::complex<float>, 0>( std::complex<float> *out,
1362 std::complex<float> *in1,
1363 std::complex<float> *in2,
1364 size_t nelem )
1365{
1366 elWiseProduct_cpu( out, in1, in2, nelem );
1367}
1368
1369template <>
1370void elWiseProduct<std::complex<double>, 0>( std::complex<double> *out,
1371 std::complex<double> *in1,
1372 std::complex<double> *in2,
1373 size_t nelem )
1374{
1375 elWiseProduct_cpu( out, in1, in2, nelem );
1376}
1377
1378template <>
1379void elWiseProduct<std::complex<float>, 1>( std::complex<float> *out,
1380 std::complex<float> *in1,
1381 std::complex<float> *in2,
1382 size_t nelem )
1383{
1384 BREAD_CRUMB;
1385 elWiseProduct_gpu( reinterpret_cast<cuComplex *>( out ),
1386 reinterpret_cast<cuComplex *>( in1 ),
1387 reinterpret_cast<cuComplex *>( in2 ),
1388 nelem );
1389
1390 cudaError_t ce = cudaDeviceSynchronize();
1391
1392 if( ce != cudaSuccess )
1393 {
1394 std::cerr << "cudaError " << cudaGetErrorName( ce ) << ": " << cudaGetErrorString( ce ) << '\n';
1395 return;
1396 }
1397 BREAD_CRUMB;
1398}
1399
1400template <>
1401void elWiseProduct<std::complex<double>, 1>( std::complex<double> *out,
1402 std::complex<double> *in1,
1403 std::complex<double> *in2,
1404 size_t nelem )
1405{
1406 elWiseProduct_gpu( reinterpret_cast<cuDoubleComplex *>( out ),
1407 reinterpret_cast<cuDoubleComplex *>( in1 ),
1408 reinterpret_cast<cuDoubleComplex *>( in2 ),
1409 nelem );
1410}
1411
1412template <typename realT, typename detectorT, int cudaGPU>
1413template <int ccudaGPU>
1416 typename std::enable_if<ccudaGPU == 0>::type * )
1417{
1418 return &cf;
1419}
1420
1421template <typename realT, typename detectorT, int cudaGPU>
1422template <int ccudaGPU>
1425 typename std::enable_if<ccudaGPU == 1>::type * )
1426{
1427 m_pupilPlaneCF_gpu.upload( cf.data() );
1428 return &m_pupilPlaneCF_gpu;
1429}
1430
1431template <typename realT, typename detectorT, int cudaGPU>
1432template <int ccudaGPU>
1434 realArrayT &im,
1435 realT w,
1436 typename std::enable_if<ccudaGPU == 0>::type * )
1437{
1438 aim += im * w;
1439}
1440
1441template <typename realT, typename detectorT, int cudaGPU>
1442template <int ccudaGPU>
1444 realArrayT &im,
1445 realT w,
1446 typename std::enable_if<ccudaGPU == 1>::type * )
1447{
1448
1449 // clang-format off
1450 #ifdef MXLIB_CUDA
1451
1452 mx::cuda::cublasTaxpy<realT>( *m_cublasHandle[omp_get_thread_num()], aim.size(), &w, im.data(), 1, aim.data(), 1 );
1453
1454 #endif
1455 // clang-format on
1456}
1457
1458template <typename realT, typename detectorT, int cudaGPU>
1459template <int ccudaGPU>
1461 realArrayT &aim,
1462 uint32_t naccums,
1463 typename std::enable_if<ccudaGPU == 0>::type * )
1464{
1465 im = aim / naccums;
1466}
1467
1468template <typename realT, typename detectorT, int cudaGPU>
1469template <int ccudaGPU>
1471 realArrayT &aim,
1472 uint32_t naccums,
1473 typename std::enable_if<ccudaGPU == 1>::type * )
1474{
1475 aim.download( im.data() );
1476 im /= naccums;
1477}
1478
1479template <typename realT, typename detectorT, int cudaGPU>
1481{
1482 if( m_modRadius == 0 )
1483 {
1484 return doSenseWavefrontNoMod( pupilPlane );
1485 }
1486
1487 if( !m_preAllocated )
1488 {
1489 preAllocate();
1490 }
1491
1492 BREAD_CRUMB;
1493
1494 BREAD_CRUMB;
1495
1496 m_wfsTipImageAccum.setZero();
1497 m_wfsImageAccum.setZero();
1498
1499 BREAD_CRUMB;
1500
1501 for( size_t l = 0; l < m_wavelengths.size(); ++l )
1502 {
1503 pupilPlane.lambda = m_lambda;
1504 pupilPlane.getWavefront( m_pupilPlaneCF, m_wavelengths[l], m_wfSz );
1505
1506 complexArrayT *pupilPlaneCF = uploadPupilPlaneCF( m_pupilPlaneCF );
1507
1508 // clang-format off
1509 #pragma omp parallel// clang-format on
1510 {
1511 int nTh = omp_get_thread_num();
1512
1513 // CUDA: these are cudaPtrs
1514 m_th_sensorImage[nTh].setZero();
1515 m_th_focalImage[nTh].setZero();
1516
1517 BREAD_CRUMB;
1518
1519 complexT *ppm_Data;
1520 complexT *tpm_Data;
1521 complexT *fpm_Data;
1522 complexT *opdm_Data;
1523 complexT *tim_Data;
1524
1525 ppm_Data = reinterpret_cast<complexT *>( pupilPlaneCF->data() );
1526 tpm_Data = reinterpret_cast<complexT *>( m_th_tiltedPlane[nTh].data() );
1527 fpm_Data = reinterpret_cast<complexT *>( m_th_focalPlane[nTh].data() );
1528 opdm_Data = reinterpret_cast<complexT *>( m_opdMask.data() );
1529
1530 int nelem = m_wfSz * m_wfSz;
1531
1532 // clang-format off
1533 #pragma omp for // clang-format on
1534 for( int i = 0; i < m_modSteps; ++i )
1535 {
1536
1537 tim_Data = reinterpret_cast<complexT*>(m_tilts[i].data());
1538
1539 BREAD_CRUMB;
1540
1541 //---------------------------------------------
1542 // Apply the modulating tip
1543 //---------------------------------------------
1544 elWiseProduct<complexT, cudaGPU>( tpm_Data, ppm_Data, tim_Data, nelem );
1545
1546 BREAD_CRUMB;
1547
1548 //---------------------------------------------
1549 // Propagate to Pyramid tip
1550 //---------------------------------------------
1551 m_frProp.propagatePupilToFocal( m_th_focalPlane[nTh], m_th_tiltedPlane[nTh], true );
1552
1553 BREAD_CRUMB;
1554
1555 //---------------------------------------------
1556 // Extract the tip image.
1557 //---------------------------------------------
1558 wfp::extractIntensityImageAccum<realArrayT, complexArrayT, cudaGPU>( m_th_focalImage[nTh],
1559 0,
1560 m_wfSz,
1561 0,
1562 m_wfSz,
1563 m_th_focalPlane[nTh],
1564 0,
1565 0 );
1566
1567 BREAD_CRUMB;
1568
1569 //---------------------------------------------
1570 // Now apply the pyramid OPD
1571 //---------------------------------------------
1572 elWiseProduct<complexT, cudaGPU>( fpm_Data, fpm_Data, opdm_Data, nelem );
1573
1574 BREAD_CRUMB;
1575
1576 //---------------------------------------------
1577 // Propagate to sensor plane
1578 //---------------------------------------------
1579 m_frProp.propagateFocalToPupil( m_th_sensorPlane[nTh], m_th_focalPlane[nTh], true );
1580
1581 BREAD_CRUMB;
1582
1583 //---------------------------------------------
1584 // Extract the image.
1585 //---------------------------------------------
1586
1587 wfp::extractIntensityImageAccum<realArrayT, complexArrayT, cudaGPU>( m_th_sensorImage[nTh],
1588 0,
1589 m_imageSz,
1590 0,
1591 m_imageSz,
1592 m_th_sensorPlane[nTh],
1593 0.5 * m_wfSz - m_imageSz / 2,
1594 0.5 * m_wfSz - m_imageSz / 2 );
1595
1596
1597
1598 } // for
1599
1600 BREAD_CRUMB;
1601
1602 // clang-format off
1603 #pragma omp critical // clang-format on
1604 {
1605 accumWeightedImage( m_wfsTipImageAccum, m_th_focalImage[nTh], _wavelengthWeights[l] );
1606
1607 accumWeightedImage( m_wfsImageAccum, m_th_sensorImage[nTh], _wavelengthWeights[l] );
1608 }
1609
1610 } // #pragma omp parallel
1611
1612 } // l for wavelength
1613
1614 BREAD_CRUMB;
1615
1616 downloadAccumImage( m_wfsImage.tipImage(), m_wfsTipImageAccum, m_modSteps );
1617 m_wfsImage.tipImage.post();
1618
1619 downloadAccumImage( m_wfsImage.image(), m_wfsImageAccum, m_modSteps );
1620 m_wfsImage.image.post();
1621
1622 BREAD_CRUMB;
1623
1624}
1625
1626template <typename T>
1627void memCopy_cpu( T *out, T *in, size_t nelem )
1628{
1629 memcpy( out, in, nelem * sizeof( T ) );
1630}
1631
1632template <typename T>
1633void memCopy_gpu( T *out, T *in, size_t nelem )
1634{
1635 cudaMemcpy( out, in, nelem * sizeof( T ), cudaMemcpyDeviceToDevice );
1636}
1637
1638template <typename T, int cudaGPU>
1639void memCopy( T *out, T *in, size_t nelem );
1640
1641template <>
1642void memCopy<std::complex<float>, 0>( std::complex<float> *out, std::complex<float> *in, size_t nelem )
1643{
1644 memCopy_cpu( out, in, nelem );
1645}
1646
1647template <>
1648void memCopy<std::complex<double>, 0>( std::complex<double> *out, std::complex<double> *in, size_t nelem )
1649{
1650 memCopy_cpu( out, in, nelem );
1651}
1652
1653template <>
1654void memCopy<std::complex<float>, 1>( std::complex<float> *out, std::complex<float> *in, size_t nelem )
1655{
1656 memCopy_gpu( out, in, nelem );
1657}
1658
1659template <>
1660void memCopy<std::complex<double>, 1>( std::complex<double> *out, std::complex<double> *in, size_t nelem )
1661{
1662 memCopy_gpu( out, in, nelem );
1663}
1664
1665template <typename realT, typename detectorT, int cudaGPU>
1666void pyramidSensor<realT, detectorT, cudaGPU>::doSenseWavefrontNoMod( wavefrontT &pupilPlane )
1667{
1668#ifndef MXLIB_CUDA
1669 BREAD_CRUMB;
1670
1671 if( !m_opdMaskMade )
1672 {
1673 makeOpdMask();
1674 }
1675
1676 m_wfsImage.image.create("camwfs", m_imageSz, m_imageSz );
1677 m_wfsImage.image().setZero();
1678 m_wfsImage.image.post();
1679
1680 m_wfsImage.tipImage.create("camtip", m_wfSz, m_wfSz );
1681 m_wfsImage.tipImage().setZero();
1682 m_wfsImage.tipImage.post();
1683
1684 pupilPlane.getWavefront( m_pupilPlaneCF, m_wfSz );
1685
1686 complexArrayT *pupilPlaneCF = uploadPupilPlaneCF( m_pupilPlaneCF );
1687
1688 complexFieldT tiltedPlane;
1689 complexFieldT focalPlane;
1690 complexFieldT sensorPlane;
1691
1692 tiltedPlane.resize( m_wfSz, m_wfSz );
1693 focalPlane.resize( m_wfSz, m_wfSz );
1694 sensorPlane.resize( m_wfSz, m_wfSz );
1695
1696 int nelem = m_wfSz * m_wfSz;
1697
1698 complexT *tpm_Data = reinterpret_cast<complexT *>( tiltedPlane.data() );
1699 complexT *ppm_Data = reinterpret_cast<complexT *>( pupilPlaneCF->data() );
1700 complexT *opdm_Data = reinterpret_cast<complexT *>( m_opdMask.data() );
1701 complexT *fpm_Data = reinterpret_cast<complexT *>( focalPlane.data() );
1702
1703 BREAD_CRUMB;
1704
1705 //---------------------------------------------
1706 // Apply NO modulator tilt
1707 //---------------------------------------------
1708 memCopy<complexT, cudaGPU>( tpm_Data, ppm_Data, nelem );
1709 /*for( int ii = 0; ii < nelem; ++ii )
1710 {
1711 tpm_Data[ii] = ppm_Data[ii];
1712 }*/
1713
1714 BREAD_CRUMB;
1715
1716 //---------------------------------------------
1717 // Propagate to Pyramid tip
1718 //---------------------------------------------
1719 m_frProp.propagatePupilToFocal( focalPlane, tiltedPlane, true );
1720
1721 BREAD_CRUMB;
1722
1723 //---------------------------------------------
1724 // Extract the tip image.
1725 //---------------------------------------------
1726 wfp::extractIntensityImageAccum<realArrayT, complexArrayT, cudaGPU>( m_wfsImage.tipImage(),
1727 0,
1728 m_wfSz,
1729 0,
1730 m_wfSz,
1731 focalPlane,
1732 0,
1733 0 );
1734
1735 BREAD_CRUMB;
1736
1737 //---------------------------------------------
1738 // Now apply the pyramid OPD
1739 //---------------------------------------------
1740 elWiseProduct<complexT, cudaGPU>( fpm_Data, fpm_Data, opdm_Data, nelem );
1741 /*for( int ii = 0; ii < nelem; ++ii )
1742 {
1743 fpm_Data[ii] = fpm_Data[ii] * opdm_Data[ii];
1744 }*/
1745
1746 BREAD_CRUMB;
1747
1748 //---------------------------------------------
1749 // Propagate to sensor plane
1750 //---------------------------------------------
1751 m_frProp.propagateFocalToPupil( sensorPlane, focalPlane, true );
1752
1753 BREAD_CRUMB;
1754
1755 //---------------------------------------------
1756 // Extract the image.
1757 //---------------------------------------------
1758 wfp::extractIntensityImageAccum<realArrayT, complexArrayT, cudaGPU>( m_wfsImage.image(),
1759 0,
1760 m_imageSz,
1761 0,
1762 m_imageSz,
1763 sensorPlane,
1764 0.5 * m_wfSz - m_imageSz / 2,
1765 0.5 * m_wfSz - m_imageSz / 2 );
1766
1767 BREAD_CRUMB;
1768
1769#endif
1770}
1771
1772} // namespace sim
1773} // namespace AO
1774} // namespace mx
1775
1776#endif // mx_AO_sim_pyramidSensor_hpp
A Pyramid Sensor Simulation.
std::vector< complexArrayT > m_th_sensorPlane
Thread-local sensor-pupil-plane wavefront.
realT perStep()
Get the minimum number of modulation steps.
uint32_t m_wfSz
Size of the wavefront in pixels.
std::vector< realT > m_wavelengths
Vector of wavelengths in the WFS bandpass.
std::vector< realArrayT > m_th_focalImage
Thread-local tip image.
realT lambda()
Get the PyWFS central wavelength.
pyramidSensor()
Default constructor.
wfp::fraunhoferPropagatorArrayT< complexFieldT, cudaGPU >::arrayT complexArrayT
The complex array data type.
realT angleOffset()
Get the angle offset.
int wfSz()
Get the wavefront size in pixels.
void detSize(const uint32_t &nrows, const uint32_t &ncols)
Set the detector columns in pixels.
uint32_t m_nSides
Number of sides in the pyramid.
realT simStep()
Get the simulation step-size, in seconds.
wavefront< realT > wavefrontT
The wavefront data type.
int iTime()
Get the PyWFS integration time, in time steps.
wfsImageT< realT > detectorImage
The image on the detector, resized from m_wfsImage.
Eigen::Array< floatT, Eigen::Dynamic, Eigen::Dynamic > wfsImageT
The wavefront sensor detector image type.
improc::eigenImage< std::complex< realT > > complexFieldT
The wavefront complex field type.
void accumWeightedImage(realArrayT &aim, realArrayT &im, realT w, typename std::enable_if< ccudaGPU==0 >::type *=0)
Accumulate an image with a weight applied on the CPU.
bool m_imageSzAuto
Flag to track if m_imageSz should be set to 0.
void linkSystem(AOSysT &AOSys)
Link this WFS to an AO system simulation.
realT m_D
Telescope diameter, in meters.
std::vector< complexArrayT > m_th_focalPlane
Thread-local tip wavefront, used for FFT tilting.
realT wfPS()
Get the wavefront pixel scale in meters per pixel.
uint32_t detCols()
Get the detector columns in pixels.
int modSteps()
Get the number of modulation steps.
realT m_pupilSep
The separation of the pupil images in fraction of a pupil. 0 <= m_pupilSep, default 1.
realT m_angleOffset
The angle by which to offset the pupils, in degrees. Default is 0.
_detectorT detectorT
The wavefront sensor detector image type.
void downloadAccumImage(improc::eigenMap< realT > &im, realArrayT &aim, uint32_t naccums, typename std::enable_if< ccudaGPU==0 >::type *=0)
Scale the accumulated image by number of accumulations and assign it to the output image....
wfsImageT< realT > m_wfsImage
The image formed by the WFS.
int roTime()
Get the PyWFS detector readout time, in time steps.
_realT realT
The real floating point type used for calculations.
uint32_t imageSz()
Get the image size in wavefront pixels.
detectorT detector
The WFS detector.
std::vector< realT > _wavelengthWeights
The relative weights of the wavelengths.
int m_iTime
Integration time in loop steps.
wfp::fraunhoferPropagatorArrayT< realImageT, cudaGPU >::arrayT realArrayT
The real array data type.
std::complex< realT > complexT
The complex floating point type used for calculations.
uint32_t detRows()
Get the detector rows in pixels.
complexArrayT * uploadPupilPlaneCF(complexFieldT &cf, typename std::enable_if< ccudaGPU==0 >::type *=0)
Convert a cpu complex field to a pointer to its CPU memory.
std::vector< complexArrayT > m_th_tiltedPlane
Thread-local modulated wavefront.
realT modRadius()
Get the radius of modulation.
realT m_lambda
Central wavelength, in meters.
uint32_t m_pupilSz
The size of the pupil in wavefront pixels.
bool senseWavefrontCal(wavefrontT &pupilPlane)
Sense the wavefront aberrations in calibration mode.
int m_roTime
Readout time in loop steps.
bool senseWavefront(wavefrontT &pupilPlane)
Sense the wavefront aberrations.
uint32_t pupilSz()
Get the pupil size in pixels.
int nSides()
Get the number of pyramid sides.
bool imageSzAuto()
Get the value of the image size auto flag.
realT D()
Get the telescope diameter.
std::vector< realArrayT > m_th_sensorImage
uint32_t m_imageSz
The size of the resulting PyWFS image in wavefront pixels.
realT m_wfPS
Wavefront pixel scale, in meters/pixel.
realT m_simStep
The simulation stepsize in seconds.
realT m_modRadius
Radius of the modulation in pixels.
Augments an exception with the source file and line.
Definition exception.hpp:42
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.
Eigen::Map< Eigen::Array< scalarT, -1, -1 > > eigenMap
Definition of the eigenMap type, which is an alias for Eigen::Map<Array>.
error_t
The mxlib error codes.
Definition error_t.hpp:26
@ noerror
No error has occurred.
@ invalidconfig
A config setting was invalid.
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 mxlib.hpp:37
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