LCOV - code coverage report
Current view: top level - include/ao/sim - pywfsSlopeReconstructor.hpp (source / functions) Coverage Total Hit
Test: mxlib Lines: 30.1 % 113 34
Test Date: 2026-08-30 22:23:37 Functions: 30.0 % 20 6

            Line data    Source code
       1              : /** \file pywfsSlopeReconstructor.hpp
       2              :  * \brief Declares the pyramid-wavefront-sensor slope reconstructor.
       3              :  * \author Jared R. Males (jaredmales@gmail.com)
       4              :  */
       5              : 
       6              : #ifndef __pywfsSlopeReconstructor_hpp__
       7              : #define __pywfsSlopeReconstructor_hpp__
       8              : 
       9              : #include <string>
      10              : 
      11              : #include "../../ao/aoPaths.hpp"
      12              : #include "../../improc/eigenCube.hpp"
      13              : #include "../../ioutils/fits/fitsFile.hpp"
      14              : #include "../../wfp/imagingUtils.hpp"
      15              : 
      16              : namespace mx
      17              : {
      18              : namespace AO
      19              : {
      20              : namespace sim
      21              : {
      22              : 
      23              : struct pywfsSlopeReconstructorSpec
      24              : {
      25              :     std::string dmName;
      26              :     std::string basisName;
      27              : 
      28              :     std::string rMatId;
      29              : };
      30              : 
      31              : /// A Pyramid Wavefront Sensor slope reconstructor.
      32              : /** Calculates slopes, normalized by total flux in the image.
      33              :  */
      34              : template <typename _floatT>
      35              : class pywfsSlopeReconstructor
      36              : {
      37              :   public:
      38              :     typedef _floatT floatT;
      39              : 
      40              :     /// The type of the measurement (i.e. the slope vector)
      41              :     // typedef Eigen::Array<floatT,-1,-1> measurementT;
      42              : 
      43              :     /// The type of the WFS image
      44              :     typedef Eigen::Array<floatT, -1, -1> imageT;
      45              : 
      46              :     /// The type of the response matrix
      47              :     typedef Eigen::Array<floatT, -1, -1> rmatT;
      48              : 
      49              :     typedef pywfsSlopeReconstructorSpec specT;
      50              : 
      51              :   protected:
      52              :     Eigen::Array<floatT, -1, -1> _recon; ///< The reconstructor matrix.
      53              : 
      54              :     int _maskType; /// 0 is centrally obscured circle, 1 is supplied by fits files
      55              : 
      56              :     floatT _maskRadius;      ///< The radius of the quadrant mask
      57              :     floatT _maskObscuration; ///< The central obscuration of the quadrant mask
      58              : 
      59              :     std::string _maskFile; ///< The name of the quadrant mask file
      60              : 
      61              :     bool _maskMade; ///< Whether or not the mask has been made
      62              : 
      63              :     Eigen::Array<floatT, -1, -1> _quadMask; ///< The quadrant mask
      64              :     void calcMask();                        ///< Calculates the quadrant mask
      65              :     int _measurementSize;                   ///< The number of slopes in the measurement
      66              : 
      67              :     floatT _calAmp; ///< The calibration amplitude used for response matrix acquisition
      68              : 
      69              :     int _nModes; ///< The number of modes to be reconstructed
      70              : 
      71              :     int _detRows; ///< The size of the WFS image, in rows
      72              :     int _detCols; ///< The size of the WFS image, in columns
      73              : 
      74              :     imageT _rMat; ///< The response matrix
      75              :     improc::eigenCube<floatT> _rImages;
      76              : 
      77              :   public:
      78              :     int _binFact; ///< The binning to apply before reconstructing.
      79              : 
      80              :     /// Default c'tor
      81              :     pywfsSlopeReconstructor();
      82              : 
      83              :     template <typename AOSysT>
      84              :     void initialize( AOSysT &AOSys, specT &spec )
      85              :     {
      86              :         std::string recMatrix = mx::AO::path::sys::cal::iMat(
      87              :             AOSys._sysName, spec.dmName, AOSys._wfsName, AOSys._pupilName, spec.basisName, spec.rMatId );
      88              :         loadRecon( recMatrix );
      89              :     }
      90              : 
      91              :     /// Get the quadrant mask radius (_maskRadius)
      92              :     floatT maskRadius();
      93              : 
      94              :     /// Set the quadrant mask radius (_maskRadius)
      95              :     /** Calling this will cause the quadrant mask to be recalculated next time it is needed.
      96              :      *
      97              :      * \param mr [in] the new mask radius
      98              :      */
      99              :     void maskRadius( floatT mr );
     100              : 
     101              :     /// Get the quadrant mask central obscuration ratio (_maskObscuration)
     102              :     floatT maskObscuration();
     103              : 
     104              :     /// Set the quadrant mask central obscuration ratio (_maskObscuration)
     105              :     /** Calling this will cause the quadrant mask to be recalculated next time it is needed.
     106              :      *
     107              :      * \param mo [in] the new central obscuration ratio
     108              :      */
     109              :     void maskObscuration( floatT mo );
     110              : 
     111              :     /// Get the quadrant mask file name (_maskFile)
     112              :     std::string maskFile();
     113              : 
     114              :     /// Set the quadrant mask file name (_maskFile)
     115              :     /** Calling this will cause the quadrant mask to be reloaded next time it is needed.
     116              :      *
     117              :      * \param mf [in] the new mask file
     118              :      */
     119              :     void maskFile( const std::string &mf );
     120              : 
     121              :     /// Get the calibration amplitude used in response matrix acquisition (_calAmp)
     122              :     floatT calAmp();
     123              : 
     124              :     /// Set the calibration amplitude used in response matrix acquisition (_calAmp)
     125              :     /**
     126              :      * \param ca [in] the new calibration amplitude
     127              :      */
     128              :     void calAmp( floatT ca );
     129              : 
     130              :     /// Get the number of modes (_nModes)
     131              :     int nModes();
     132              : 
     133              :     /// Get the number of detector rows (_detRows)
     134              :     int detRows();
     135              : 
     136              :     /// Set the number of detector rows (_detRows)
     137              :     void detRows( int dr );
     138              : 
     139              :     /// Get the number of detector columns (_detCols)
     140              :     int detCols();
     141              : 
     142              :     /// Set the number of detector columns (_detCols)
     143              :     void detCols( int dc );
     144              : 
     145              :     /// Load the reconstrutor from the specified FITS file
     146              :     /**
     147              :      * \param fname is the name of the FITS file, including path
     148              :      */
     149              :     void loadRecon( std::string fname );
     150              : 
     151              :     /// Return the size of the unbinned measurement
     152              :     int measurementSize();
     153              : 
     154              :     /// Calculate the slope measurement
     155              :     /**
     156              :      * \param slopes [out] a (_measurementSize X 2)  array of slopes
     157              :      * \param wfsImage [in] the WFS image from which to measure the slopes
     158              :      */
     159              :     template <typename measurementT, typename wfsImageT>
     160              :     void calcMeasurement( measurementT &slopes, wfsImageT &wfsImage );
     161              : 
     162              :     /// Reconstruct the wavefront from the input image, producing the modal amplitude vector
     163              :     template <typename measurementT, typename wfsImageT>
     164              :     void reconstruct( measurementT &commandVect, wfsImageT &wfsImage );
     165              : 
     166              :     /// Initialize the response matrix for acquisition
     167              :     /**
     168              :      * \param nmodes the number of modes
     169              :      * \param calamp the calibration amplitude
     170              :      * \param detrows the number of detector rows
     171              :      * \param detcols the number of detector columns
     172              :      */
     173              :     void initializeRMat( int nmodes, floatT calamp, int detrows, int detcols );
     174              : 
     175              :     /// Accumalte the next measurement in the response matrix
     176              :     /**
     177              :      * \param i the measurement index
     178              :      * \param measureVec is the i-th measurement vector
     179              :      */
     180              :     template <typename measurementT>
     181              :     void accumulateRMat( int i, measurementT &measureVec );
     182              : 
     183              :     template <typename measurementT, typename wfsImageT>
     184              :     void accumulateRMat( int i, measurementT &measureVec, wfsImageT &wfsImage );
     185              : 
     186              :     /// Write the accumulated response matrix to disk
     187              :     /**
     188              :      * \param fname the name, including path, of the response matrix
     189              :      */
     190              :     void saveRMat( std::string fname );
     191              : 
     192              :     void saveRImages( std::string fname );
     193              : };
     194              : 
     195              : template <typename floatT>
     196            1 : pywfsSlopeReconstructor<floatT>::pywfsSlopeReconstructor()
     197              : {
     198            1 :     _maskRadius = 0;
     199            1 :     _maskObscuration = 0;
     200              : 
     201            1 :     _maskMade = false;
     202              : 
     203            1 :     _binFact = 1;
     204            1 : }
     205              : 
     206              : template <typename floatT>
     207            0 : floatT pywfsSlopeReconstructor<floatT>::maskRadius()
     208              : {
     209            0 :     return _maskRadius;
     210              : }
     211              : 
     212              : template <typename floatT>
     213            0 : void pywfsSlopeReconstructor<floatT>::maskRadius( floatT mr )
     214              : {
     215            0 :     _maskRadius = mr;
     216            0 :     _maskType = 0;
     217            0 :     _maskMade = false;
     218            0 : }
     219              : 
     220              : template <typename floatT>
     221            0 : floatT pywfsSlopeReconstructor<floatT>::maskObscuration()
     222              : {
     223            0 :     return _maskObscuration;
     224              : }
     225              : 
     226              : template <typename floatT>
     227            0 : void pywfsSlopeReconstructor<floatT>::maskObscuration( floatT mo )
     228              : {
     229            0 :     _maskObscuration = mo;
     230            0 :     _maskType = 0;
     231            0 :     _maskMade = false;
     232            0 : }
     233              : 
     234              : template <typename floatT>
     235            0 : std::string pywfsSlopeReconstructor<floatT>::maskFile()
     236              : {
     237            0 :     return _maskFile;
     238              : }
     239              : 
     240              : template <typename floatT>
     241            1 : void pywfsSlopeReconstructor<floatT>::maskFile( const std::string &mf )
     242              : {
     243            1 :     _maskFile = mf;
     244            1 :     _maskType = 1;
     245            1 :     _maskMade = false;
     246            1 : }
     247              : 
     248              : template <typename floatT>
     249            0 : floatT pywfsSlopeReconstructor<floatT>::calAmp()
     250              : {
     251            0 :     return _calAmp;
     252              : }
     253              : 
     254              : template <typename floatT>
     255            0 : void pywfsSlopeReconstructor<floatT>::calAmp( floatT ca )
     256              : {
     257            0 :     _calAmp = ca;
     258            0 : }
     259              : 
     260              : template <typename floatT>
     261            0 : int pywfsSlopeReconstructor<floatT>::nModes()
     262              : {
     263            0 :     return _nModes;
     264              : }
     265              : 
     266              : template <typename floatT>
     267            0 : int pywfsSlopeReconstructor<floatT>::detRows()
     268              : {
     269            0 :     return _detRows;
     270              : }
     271              : 
     272              : template <typename floatT>
     273            1 : void pywfsSlopeReconstructor<floatT>::detRows( int dr )
     274              : {
     275            1 :     _detRows = dr;
     276            1 :     _maskMade = false;
     277            1 : }
     278              : 
     279              : template <typename floatT>
     280            0 : int pywfsSlopeReconstructor<floatT>::detCols()
     281              : {
     282            0 :     return _detCols;
     283              : }
     284              : 
     285              : template <typename floatT>
     286            1 : void pywfsSlopeReconstructor<floatT>::detCols( int dc )
     287              : {
     288            1 :     _detCols = dc;
     289            1 :     _maskMade = false;
     290            1 : }
     291              : 
     292              : template <typename floatT>
     293            0 : void pywfsSlopeReconstructor<floatT>::loadRecon( std::string fname )
     294              : {
     295            0 :     fits::fitsFile<floatT> ff;
     296            0 :     fits::fitsHeader<> head;
     297              : 
     298            0 :     ff.read( _recon, head, fname );
     299              : 
     300            0 :     _maskRadius = head["MASKRAD"].value<floatT>();
     301            0 :     _maskObscuration = head["MASKOBS"].value<floatT>();
     302            0 :     _maskFile = head["MASKFILE"].value<std::string>();
     303              : 
     304            0 :     if( _maskFile != "" )
     305            0 :         _maskType = 1;
     306              : 
     307            0 :     _calAmp = head["CALAMP"].value<floatT>();
     308              : 
     309            0 :     _nModes = head["NMODES"].value<int>();
     310              : 
     311            0 :     _detRows = head["DETROWS"].value<int>();
     312            0 :     _detCols = head["DETCOLS"].value<int>();
     313              : 
     314            0 :     _maskMade = false;
     315            0 : }
     316              : 
     317              : template <typename floatT>
     318            1 : void pywfsSlopeReconstructor<floatT>::calcMask()
     319              : {
     320            1 :     if( _maskType == 1 )
     321              :     {
     322            1 :         fits::fitsFile<floatT> ff;
     323              : 
     324            1 :         std::cerr << "Loading Mask: " << _maskFile << "\n";
     325            1 :         ff.read( _quadMask, _maskFile );
     326            1 :     }
     327              :     else
     328              :     {
     329            0 :         _quadMask.resize( 0.5 * _detRows / _binFact, 0.5 * _detCols / _binFact );
     330            0 :         wfp::circularPupil( _quadMask, _maskObscuration, _maskRadius / _binFact );
     331              :     }
     332              : 
     333            1 :     _measurementSize = 2 * _quadMask.sum(); // + 64*64;
     334              : 
     335            1 :     _maskMade = true;
     336              : 
     337            1 :     fits::fitsFile<floatT> ff;
     338            2 :     ff.write( "quadMask.fits", _quadMask );
     339            1 : }
     340              : 
     341              : template <typename floatT>
     342            1 : int pywfsSlopeReconstructor<floatT>::measurementSize()
     343              : {
     344            1 :     if( !_maskMade )
     345            1 :         calcMask();
     346            1 :     return _measurementSize;
     347              : }
     348              : 
     349              : template <typename floatT>
     350              : template <typename measurementT, typename wfsImageT>
     351              : void pywfsSlopeReconstructor<floatT>::calcMeasurement( measurementT &slopes, wfsImageT &wfsImage )
     352              : {
     353              :     if( !_maskMade )
     354              :         calcMask();
     355              : 
     356              :     imageT _wfsImage;
     357              : 
     358              :     if( _binFact > 1 )
     359              :     {
     360              :         std::cout << "rebinning" << "\n";
     361              :         _wfsImage.resize( wfsImage.image.rows() / _binFact, wfsImage.image.cols() / _binFact );
     362              :         imageDownSample( _wfsImage, wfsImage.image );
     363              :     }
     364              :     else
     365              :     {
     366              :         _wfsImage = wfsImage.image;
     367              :     }
     368              : 
     369              :     int nsz = _wfsImage.rows();
     370              : 
     371              :     int nPix = _quadMask.sum();
     372              : 
     373              :     std::vector<int> x( nPix ), y( nPix );
     374              : 
     375              :     int k = 0;
     376              :     for( int i = 0; i < _quadMask.rows(); ++i )
     377              :     {
     378              :         for( int j = 0; j < _quadMask.rows(); ++j )
     379              :         {
     380              :             if( _quadMask( i, j ) == 1 )
     381              :             {
     382              :                 x[k] = i;
     383              :                 y[k] = j;
     384              :                 ++k;
     385              :             }
     386              :         }
     387              :     }
     388              : 
     389              :     slopes.measurement.resize( 1, 2. * nPix ); // + 64*64); //wfsImage.tipImage.rows()*wfsImage.tipImage.cols());
     390              : 
     391              :     floatT I0, I1, I2, I3;
     392              : 
     393              :     floatT norm = _wfsImage.sum();
     394              : 
     395              :     for( int i = 0; i < nPix; ++i )
     396              :     {
     397              :         I0 = _wfsImage( x[i], y[i] );
     398              :         I1 = _wfsImage( x[i] + 0.5 * nsz, y[i] );
     399              :         I2 = _wfsImage( x[i], y[i] + 0.5 * nsz );
     400              :         I3 = _wfsImage( x[i] + 0.5 * nsz, y[i] + 0.5 * nsz );
     401              : 
     402              :         if( norm == 0 )
     403              :         {
     404              :             slopes.measurement( 0, i ) = 0;
     405              :             ;
     406              :             slopes.measurement( 0, i + nPix ) = 0; //
     407              :         }
     408              :         else
     409              :         {
     410              :             slopes.measurement( 0, i ) = ( I0 + I1 - I2 - I3 ) / norm;        //(I0+I1+I2+I3);
     411              :             slopes.measurement( 0, i + nPix ) = ( I0 + I2 - I1 - I3 ) / norm; //(I0+I1+I2+I3);
     412              :         }
     413              :     }
     414              : 
     415              :     /*
     416              :     for(int i=0; i< wfsImage.tipImage.rows(); ++i)
     417              :     {
     418              :        for(int j=0;j<wfsImage.tipImage.cols(); ++j)
     419              :        {
     420              :           slopes.measurement(0, 2*nPix + i*wfsImage.tipImage.cols() + j) = wfsImage.tipImage(i,j);
     421              :        }
     422              :     }*/
     423              : }
     424              : 
     425              : template <typename floatT>
     426              : template <typename measurementT, typename wfsImageT>
     427              : void pywfsSlopeReconstructor<floatT>::reconstruct( measurementT &commandVect, wfsImageT &wfsImage )
     428              : {
     429              :     measurementT slopes;
     430              : 
     431              :     calcMeasurement( slopes, wfsImage );
     432              : 
     433              :     commandVect.measurement = slopes.measurement.matrix() * _recon.matrix();
     434              : 
     435              :     commandVect.iterNo = wfsImage.iterNo;
     436              : }
     437              : 
     438              : template <typename floatT>
     439            0 : void pywfsSlopeReconstructor<floatT>::initializeRMat( int nModes, floatT calamp, int detRows, int detCols )
     440              : {
     441              : 
     442            0 :     _nModes = nModes;
     443              : 
     444            0 :     calAmp( calamp );
     445              : 
     446            0 :     _detRows = detRows;
     447            0 :     _detCols = detCols;
     448              : 
     449            0 :     _rMat.resize( measurementSize(), nModes );
     450            0 :     _rMat.setZero();
     451              : 
     452            0 :     _rImages.resize( _detRows, _detCols, _nModes );
     453            0 : }
     454              : 
     455              : template <typename floatT>
     456              : template <typename measurementT>
     457              : void pywfsSlopeReconstructor<floatT>::accumulateRMat( int i, measurementT &measureVec )
     458              : {
     459              :     _rMat.col( i ) = measureVec.measurement.row( 0 );
     460              : }
     461              : 
     462              : template <typename floatT>
     463              : template <typename measurementT, typename wfsImageT>
     464              : void pywfsSlopeReconstructor<floatT>::accumulateRMat( int i, measurementT &measureVec, wfsImageT &wfsImage )
     465              : {
     466              :     accumulateRMat( i, measureVec );
     467              : 
     468              :     _rImages.image( i ) = wfsImage.image;
     469              : }
     470              : 
     471              : template <typename floatT>
     472            0 : void pywfsSlopeReconstructor<floatT>::saveRMat( std::string fname )
     473              : {
     474            0 :     fits::fitsFile<floatT> ff;
     475            0 :     fits::fitsHeader<> head;
     476              : 
     477            0 :     if( _maskType == 1 )
     478              :     {
     479            0 :         head.append( "MASKFILE", maskFile(), "Name of mask file" );
     480              :     }
     481              :     else
     482              :     {
     483            0 :         head.append( "MASKRAD", maskRadius(), "Mask radius, in pixels" );
     484            0 :         head.append( "MASKOBS", maskObscuration(), "Mask fractional central obscuration" );
     485              :     }
     486              : 
     487            0 :     head.append( "DETROWS", _detRows, "WFS detector rows" );
     488            0 :     head.append( "DETCOLS", _detCols, "WFS detector cols" );
     489            0 :     head.append( "CALAMP", _calAmp, "DM Calibration amplitude" );
     490            0 :     head.append( "NMODES", _nModes, "Number of modes included in the response matrix." );
     491              : 
     492              :     // ff.write(fname, _rMat.data(), _rMat.rows(), _rMat.cols(), 1, &head);
     493            0 :     ff.write( fname, _rMat, head );
     494            0 : }
     495              : 
     496              : template <typename floatT>
     497            0 : void pywfsSlopeReconstructor<floatT>::saveRImages( std::string fname )
     498              : {
     499            0 :     fits::fitsFile<floatT> ff;
     500            0 :     fits::fitsHeader<> head;
     501              : 
     502            0 :     if( _maskType == 1 )
     503              :     {
     504            0 :         head.append( "MASKFILE", maskFile(), "Name of mask file" );
     505              :     }
     506              :     else
     507              :     {
     508            0 :         head.append( "MASKRAD", maskRadius(), "Mask radius, in pixels" );
     509            0 :         head.append( "MASKOBS", maskObscuration(), "Mask fractional central obscuration" );
     510              :     }
     511              : 
     512            0 :     head.append( "DETROWS", _detRows, "WFS detector rows" );
     513            0 :     head.append( "DETCOLS", _detCols, "WFS detector cols" );
     514            0 :     head.append( "CALAMP", _calAmp, "DM Calibration amplitude" );
     515            0 :     head.append( "NMODES", _nModes, "Number of modes included in the response matrix." );
     516              : 
     517              :     // ff.write(fname, _rImages.data(), _rImages.rows(), _rImages.cols(), _rImages.planes(), &head);
     518            0 :     ff.write( fname, _rImages, head );
     519            0 : }
     520              : 
     521              : } // namespace sim
     522              : } // namespace AO
     523              : } // namespace mx
     524              : 
     525              : #endif //__pywfsSlopeReconstructor_hpp__
        

Generated by: LCOV version 2.0-1