8#ifndef mx_AO_sim_pyramidSensor_hpp
9#define mx_AO_sim_pyramidSensor_hpp
13#include "../../mxException.hpp"
15#include "../../wfp/imagingUtils.hpp"
16#include "../../wfp/fraunhoferPropagator.hpp"
17#include "../../sys/timeUtils.hpp"
19#include "../../improc/eigenImage.hpp"
20#include "../../improc/imageMasks.hpp"
22#include "../../math/constants.hpp"
23#include "../../math/geo.hpp"
25#include "wavefront.hpp"
28#define BREAD_CRUMB std::cout << "DEBUG: " << __FILE__ << " " << __LINE__ << "\n";
40template <
typename _realT>
48 typedef Eigen::Array<realT, Eigen::Dynamic, Eigen::Dynamic> imageT;
62template <
typename _realT,
typename _detectorT>
76 typedef wfp::imagingArray<std::complex<realT>, wfp::fftwAllocator<std::complex<realT>>, 0>
complexFieldT;
123 void wfSz(
const uint32_t & sz );
140 void detSize(
const uint32_t &nrows,
141 const uint32_t &ncols
153 void lambda(
const realT &l );
159 void iTime(
const uint32_t &it );
165 void roTime(
const uint32_t &rt );
171 void simStep(
const realT &st );
174 template <
typename AOSysT>
271 void nSides(
const uint32_t & ns );
283 void perStep(
const realT &prStp );
319 void D(
const realT &d );
334 void pupilSz(
const uint32_t &sz );
377 void imageSz(
const uint32_t &is );
395 bool m_opdMaskMade{
false };
398 bool m_tiltsMade{
false };
399 std::vector<complexFieldT> m_tilts;
401 bool m_preAllocated{
false };
414 std::vector<typename wfsImageT<realT>::imageT>
417 int m_iTime_counter{ 0 };
421 int m_roTime_counter{ 0 };
423 std::vector<wavefrontT> _wavefronts;
425 int m_lastWavefront{ 0 };
438 void allocThreadMem();
442 void doSenseWavefront();
446 bool m_firstRun{
true };
449template <
typename realT,
typename detectorT>
454 m_frProp.wholePixel( 0 );
457template <
typename realT,
typename detectorT>
463template <
typename realT,
typename detectorT>
474 m_opdMaskMade =
false;
475 m_preAllocated =
false;
478template <
typename realT,
typename detectorT>
484template <
typename realT,
typename detectorT>
490template <
typename realT,
typename detectorT>
493 if( m_detRows == nrows && m_detCols == ncols )
499 detector.setSize(m_detRows, m_detCols);
500 detectorImage.image.resize(m_detRows, m_detCols);
502 m_opdMaskMade =
false;
504 m_opdMaskMade =
false;
507template <
typename realT,
typename detectorT>
513template <
typename realT,
typename detectorT>
521 if( m_wavelengths.size() == 0 )
523 m_wavelengths.resize( 1, m_lambda );
524 _wavelengthWeights.resize( 1, 1.0 );
528template <
typename realT,
typename detectorT>
534template <
typename realT,
typename detectorT>
544 _wavefronts.resize( m_iTime + 2 );
545 m_lastWavefront = -1;
547 detector.expTime( m_simStep * m_iTime );
550template <
typename realT,
typename detectorT>
556template <
typename realT,
typename detectorT>
567template <
typename realT,
typename detectorT>
573template <
typename realT,
typename detectorT>
579 detector.expTime( m_simStep * m_iTime );
582template <
typename realT,
typename detectorT>
583template <
typename AOSysT>
586 AOSys.wfs.wfPS( AOSys.m_wfPS );
587 AOSys.wfs.D( AOSys.m_D );
590template <
typename realT,
typename detectorT>
595 if( m_lastWavefront >= _wavefronts.size() )
597 _wavefronts[m_lastWavefront].amplitude = pupilPlane.
amplitude;
598 _wavefronts[m_lastWavefront].phase = pupilPlane.
phase;
599 _wavefronts[m_lastWavefront].iterNo = pupilPlane.
iterNo;
616 if( m_roTime_counter >= m_roTime )
618 detector.exposeImage( detectorImage.image, m_wfsImage.image );
620 detectorImage.tipImage = wfsTipImage.image;
621 detectorImage.iterNo = m_wfsImage.iterNo;
623 m_roTime_counter = 0;
629 if( m_iTime_counter >= m_iTime )
635 m_roTime_counter = 0;
641template <
typename realT,
typename detectorT>
647 doSenseWavefront(pupilPlane);
652 detector.exposeImage( detectorImage.image, m_wfsImage.image );
656 detectorImage.tipImage = wfsTipImage.image;
665template <
typename realT,
typename detectorT>
671template <
typename realT,
typename detectorT>
674 m_opdMaskMade =
false;
677template <
typename realT,
typename detectorT>
683template <
typename realT,
typename detectorT>
689template <
typename realT,
typename detectorT>
696 m_wfPS = m_D/m_pupilSz;
704 m_opdMaskMade =
false;
705 m_preAllocated =
false;
706 m_opdMaskMade =
false;
707 m_preAllocated =
false;
710template <
typename realT,
typename detectorT>
716template <
typename realT,
typename detectorT>
721 if( m_modRadius <= 0 )
727 realT radPerStep = m_perStep / m_modRadius;
741template <
typename realT,
typename detectorT>
747template <
typename realT,
typename detectorT>
753template <
typename realT,
typename detectorT>
757 perStep( m_perStep );
762template <
typename realT,
typename detectorT>
768template <
typename realT,
typename detectorT>
771 if( m_pupilSz == sz )
780 m_wfPS = m_D/m_pupilSz;
789 m_wfPS = m_D/m_pupilSz;
797 m_opdMaskMade =
false;
798 m_preAllocated =
false;
801template <
typename realT,
typename detectorT>
807template <
typename realT,
typename detectorT>
810 if( m_pupilSep == sz )
817 m_opdMaskMade =
false;
818 m_preAllocated =
false;
821template <
typename realT,
typename detectorT>
824 return m_angleOffset;
827template <
typename realT,
typename detectorT>
830 if (m_angleOffset == ao)
837 m_opdMaskMade =
false;
838 m_preAllocated =
false;
841template <
typename realT,
typename detectorT>
852template <
typename realT,
typename detectorT>
855 if( m_imageSz == sz )
864 m_imageSzAuto =
true;
868 m_imageSzAuto =
false;
871 m_opdMaskMade =
false;
872 m_preAllocated =
false;
875template <
typename realT,
typename detectorT>
878 return m_imageSzAuto;
881template <
typename realT,
typename detectorT>
886 m_opdMaskMade =
false;
888 m_preAllocated =
false;
891template <
typename realT,
typename detectorT>
894 complexFieldT opdMaskQ;
901 if(!std::isfinite(m_wfPS) || !std::isnormal(m_wfPS))
906 std::cerr << m_wfPS <<
" " << m_D <<
"\n";
914 m_frProp.setWavefrontSizePixels(m_wfSz);
916 m_opdMask.resize(m_wfSz, m_wfSz);
917 opdMaskQ.resize(m_wfSz, m_wfSz);
921 mask.resize(m_opdMask.rows(), m_opdMask.cols());
929 realT pupilRad = m_pupilSep*m_pupilSz /(2*sin(dang/2.0));
931 for(
int n = 0; n < m_nSides; ++n)
933 realT ang = m_angleOffset*math::degreesT<realT>::radians + 0.5*dang + n * dang;
935 realT dx = pupilRad * cos(ang);
937 if(dx < minx) minx = dx;
938 if(dx > maxx) maxx = dx;
940 realT dy = pupilRad * sin(ang);
942 if(dy < miny) miny = dy;
943 if(dy > maxy) maxy = dy;
945 opdMaskQ.set(std::complex<realT>(0, 1));
949 wfp::extractMaskedPixels(m_opdMask, opdMaskQ, mask);
952 int xsz = 2*std::max( {fabs(maxx), fabs(minx)} ) + 2*std::max({(pupilRad/2), ((realT)m_pupilSz/2)});
954 int ysz = 2*std::max( {fabs(maxy), fabs(miny)} ) + 2*std::max({(pupilRad/2), ((realT)m_pupilSz/2)});
958 m_imageSz = std::max(xsz, ysz);
962 m_imageSz += (m_pupilSep-1.0)*m_pupilSz;
966 if(m_imageSz > m_wfSz)
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. ";
974 m_wfsImage.image.resize(m_imageSz, m_imageSz);
976 if(m_detRows == 0 || m_detCols == 0)
981 if(m_detRows > m_imageSz || m_detCols > m_imageSz)
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) +
"). ";
989 m_opdMaskMade =
true;
992template <
typename realT,
typename detectorT>
993void pyramidSensor<realT, detectorT>::makeTilts()
997 if( m_modSteps == 0 )
1000 "pyramidSensor::makeTilts()",
1001 "number of modulation steps (m_modSteps) has not been set." );
1007 "pyramidSensor::makeTilts()",
1008 "wavefront platescale (m_wfPS) is 0. Must set pupilSz and D first." );
1011 if( !std::isfinite( m_wfPS ) )
1014 "pyramidSensor::makeTilts()",
1015 "wavefront platescale (m_wfPS) is infinite. Must set pupilSz and D first." );
1023 realT dang = 2 *
pi / ( m_modSteps );
1026 m_tilts.resize( m_modSteps );
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 )
1036 for(
int i = 0; i < m_modSteps; ++i )
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 );
1043 m_tilts[i].resize( m_wfSz, m_wfSz );
1044 m_tilts[i].set( std::complex<realT>( 0, 1 ) );
1052template <
typename realT,
typename detectorT>
1053void pyramidSensor<realT, detectorT>::allocThreadMem()
1055 if( !m_opdMaskMade )
1060 m_pupilPlaneCF.resize( m_wfSz, m_wfSz );
1062 int maxTh = omp_get_max_threads();
1063 m_th_tiltedPlane.resize( maxTh );
1065 m_th_focalPlane.resize( maxTh );
1067 m_th_focalImage.resize( maxTh );
1069 m_th_sensorPlane.resize( maxTh );
1071 m_th_sensorImage.resize( maxTh );
1073 for(
int nTh = 0; nTh < maxTh; ++nTh )
1075 m_th_tiltedPlane[nTh].resize( m_wfSz, m_wfSz );
1077 m_th_focalPlane[nTh].resize( m_wfSz, m_wfSz );
1079 m_th_focalImage[nTh].resize( m_wfSz, m_wfSz );
1081 m_th_sensorPlane[nTh].resize( m_wfSz, m_wfSz );
1083 m_th_sensorImage[nTh].resize( m_imageSz, m_imageSz );
1086 m_preAllocated =
true;
1089template <
typename realT,
typename detectorT>
1090void pyramidSensor<realT, detectorT>::preAllocate()
1097 if( !m_opdMaskMade )
1102 if( !m_preAllocated )
1108template <
typename realT,
typename detectorT>
1109void pyramidSensor<realT, detectorT>::doSenseWavefront()
1113 wavefrontT pupilPlane;
1116 int _firstWavefront = m_lastWavefront - m_iTime;
1117 if( _firstWavefront < 0 )
1118 _firstWavefront += _wavefronts.size();
1120 pupilPlane.amplitude = _wavefronts[_firstWavefront].amplitude;
1121 pupilPlane.phase = _wavefronts[_firstWavefront].phase;
1123 realT avgIt = _wavefronts[_firstWavefront].iterNo;
1127 for(
int i = 0; i < m_iTime; ++i )
1130 if( (
size_t)_firstWavefront >= _wavefronts.size() )
1131 _firstWavefront = 0;
1133 pupilPlane.amplitude += _wavefronts[_firstWavefront].amplitude;
1134 pupilPlane.phase += _wavefronts[_firstWavefront].phase;
1135 avgIt += _wavefronts[_firstWavefront].iterNo;
1140 pupilPlane.amplitude /= ( m_iTime + 1 );
1141 pupilPlane.phase /= ( m_iTime + 1 );
1143 avgIt /= ( m_iTime + 1.0 );
1146 doSenseWavefront( pupilPlane );
1148 m_wfsImage.iterNo = avgIt;
1151template <
typename realT,
typename detectorT>
1152void pyramidSensor<realT, detectorT>::doSenseWavefront( wavefrontT &pupilPlane )
1154 if( m_modRadius == 0 )
1156 return doSenseWavefrontNoMod( pupilPlane );
1159 if( !m_preAllocated )
1166 m_wfsImage.image.resize( m_imageSz, m_imageSz );
1167 m_wfsImage.image.setZero();
1169 wfsTipImage.image.resize( m_wfSz, m_wfSz );
1170 wfsTipImage.image.setZero();
1172 for(
size_t l = 0; l < m_wavelengths.size(); ++l )
1174 pupilPlane.lambda = m_lambda;
1175 pupilPlane.getWavefront( m_pupilPlaneCF, m_wavelengths[l], m_wfSz );
1179 int nTh = omp_get_thread_num();
1181 m_th_sensorImage[nTh].setZero();
1182 m_th_focalImage[nTh].setZero();
1188 complexT *opdm_Data;
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();
1195 int nelem = m_wfSz * m_wfSz;
1198 for(
int i = 0; i < m_modSteps; ++i )
1201 tim_Data = m_tilts[i].data();
1206 for(
int ii = 0; ii < nelem; ++ii )
1208 tpm_Data[ii] = ppm_Data[ii] * tim_Data[ii];
1214 m_frProp.propagatePupilToFocal( m_th_focalPlane[nTh], m_th_tiltedPlane[nTh],
true );
1219 wfp::extractIntensityImageAccum(
1220 m_th_focalImage[nTh], 0, m_wfSz, 0, m_wfSz, m_th_focalPlane[nTh], 0, 0 );
1225 for(
int ii = 0; ii < nelem; ++ii )
1227 fpm_Data[ii] = fpm_Data[ii] * opdm_Data[ii];
1233 m_frProp.propagateFocalToPupil( m_th_sensorPlane[nTh], m_th_focalPlane[nTh],
true );
1238 wfp::extractIntensityImageAccum( m_th_sensorImage[nTh],
1243 m_th_sensorPlane[nTh],
1244 0.5 * m_wfSz - m_imageSz / 2,
1245 0.5 * m_wfSz - m_imageSz / 2 );
1253 m_wfsImage.image += m_th_sensorImage[nTh] * _wavelengthWeights[l];
1254 wfsTipImage.image += m_th_focalImage[nTh] * _wavelengthWeights[l];
1262 m_wfsImage.image /= m_modSteps;
1263 wfsTipImage.image /= m_modSteps;
1266template <
typename realT,
typename detectorT>
1267void pyramidSensor<realT, detectorT>::doSenseWavefrontNoMod( wavefrontT &pupilPlane )
1271 if( !m_opdMaskMade )
1276 m_wfsImage.image.resize( m_imageSz, m_imageSz );
1277 m_wfsImage.image.setZero();
1279 wfsTipImage.image.resize( m_wfSz, m_wfSz );
1280 wfsTipImage.image.setZero();
1282 complexFieldT m_pupilPlaneCF;
1284 pupilPlane.getWavefront( m_pupilPlaneCF, m_wfSz );
1286 complexFieldT tiltedPlane;
1287 complexFieldT focalPlane;
1288 complexFieldT sensorPlane;
1290 tiltedPlane.resize( m_wfSz, m_wfSz );
1291 focalPlane.resize( m_wfSz, m_wfSz );
1292 sensorPlane.resize( m_wfSz, m_wfSz );
1294 int nelem = m_wfSz * m_wfSz;
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();
1306 for(
int ii = 0; ii < nelem; ++ii )
1308 tpm_Data[ii] = ppm_Data[ii];
1316 m_frProp.propagatePupilToFocal( focalPlane, tiltedPlane,
true );
1323 wfp::extractIntensityImageAccum( wfsTipImage.image, 0, m_wfSz, 0, m_wfSz, focalPlane, 0, 0 );
1330 for(
int ii = 0; ii < nelem; ++ii )
1332 fpm_Data[ii] = fpm_Data[ii] * opdm_Data[ii];
1340 m_frProp.propagateFocalToPupil( sensorPlane, focalPlane,
true );
1347 wfp::extractIntensityImageAccum( m_wfsImage.image,
1353 0.5 * m_wfSz - m_imageSz / 2,
1354 0.5 * m_wfSz - m_imageSz / 2 );
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.
pyramidSensor()
Default c'tor.
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.
constexpr floatT six_fifths()
Return 6/5 in the specified precision.
realT rtod(realT q)
Convert from radians to degrees.
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.
Structure containing the phase and amplitude of a wavefront.
realImageT amplitude
The wavefront amplitude.
realT iterNo
The iteration number of this wavefront.
realImageT phase
The wavefront phase.