mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
zernike.hpp
Go to the documentation of this file.
1 /** \file zernike.hpp
2  * \author Jared R. Males (jaredmales@gmail.com)
3  * \brief Working with the Zernike polynomials.
4  *
5  * \todo the basic zernike polys should be in math::func.
6  *
7  * \ingroup signal_processing_files
8  *
9  */
10 
11 //***********************************************************************//
12 // Copyright 2015-2020 Jared R. Males (jaredmales@gmail.com)
13 //
14 // This file is part of mxlib.
15 //
16 // mxlib is free software: you can redistribute it and/or modify
17 // it under the terms of the GNU General Public License as published by
18 // the Free Software Foundation, either version 3 of the License, or
19 // (at your option) any later version.
20 //
21 // mxlib is distributed in the hope that it will be useful,
22 // but WITHOUT ANY WARRANTY; without even the implied warranty of
23 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 // GNU General Public License for more details.
25 //
26 // You should have received a copy of the GNU General Public License
27 // along with mxlib. If not, see <http://www.gnu.org/licenses/>.
28 //***********************************************************************//
29 
30 #ifndef math_zernike_hpp
31 #define math_zernike_hpp
32 
33 #include <cmath>
34 
35 #include <vector>
36 
37 #include "../math/func/bessel.hpp"
38 #include "../math/func/jinc.hpp"
39 #include "../math/func/factorial.hpp"
40 #include "../math/func/sign.hpp"
41 #include "../math/constants.hpp"
42 #include "../mxError.hpp"
43 
44 
45 namespace mx
46 {
47 namespace sigproc
48 {
49 
50 /**
51  * \ingroup zernike_basis
52  * @{
53  */
54 
55 
56 /// Get the Zernike coefficients n,m corrresponding the Noll index j.
57 /** Calculates the values of (n,m) for an index j following Noll (1976) \cite noll_1976
58  * See also: http://en.wikipedia.org/wiki/Zernike_polynomials
59  *
60  * If j is odd, this returns m <= 0.
61  *
62  *
63  * \retval 0 on success
64  * \retval -1 on error (j < 1)
65  *
66  * \test Scenario: testing noll_nm \ref tests_sigproc_zernike_noll_nm "[test doc]"
67  */
68 int noll_nm( int & n, ///< [out] n the radial index of the Zernike polynomial
69  int & m, ///< [out] m the azimuthal index of the Zernnike polynomial. m < 0 if j odd.
70  int j ///< [in] j the Noll index, j > 0.
71  );
72 
73 /// Get the Noll index j corresponding to Zernike coefficients n,m
74 /** Calculates the value j for(n,m) following Noll (1976) \cite noll_1976
75  * See also: http://en.wikipedia.org/wiki/Zernike_polynomials
76  *
77  * \retval >= 0 on success
78  * \retval -1 on error (n-m odd)
79  *
80  */
81 int noll_j( unsigned n, ///< [in] n the radial index of the Zernike polynomial
82  int m ///< [in] m the azimuthal index of the Zernnike polynomial.
83  );
84 
85 /// Get the number of Zernikes up to and including a radial order.
86 /** Calculates the total number of Zernike polynomials through radial order \p n. See Noll (1976) \cite noll_1976
87  * See also: http://en.wikipedia.org/wiki/Zernike_polynomials
88  *
89  * \retval the number of
90  * \retval -1 on error (n-m odd)
91  *
92  */
93 int nZernRadOrd( unsigned n /**< [n] the radial order */ );
94 
95 /// Calculate the coefficients of a Zernike radial polynomial
96 /**
97  * \retval 0 on success
98  * \retval -1 on error
99  *
100  * \tparam realT is a real floating type
101  */
102 template<typename realT>
103 int zernikeRCoeffs( std::vector<realT> & c, ///< [out] allocated to length \f$ 0.5(n-m)+1\f$ and filled with the coefficients.
104  int n, ///< [in] the radial index of the Zernike polynomial.
105  int m ///< [in] the azimuthal index of the Zernike polynomial.
106  )
107 {
108  m = abs(m);
109 
110  if( n < m)
111  {
112  mxError("zernikeRCoeffs", MXE_INVALIDARG, "n cannot be less than m in the Zernike polynomials");
113  return -1;
114  }
115 
116  //If odd, it's 0.
117  if( (n-m) % 2 > 0)
118  {
119  c.resize(1,0);
120  return 0;
121  }
122 
123  int ul = 0.5*(n-m) + 1;
124 
125  c.resize(ul);
126 
127  for(int k=0; k < ul; ++k)
128  {
129  c[k] = pow(-1.0, k) * math::func::factorial<realT>(n - k) / ( math::func::factorial<realT>(k) * math::func::factorial<realT>(0.5*(n+m) - k)* math::func::factorial<realT>(0.5*(n-m) - k));
130  }
131 
132  return 0;
133 }
134 
135 //Explicit instantiations:
136 extern template
137 int zernikeRCoeffs<float>( std::vector<float> & c, int n, int m);
138 
139 extern template
140 int zernikeRCoeffs<double>( std::vector<double> & c, int n, int m);
141 
142 extern template
143 int zernikeRCoeffs<long double>( std::vector<long double> & c, int n, int m);
144 
145 #ifdef HASQUAD
146 extern template
147 int zernikeRCoeffs<__float128>( std::vector<__float128> & c, int n, int m);
148 #endif
149 
150 /// Calculate the value of a Zernike radial polynomial at a given separation.
151 /**
152  *
153  * \retval -9999 indicates a possible error
154  * \retval R the value of the Zernike radial polynomial otherwise
155  *
156  * \tparam realT is a real floating type
157  * \tparam calcRealT is a real floating type used for internal calcs, should be at least double.
158  */
159 template<typename realT, typename calcRealT>
160 realT zernikeR( realT rho, ///< [in] the radial coordinate, \f$ 0 \le \rho \le 1 \f$.
161  int n, ///< [in] the radial index of the Zernike polynomial.
162  int m, ///< [in] the azimuthal index of the Zernike polynomial.
163  std::vector<calcRealT> & c ///< [in] contains the radial polynomial coeeficients, and must be of length \f$ 0.5(n-m)+1\f$.
164  )
165 {
166  m = abs(m);
167 
168  //If odd, it's 0.
169  if( (n-m) % 2 > 0)
170  {
171  c.resize(1,0);
172  return 0.0;
173  }
174 
175  if( c.size() != 0.5*(n-m) + 1)
176  {
177  mxError("zernikeR", MXE_INVALIDARG, "c vector has incorrect length for n and m.");
178  return -9999;
179  }
180 
181  realT R = 0.0;
182  for(size_t k=0; k< c.size(); ++k)
183  {
184  R += c[k] * pow(rho, n-2*k);
185  }
186 
187  return R;
188 
189 }
190 
191 extern template
192 float zernikeR<float,double>(float rho, int n, int m, std::vector<double> & c);
193 
194 extern template
195 double zernikeR<double,double>(double rho, int n, int m, std::vector<double> & c);
196 
197 extern template
198 long double zernikeR<long double, long double>(long double rho, int n, int m, std::vector<long double> & c);
199 
200 #ifdef HASQUAD
201 extern template
202 __float128 zernikeR<__float128,__float128>(__float128 rho, int n, int m, std::vector<__float128> & c);
203 #endif
204 
205 /// Calculate the value of a Zernike radial polynomial at a given separation.
206 /**
207  * \retval -9999 indicates a possible error
208  * \retval R the value of the Zernike radial polynomial otherwise
209  *
210  * \tparam realT is a real floating type
211  * \tparam calcRealT is a real floating type used for internal calculations, should be at least double
212  */
213 template<typename realT, typename calcRealT>
214 realT zernikeR( realT rho, ///< [in] the radial coordinate, \f$ 0 \le \rho \le 1 \f$.
215  int n, ///< [in] the radial index of the Zernike polynomial.
216  int m ///< [in] the azimuthal index of the Zernike polynomial.
217  )
218 {
219  m = abs(m);
220 
221  //If odd, it's 0.
222  if( (n-m) % 2 > 0)
223  {
224  return 0.0;
225  }
226 
227  std::vector<calcRealT> c;
228 
229  if(zernikeRCoeffs(c, n, m) < 0) return -9999;
230 
231  return zernikeR<realT, calcRealT>(rho, n, m, c);
232 
233 }
234 
235 extern template
236 float zernikeR<float, double>( float rho, int n, int m);
237 
238 extern template
239 double zernikeR<double, double>( double rho, int n, int m);
240 
241 extern template
242 long double zernikeR<long double, long double>( long double rho, int n, int m);
243 
244 #ifdef HASQUAD
245 extern template
246 __float128 zernikeR<__float128,__float128>( __float128 rho, int n, int m);
247 #endif
248 
249 /// Calculate the value of a Zernike radial polynomial at a given radius and angle.
250 /**
251  * \param[in] rho is the radial coordinate, \f$ 0 \le \rho \le 1 \f$.
252  * \param[in] phi is the azimuthal angle (in radians)
253  * \param[in] n is the radial index of the Zernike polynomial.
254  * \param[in] m is the azimuthal index of the Zernike polynomial.
255  * \param[in] c is contains the radial polynomial coeeficients, and must be of length \f$ 0.5(n-m)+1\f$.
256  *
257  * \retval -9999 indicates a possible error
258  * \retval R the value of the Zernike radial polynomial otherwise
259  *
260  * \tparam realT is a real floating type
261  * \tparam calcRealT is a real floating type used for internal calculations, should be at least double
262  */
263 template<typename realT, typename calcRealT>
264 realT zernike( realT rho,
265  realT phi,
266  int n,
267  int m,
268  std::vector<calcRealT> & c
269  )
270 {
271  realT azt;
272 
273  if( n == 0 && m == 0)
274  {
275  return 1.0;
276  }
277 
278  if( m < 0 )
279  {
280  azt = math::root_two<realT>()*sin(-m*phi);
281  }
282  else if (m > 0)
283  {
284  azt = math::root_two<realT>()*cos(m*phi);
285  }
286  else
287  {
288  azt = 1.0;
289  }
290 
291  return sqrt((realT) n+1) * zernikeR<realT, calcRealT>(rho, n, m, c) * azt;
292 
293 }
294 
295 extern template
296 float zernike<float, double>(float rho, float phi, int n, int m, std::vector<double> & c);
297 
298 extern template
299 double zernike<double, double>(double rho, double phi, int n, int m, std::vector<double> & c);
300 
301 extern template
302 long double zernike<long double, long double>(long double rho, long double phi, int n, int m, std::vector<long double> & c);
303 
304 #ifdef HASQUAD
305 extern template
306 __float128 zernike<__float128,__float128>(__float128 rho, __float128 phi, int n, int m, std::vector<__float128> & c);
307 #endif
308 
309 /// Calculate the value of a Zernike radial polynomial at a given radius and angle.
310 /**
311  *
312  * \retval -9999 indicates a possible error
313  * \retval R the value of the Zernike radial polynomial otherwise
314  *
315  * \tparam realT is a real floating type
316  * \tparam calcRealT is a real floating type used for internal calculations, should be at least double
317  */
318 template<typename realT, typename calcRealT>
319 realT zernike( realT rho, ///< [in] the radial coordinate, \f$ 0 \le \rho \le 1 \f$.
320  realT phi, ///< [in] the azimuthal angle (in radians)
321  int n, ///< [in] the radial index of the Zernike polynomial.
322  int m ///< [in] the azimuthal index of the Zernike polynomial.
323  )
324 {
325 
326  std::vector<calcRealT> c;
327 
328  if( zernikeRCoeffs<calcRealT>(c, n, m) < 0) return -9999;
329 
330  return zernike<realT,calcRealT>(rho, phi, n, m, c);
331 }
332 
333 extern template
334 float zernike<float,double>( float rho, float phi, int n, int m);
335 
336 extern template
337 double zernike<double,double>( double rho, double phi, int n, int m);
338 
339 extern template
340 long double zernike<long double, long double>( long double rho, long double phi, int n, int m);
341 
342 #ifdef HASQUAD
343 extern template
344 __float128 zernike<__float128,__float128>( __float128 rho, __float128 phi, int n, int m);
345 #endif
346 
347 /// Calculate the value of a Zernike radial polynomial at a given radius and angle.
348 /**
349  * \retval -9999 indicates a possible error
350  * \retval R the value of the Zernike radial polynomial otherwise
351  *
352  * \tparam realT is a real floating type
353  * \tparam calcRealT is a real floating type used for internal calculations, should be at least double
354  */
355 template<typename realT, typename calcRealT>
356 realT zernike( realT rho, ///< [in] the radial coordinate, \f$ 0 \le \rho \le 1 \f$.
357  realT phi, ///< [in] the azimuthal angle (in radians)
358  int j ///< [in] the Noll index of the Zernike polynomial.
359  )
360 {
361  int n, m;
362 
363  //Get n and m from j
364  if(noll_nm(n, m, j) < 0) return -9999;
365 
366  return zernike<realT, calcRealT>(rho, phi, n, m);
367 }
368 
369 extern template
370 float zernike<float, double>(float rho, float phi, int j);
371 
372 extern template
373 double zernike<double, double>(double rho, double phi, int j);
374 
375 extern template
376 long double zernike<long double, long double>(long double rho, long double phi, int j);
377 
378 #ifdef HASQUAD
379 extern template
380 __float128 zernike<__float128, __float128>(__float128 rho, __float128 phi, int j);
381 #endif
382 
383 ///Fill in an Eigen-like array with a Zernike polynomial
384 /** Sets any pixel which is at rad <= r < rad+0.5 pixels to rho = 1, to be consistent with mx::circularPupil
385  *
386  * \param[out] arr is the allocated array with an Eigen-like interface. The rows() and cols() members are used to size the polynomial.
387  * \param[in] n is the radial index of the polynomial
388  * \param[in] m is the azimuthal index of the polynomial
389  * \param[in] xcen is the x coordinate of the desired center of the polynomial, in pixels
390  * \param[in] ycen is the y coordinate of the desired center of the polynomial, in pixels
391  * \param[in] rad [optional] is the desired radius. If rad <= 0, then the maximum radius based on dimensions of m is used.
392  *
393  * \tparam realT is a real floating type
394  * \tparam calcRealT is a real floating type used for internal calculations, should be at least double
395  */
396 template<typename arrayT, typename calcRealT, int overscan=2>
397 int zernike( arrayT & arr,
398  int n,
399  int m,
400  typename arrayT::Scalar xcen,
401  typename arrayT::Scalar ycen,
402  typename arrayT::Scalar rad = -1 )
403 {
404  typedef typename arrayT::Scalar realT;
405  realT x;
406  realT y;
407  realT r, rho;
408  realT phi;
409 
410  std::vector<calcRealT> c;
411 
412  if(zernikeRCoeffs(c, n, m) < 0) return -1;
413 
414  size_t l0 = arr.rows();
415  size_t l1 = arr.cols();
416 
417  if(rad <= 0) rad = 0.5*std::min(l0-1, l1-1);
418 
419  for(size_t i=0; i < l0; ++i)
420  {
421  for(size_t j=0; j < l1; ++j)
422  {
423  x = i - xcen;
424  y = j - ycen;
425 
426 
427  r = std::sqrt( x*x + y*y );
428 
429  //This is to be consistent with mx::circularPupil while still respecting the Zernike rules
430  if(r > rad && r <= rad+(1.0/overscan)) r = rad;
431 
432  rho = r / rad;
433 
434  if(rho <= 1.0)
435  {
436  phi = std::atan2( y, x);
437  arr(i,j) = zernike(rho, phi, n, m, c);
438  }
439  else
440  {
441  arr(i,j) = 0.0;
442  }
443  }
444  }
445  return 0;
446 }
447 
448 ///Fill in an Eigen-like array with a Zernike polynomial
449 /** Sets any pixel which is at rad <= r <= rad+0.5 pixels to rho = 1, to be consistent with mx::circularPupil
450  *
451  * \param[out] arr is the allocated array with an Eigen-like interface. The rows() and cols() members are used to size the polynomial.
452  * \param[in] j is the Noll index of the polynomial
453  * \param[in] xcen is the x coordinate of the desired center of the polynomial, in pixels
454  * \param[in] ycen is the y coordinate of the desired center of the polynomial, in pixels
455  * \param[in] rad [optional] is the desired radius. If rad <= 0, then the maximum radius based on dimensions of m is used.
456  *
457  * \tparam realT is a real floating type
458  * \tparam calcRealT is a real floating type used for internal calculations, should be at least double
459  */
460 template<typename arrayT, typename calcRealT>
461 int zernike( arrayT & arr,
462  int j,
463  typename arrayT::Scalar xcen,
464  typename arrayT::Scalar ycen,
465  typename arrayT::Scalar rad = -1 )
466 {
467  typedef typename arrayT::Scalar realT;
468 
469  int n, m;
470 
471  if(noll_nm(n, m, j) < 0) return -1;
472 
473  return zernike<arrayT, calcRealT>(arr, n, m, xcen, ycen, rad);
474 }
475 
476 ///Fill in an Eigen-like array with a Zernike polynomial
477 /** The geometric center of the array, 0.5*(arr.rows()-1), 0.5*(arr.cols()-1), is used as the center.
478  * Sets any pixel which is at rad <= r < rad+0.5 pixels to rho = 1, to be consistent with mx::circularPupil
479  *
480  * \param[out] arr is the allocated array with an Eigen-like interface. The rows() and cols() members are used to size the polynomial.
481  * \param[in] j is the Noll index of the polynomial
482  * \param[in] rad [optional] is the desired radius. If rad <= 0, then the maximum radius based on dimensions of m is used.
483  *
484  * \tparam realT is a real floating type
485  * \tparam calcRealT is a real floating type used for internal calculations, should be at least double
486  */
487 template<typename arrayT, typename calcRealT>
488 int zernike( arrayT & arr,
489  int n,
490  int m,
491  typename arrayT::Scalar rad = -1 )
492 {
493  typename arrayT::Scalar xcen = 0.5*(arr.rows()-1.0);
494  typename arrayT::Scalar ycen = 0.5*(arr.cols()-1.0);
495 
496  return zernike<arrayT, calcRealT>(arr, n, m, xcen, ycen, rad);
497 }
498 
499 ///Fill in an Eigen-like array with a Zernike polynomial
500 /** The geometric center of the array, 0.5*(arr.rows()-1), 0.5*(arr.cols()-1), is used as the center.
501  * Sets any pixel which is at rad <= r < rad+0.5 pixels to rho = 1, to be consistent with mx::circularPupil
502  *
503  * \param[out] arr is the allocated array with an Eigen-like interface. The rows() and cols() members are used to size the polynomial.
504  * \param[in] j is the Noll index of the polynomial
505  * \param[in] rad [optional] is the desired radius. If rad <= 0, then the maximum radius based on dimensions of m is used.
506  *
507  * \tparam arrayT is an Eigen-like array of real floating type
508  * \tparam calcRealT is a real floating type used for internal calculations, should be at least double
509  */
510 template<typename arrayT, typename calcRealT>
511 int zernike( arrayT & arr,
512  int j,
513  typename arrayT::Scalar rad = -1 )
514 {
515  typename arrayT::Scalar xcen = 0.5*( arr.rows()-1.0);
516  typename arrayT::Scalar ycen = 0.5*( arr.cols()-1.0);
517 
518  return zernike<arrayT, calcRealT>(arr, j, xcen, ycen, rad);
519 }
520 
521 ///Fill in an Eigencube-like array with Zernike polynomials in Noll order
522 /** The cube is pre-allocated to set the image size and the number of modes.
523  *
524  * \returns 0 on success
525  * \returns -1 on error
526  *
527  * \tparam cubeT is an Eigencube-like array with real floating point type
528  * \tparam calcRealT is a real floating type used for internal calculations, should be at least double
529  */
530 template<typename cubeT, typename calcRealT>
531 int zernikeBasis( cubeT & cube, ///< [in.out] the pre-allocated cube which will be filled with the Zernike basis
532  typename cubeT::Scalar rad = -1, ///< [in] [optional] the radius of the aperture. If -1 then the full image size is used.
533  int minj = 2 ///< [in] [optional] the minimum j value to include. The default is j=2, which skips piston (j=1).
534  )
535 {
536  typedef typename cubeT::imageT arrayT;
537 
538  typename cubeT::imageT im;
539 
540  im.resize(cube.rows(), cube.cols());
541 
542  int rv;
543  for(int i=0; i < cube.planes(); ++i)
544  {
545  rv = zernike<arrayT,calcRealT>( im, minj+i, rad);
546 
547  if(rv < 0)
548  {
549  return rv;
550  }
551  cube.image(i) = im;
552  }
553 
554  return 0;
555 }
556 
557 ///Calculate the square-normed Fourier transform of a Zernike polynomial at position (k,phi)
558 /** Implements Equation (8) of Noll (1976) \cite noll_1976.
559  *
560  * \todo need a more robust jinc_n function for n > 1
561  *
562  * \test Scenario: testing zernikeQNorm \ref tests_sigproc_zernike_zernikeQNorm "[test doc]"
563  *
564  * \returns the value of |Q(k,phi)|^2
565  *
566  * \tparam realT is the floating point type used for arithmetic
567  */
568 template<typename realT>
569 realT zernikeQNorm( realT k, ///< [in] the radial coordinate of normalized spatial frequency. This is in the \cite noll_1976 convention of cycles-per-radius.
570  realT phi, ///< [in] the azimuthal coordinate of normalized spatial frequency
571  int n, ///< [in] the Zernike polynomial n
572  int m ///< [in] the Zernike polynomial m
573  )
574 {
575 
576  realT B;
577 
578  //sloppy implementation of jinc_n for k ~ 0
579  if(k < 0.00001)
580  {
581  if( n == 0 ) B = 1.0;
582  else B = 0.0;
583  }
584  else
585  {
586  B = math::func::bessel_j(n+1, math::two_pi<realT>()*k) / (math::pi<realT>()*k);
587  }
588 
589  realT Q2 = (n+1) * (B * B);
590 
591  if (m > 0 ) // Even j (see Noll 1976)
592  {
593  Q2 = 2*Q2 * pow(cos(m*phi), 2);
594  }
595  else if( m < 0 ) //%Odd j (see Noll 1976)
596  {
597  Q2 = 2*Q2 * pow(sin(-m*phi), 2);
598  }
599 
600  return Q2;
601 }
602 
603 extern template
604 float zernikeQNorm<float>(float k, float phi, int n, int m);
605 
606 extern template
607 double zernikeQNorm<double>(double k, double phi, int n, int m);
608 
609 extern template
610 long double zernikeQNorm<long double>(long double k, long double phi, int n, int m);
611 
612 #ifdef HASQUAD
613 extern template
614 __float128 zernikeQNorm<__float128>(__float128 k, __float128 phi, int n, int m);
615 #endif
616 
617 
618 ///Calculate the square-normed Fourier transform of a Zernike polynomial at position (k,phi)
619 /** Implements Equation (8) of Noll (1976) \cite noll_1976.
620  *
621  * \returns the value of |Q(k,phi)|^2
622  *
623  * \tparam realT is the floating point type used for arithmetic
624  *
625  */
626 template<typename realT>
627 realT zernikeQNorm( realT k, ///< [in] the radial coordinate of normalized spatial frequency. This is in the \cite noll_1976 convention of cycles-per-radius.
628  realT phi, ///< [in] the azimuthal coordinate of normalized spatial frequency
629  int j ///< [in] the Zernike polynomial index j (Noll convention)
630  )
631 {
632  int n, m;
633 
634  noll_nm(n,m,j);
635 
636  return zernikeQNorm(k, phi, n, m);
637 }
638 
639 /// Fill in an Eigen-like array with the square-normed Fourier transform of a Zernike polynomial
640 /** The array is filled in with the values of |Q(k,phi)|^2 according to Equation (8) of Noll (1976) \cite noll_1976.
641  *
642  * \test Scenario: testing zernikeQNorm \ref tests_sigproc_zernike_zernikeQNorm "[test doc]"
643  *
644  * \returns 0 on success
645  * \returns -1 on error
646  *
647  * \tparam arrayT is the Eigen-like array type. Arithmetic will be done in arrayT::Scalar.
648  */
649 template<typename arrayT>
650 int zernikeQNorm( arrayT & arr, ///< [out] the allocated array. The rows() and cols() members are used to size the transform.
651  arrayT & k, ///< [in] the normalized spatial frequency magnitude at each pixel. This is in the \cite noll_1976 convention of cycles-per-radius.
652  arrayT & phi, ///< [in] the spatial frequency angle at each pixel
653  int j ///< [in] the polynomial index in the Noll convention \cite noll_1976
654  )
655 {
656  if(arr.rows() != k.rows() || arr.cols() != k.cols())
657  {
658  mxError("zernikeQNorm", MXE_INVALIDARG, "output array and input k are not the same size");
659  return -1;
660  }
661 
662  if(arr.rows() != phi.rows() || arr.cols() != phi.cols())
663  {
664  mxError("zernikeQNorm", MXE_INVALIDARG, "output array and input phi are not the same size");
665  return -1;
666  }
667 
668  int n,m;
669  if(noll_nm(n,m,j) < 0) return -1; //noll_nm will explain error
670 
671  for(size_t i=0; i < arr.rows(); ++i)
672  {
673  for(size_t j=0; j < arr.cols(); ++j)
674  {
675  arr(i,j) = zernikeQNorm(k(i,j), phi(i,j), n, m);
676  }
677  }
678  return 0;
679 }
680 
681 /// Calculate the spatial power spectrum of Piston
682 template<typename realT>
683 realT zernikePPiston( const realT & kD /**< [in] Spatial frequency in diameter units, i.e. cycles per aperture.*/)
684 {
685  return 4*pow(math::func::jinc(math::pi<realT>()*kD),2);
686 }
687 
688 /// Calculate the spatial power spectrum of Tip \& Tilt
689 template<typename realT>
690 realT zernikePTipTilt( const realT & kD /**< [in] Spatial frequency in diameter units, i.e. cycles per aperture.*/)
691 {
692  return 16*pow(math::func::jincN(2,math::pi<realT>()*kD),2);
693 }
694 
695 /// Calculate the spatial power spectrum of Defocus
696 template<typename realT>
697 realT zernikePDefocus( const realT & kD /**< [in] Spatial frequency in diameter units, i.e. cycles per aperture.*/)
698 {
699  return 12*pow(math::func::jincN(3,math::pi<realT>()*kD),2);
700 }
701 
702 /// Calculate the spatial power spectrum of Astigmatism
703 template<typename realT>
704 realT zernikePAstig( const realT & kD /**< [in] Spatial frequency in diameter units, i.e. cycles per aperture.*/)
705 {
706  return 24*pow(math::func::jincN(3,math::pi<realT>()*kD),2);
707 }
708 
709 /// Calculate the spatial power spectrum of Coma
710 template<typename realT>
711 realT zernikePComa( const realT & kD /**< [in] Spatial frequency in diameter units, i.e. cycles per aperture.*/)
712 {
713  return 32*pow(math::func::jincN(4,math::pi<realT>()*kD),2);
714 }
715 
716 /// Calculate the spatial power spectrum of Trefoil
717 template<typename realT>
718 realT zernikePTrefoil( const realT & kD /**< [in] Spatial frequency in diameter units, i.e. cycles per aperture.*/)
719 {
720  return 32*pow(math::func::jincN(4,math::pi<realT>()*kD),2);
721 }
722 
723 ///@} signal_processing
724 
725 } //namespace sigproc
726 } //namespace mx
727 
728 #endif //math_zernike_hpp
729 
constexpr units::realT c()
The speed of light.
Definition: constants.hpp:60
constexpr units::realT k()
Boltzmann Constant.
Definition: constants.hpp:71
T2 bessel_j(T1 v, T2 x)
Bessel Functions of the First Kind.
Definition: bessel.hpp:49
T2 jincN(const T1 &v, const T2 &x)
The JincN function.
Definition: jinc.hpp:120
T jinc(const T &x)
The Jinc function.
Definition: jinc.hpp:64
int noll_j(unsigned n, int m)
Get the Noll index j corresponding to Zernike coefficients n,m.
realT zernikePPiston(const realT &kD)
Calculate the spatial power spectrum of Piston.
Definition: zernike.hpp:683
realT zernikePDefocus(const realT &kD)
Calculate the spatial power spectrum of Defocus.
Definition: zernike.hpp:697
realT zernikeR(realT rho, int n, int m)
Calculate the value of a Zernike radial polynomial at a given separation.
Definition: zernike.hpp:214
realT zernikePTrefoil(const realT &kD)
Calculate the spatial power spectrum of Trefoil.
Definition: zernike.hpp:718
int nZernRadOrd(unsigned n)
Get the number of Zernikes up to and including a radial order.
realT zernikePTipTilt(const realT &kD)
Calculate the spatial power spectrum of Tip & Tilt.
Definition: zernike.hpp:690
int noll_nm(int &n, int &m, int j)
Get the Zernike coefficients n,m corrresponding the Noll index j.
Definition: zernike.cpp:38
int zernike(arrayT &arr, int j, typename arrayT::Scalar rad=-1)
Fill in an Eigen-like array with a Zernike polynomial.
Definition: zernike.hpp:511
realT zernikePAstig(const realT &kD)
Calculate the spatial power spectrum of Astigmatism.
Definition: zernike.hpp:704
realT zernikePComa(const realT &kD)
Calculate the spatial power spectrum of Coma.
Definition: zernike.hpp:711
int zernikeQNorm(arrayT &arr, arrayT &k, arrayT &phi, int j)
Fill in an Eigen-like array with the square-normed Fourier transform of a Zernike polynomial.
Definition: zernike.hpp:650
int zernikeRCoeffs(std::vector< realT > &c, int n, int m)
Calculate the coefficients of a Zernike radial polynomial.
Definition: zernike.hpp:103
int zernikeBasis(cubeT &cube, typename cubeT::Scalar rad=-1, int minj=2)
Fill in an Eigencube-like array with Zernike polynomials in Noll order.
Definition: zernike.hpp:531
The mxlib c++ namespace.
Definition: mxError.hpp:107