mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
clGainOpt.hpp
Go to the documentation of this file.
1 /** \file clGainOpt.hpp
2  * \author Jared R. Males (jaredmales@gmail.com)
3  * \brief Provides a class to manage closed loop gain optimization.
4  * \ingroup mxAO_files
5  *
6  */
7 
8 //***********************************************************************//
9 // Copyright 2016-2020 Jared R. Males (jaredmales@gmail.com)
10 //
11 // This file is part of mxlib.
12 //
13 // mxlib is free software: you can redistribute it and/or modify
14 // it under the terms of the GNU General Public License as published by
15 // the Free Software Foundation, either version 3 of the License, or
16 // (at your option) any later version.
17 //
18 // mxlib is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 // GNU General Public License for more details.
22 //
23 // You should have received a copy of the GNU General Public License
24 // along with mxlib. If not, see <http://www.gnu.org/licenses/>.
25 //***********************************************************************//
26 
27 #ifndef clGainOpt_hpp
28 #define clGainOpt_hpp
29 
30 #ifdef MX_INCLUDE_BOOST
31 #include <boost/math/tools/minima.hpp>
32 #endif
33 
34 #include <type_traits>
35 
36 #include <Eigen/Dense>
37 
38 #include "../../sys/timeUtils.hpp"
39 
40 #include "../../math/constants.hpp"
41 
42 //#define ALLOW_F_ZERO
43 
44 namespace mx
45 {
46 namespace AO
47 {
48 namespace analysis
49 {
50 
51 //forward declaration of worker functor
52 template<typename realT>
53 struct clGainOptOptGain_OL;
54 
55 
56 /// A class to manage optimizing closed-loop gains
57 /**
58  * \tparam _realT the real floating point type in which to do all arithmetic. Is used to define the complex type as well.
59  *
60  * \ingroup mxAOAnalytic
61  */
62 template<typename _realT>
63 struct clGainOpt
64 {
65  typedef _realT realT; ///< The real data type
66  typedef std::complex<_realT> complexT; ///< The complex data type
67 
68 protected:
69 
70  int _N; ///< Number of integrations in the (optional) moving average. Default is 1.
71  realT _Ti; ///< The loop sampling interval
72  realT _tau; ///< The loop delay
73 
74  std::vector<realT> _b; ///< Vector of FIR coefficients
75  std::vector<realT> _a; ///< Vector of IIR coefficients
76 
77  std::vector<realT> _f; ///< Vector of frequencies
78 
79  bool _fChanged; ///< True if frequency or max size of _a and _b changes
80 
81  bool _changed; ///< True if any of the members which make up the basic transfer functions are changed
82 
83  Eigen::Array<realT, -1, -1> _cs;
84  Eigen::Array<realT, -1, -1> _ss;
85 
86  std::vector<std::complex<realT> > _H_dm, _H_wfs, _H_ma, _H_del, _H_con;
87 
88 public:
89  /** Parameters for stability analysis
90  * @{
91  */
92 
93  realT _maxFindMin; ///< The Minimum value for the maximum stable gain finding algorithm.
94 
95  ///@}
96 
97  /** Parameters for minimization finding
98  * @{
99  */
100 
101  realT _minFindMin; ///< The Minimum value for the minimum finding algorithm.
102  realT _minFindMaxFact; ///< The maximum value, as a multiplicative factor of maximum gain
103  int _minFindBits; ///< The bits of precision to use for minimum finding. Defaults to std::numeric_limits<realT>::digits.
104  uintmax_t _minFindMaxIter; ///< The maximum iterations allowed for minimization.
105 
106  ///@}
107 
108 
109  /// Default c'tor.
110  clGainOpt();
111 
112  /// C'tor setting the loop timings.
113  /**
114  */
115  clGainOpt( realT Ti, ///< [in] the desired loop sampling interval.
116  realT tau ///< [in] the desired loop delay.
117  );
118 
119  /// Initialize this instance.
120  void init();
121 
122  /// Get the number of integrations in the (optional) moving average
123  /**
124  * \returns the current value of _N.
125  */
126  int N();
127 
128  /// Set the number of integrations in the moving average
129  /**
130  */
131  void N( int newN /**< [in] the value of _N. */);
132 
133  /// Get the loop sampling interval
134  /**
135  * \returns the current value of _Ti.
136  */
137  realT Ti();
138 
139  /// Set the loop sampling interval
140  /**
141  */
142  void Ti( realT newTi /**< [in] the new value of _Ti. */);
143 
144  /// Get the loop delay
145  /**
146  * \returns the current value of _tau.
147  */
148  realT tau();
149 
150  /// Set the loop delay
151  /**
152  */
153  void tau( realT newTau /**< [in] the new value of _tau.*/);
154 
155  /// Set the vector of FIR coefficients
156  /**
157  */
158  void b( const std::vector<realT> & newB /**< [in] a vector of coefficients, which is copied to _b.*/);
159 
160  /// Set the vector of FIR coefficients
161  /**
162  */
163  void b( const Eigen::Array<realT, -1, -1> & newB /**< [in] a column-vector Eigen::Array of coefficients, which is copied to _b.*/);
164 
165  void bScale( realT scale)
166  {
167  for(size_t n=0; n < _b.size(); ++n)
168  {
169  _b[n] *= scale;
170  }
171  }
172 
173  /// Set the vector of IIR coefficients
174  /**
175  */
176  void a ( const std::vector<realT> & newA /**< [in] a vector of coefficients, which is copied to _a. */);
177 
178  /// Set the vector of IIR coefficients
179  /**
180  */
181  void a( const Eigen::Array<realT, -1, -1> & newA /**< [in] a column-vector Eigen::Array of coefficients, which is copied to _a.*/);
182 
183  realT a( size_t i) { return _a[i];}
184 
185  void aScale( realT scale)
186  {
187  for(size_t n=0; n < _a.size(); ++n)
188  {
189  _a[n] *= scale;
190  }
191  }
192 
193  /// Set the FIR and IIR coefficients so that the control law is a leaky integrator.
194  /** Set remember to 1.0 for a pure integrator control law.
195  *
196  */
197  void setLeakyIntegrator( realT remember /**< [in] a number usually close to 1 setting the amount "remembered" from previous iterations.*/);
198 
199  /// Set the vector of frequencies
200  /**
201  */
202  void f ( realT * newF, ///< [in] a pointer to an array containing the new frequencies
203  size_t nF ///< [in] the number of elements of size(realT) in newF.
204  );
205 
206  /// Set the vector of frequencies
207  /**
208  */
209  void f ( std::vector<realT> & newF /**< [in] a vector containing the new frequencies */);
210 
211  ///Get the size of the frequency vector
212  /**
213  * \returns _f.size()
214  */
215  size_t f_size()
216  {
217  return _f.size();
218  }
219 
220  /// Get the i-th value of frequency.
221  /** No range checks are conducted.
222  *
223  * \returns the value of _f[i]
224  *
225  */
226  realT f( size_t i /**< [in] the index of the frequency to return*/ );
227 
228  /// Calculate the open-loop transfer function
229  /**
230  * \return the complex value of the open-loop transfer function at f.
231  */
232  complexT olXfer( int fi, ///< [in] the index of the frequency
233  complexT & H_dm, ///< [out] the transfer function of the DM
234  complexT & H_del, ///< [out] the delay transfer function
235  complexT & H_con ///< [out] the controller transfer function.
236  );
237 
238  /// Calculate the open-loop transfer function
239  /**
240  * \overload
241  *
242  * \returns the complex value of the open-loop transfer function at f[fi].
243  */
244  complexT olXfer( int fi /**< [in] the index of the frequency */);
245 
246 
247  /// Return the closed loop error transfer function (ETF) at frequency f for gain g.
248  /**
249  * \returns the closed loop ETF at f and g.
250  */
251  complexT clETF( int fi, ///< [in] the index of the frequency at which to calculate the ETF
252  realT g ///< [in] the loop gain.
253  );
254 
255  /// Return the closed loop error transfer function (ETF) phase at frequency f for gain g.
256  /**
257  * \returns the phase of the closed loop ETF at f and g.
258  */
259  realT clETFPhase( int fi, ///< [in] the index of the frequency at which to calculate the ETF
260  realT g ///< [in] the loop gain.
261  );
262 
263  /// Return the norm of the closed loop error transfer function (ETF) at frequency f for gain g.
264  /**
265  * \returns the norm of the closed loop ETF at f and g.
266  */
267  realT clETF2( int fi, ///< [in] the index of the frequency at which to calculate the ETF.
268  realT g ///< [in] the loop gain.
269  );
270 
271  /// Return the closed loop noise transfer function (NTF) at frequency f for gain g.
272  /**
273  * \returns the closed loop NTF at f and g.
274  */
275  complexT clNTF( int fi, ///< [in] the index of the frequency at which to calculate the NTF
276  realT g ///< [in] the loop gain.
277  );
278 
279  /// Return the norm of the closed loop noise transfer function (NTF) at frequency f for gain g.
280  /**
281  * \returns the value of the closed loop NTF at f and g.
282  */
283  realT clNTF2( int fi, ///< [in] the index of the frequency at which to calculate the NTF
284  realT g ///< [in] the loop gain.
285  );
286 
287  /// Return the norm of the closed loop transfer functions at frequency f for gain g.
288  /** Calculates both the error transfer function (ETF) and the noise transfer function (NTF).
289  * This minimizes the various complex number operations, compared to calling both clETF2 and clNTF2.
290  *
291  */
292  void clTF2( realT & ETF, ///< [out] is set to the ETF at f and g
293  realT & NTF, ///< [out] is set to the NTF at f and g
294  int fi, ///< [in] is the index of the frequency at which to calculate the ETF
295  realT g ///< [in] is the loop gain.
296  );
297 
298 
299  /// Calculate the closed loop variance given open-loop PSDs and gain
300  /** Calculates the following quantities.
301  \f[
302  \sigma_{err}^2 = \sum_i \left| ETF(f_i) \right|^2 PSD_{err}(fi) \Delta f\\
303  \sigma_{noise}^2 = \sum_i \left| NTF(f_i) \right|^2 PSD_{noise}(fi) \Delta f\\
304  \sigma^2 = \sigma_{err}^2 + \sigma_{noise}^2
305  \f]
306  * \f$ \sigma^2 \f$ is returned, and \f$ \sigma_{err}^2 \f$ and \f$ \sigma_{noise}^2 \f$ are available as the optional
307  * arguments varErr and varNoise.
308  *
309  * \returns the total variance (error + noise) in closed loop
310  */
311  realT clVariance( realT & varErr, ///< [out] the variance in the residual process error.
312  realT & varNoise, ///< [out] the variance in the residual measurement noise.
313  std::vector<realT> & PSDerr, ///< [in] the open-loop process error PSD.
314  std::vector<realT> & PSDnoise, ///< [in] the open-loop measurement noise PSD.
315  realT g ///< [in] the gain.
316  );
317 
318  /// Calculate the closed loop variance given open-loop PSDs and gain
319  /** Overload of clVariance without the varErr and varNoise output parameters.
320  *
321  * \overload
322  *
323  * \returns the total variance (error + noise) in closed loop
324  */
325  realT clVariance( std::vector<realT> & PSDerr, ///< [in] the open-loop process error PSD.
326  std::vector<realT> & PSDnoise, ///< [in] the open-loop measurement noise PSD.
327  realT g ///< [in] the gain.
328  );
329 
330  /// Find the maximum stable gain for the loop parameters
331  /**
332  *
333  * \returns the maximum stable gain for the loop parameters
334  */
335 
336 
337  /// Find the maximum stable gain for the loop parameters
338  /** Conducts a search along the Nyquist contour of the open-loop transfer function to find
339  * the most-negative crossing of the real axis.
340  *
341  * \returns the maximum stable gain for the loop parameters
342  */
343  realT maxStableGain( realT & ll, ///< [in.out] the lower limit used for the search
344  realT & ul ///< [in.out] the upper limit used for hte search
345  );
346 
347  /// Find the maximum stable gain for the loop parameters
348  /** Conducts a search along the Nyquist contour of the open-loop transfer function to find
349  * the most-negative crossing of the real axis.
350  *
351  * This version allows constant arguments.
352  * \overload
353  *
354  * \returns the maximum stable gain for the loop parameters
355  */
356  realT maxStableGain( const realT & ll, ///< [in] the lower limit used for the search
357  const realT & ul ///< [in] the upper limit used for hte search
358  );
359 
360  /// Find the maximum stable gain for the loop parameters
361  /** Conducts a search along the Nyquist contour of the open-loop transfer function to find
362  * the most-negative crossing of the real axis.
363  *
364  * This version uses _maxFindMin for the lower limit and no upper limit.
365  *
366  * \overload
367  *
368  * \returns the maximum stable gain for the loop parameters
369  */
370  realT maxStableGain();
371 
372  ///Return the optimum closed loop gain given an open loop PSD
373  /** Uses _gmax for the upper limit.
374  * \returns the optimum gain
375  */
376  realT optGainOpenLoop( realT & var, ///< [out] the variance at the optimum gain
377  std::vector<realT> & PSDerr, ///< [in] open loop error PSD
378  std::vector<realT> & PSDnoise ///< [in] open loop measurement noise PSD
379  );
380 
381  ///Return the optimum closed loop gain given an open loop PSD
382  /**
383  * \returns the optimum gain.
384  */
385  realT optGainOpenLoop( realT & var, ///< [out] the variance at the optimum gain
386  std::vector<realT> & PSDerr, ///< [in] open loop error PSD
387  std::vector<realT> & PSDnoise, ///< [in] open loop measurement noise PSD
388  realT & gmax ///< [in] maximum gain to consider. If 0, then _gmax is used.
389  );
390 
391  ///Calculate the pseudo open-loop PSD given a closed loop PSD
392  /**
393  * \returns 0 on success
394  */
395  int pseudoOpenLoop( std::vector<realT> & PSD, ///< [in.out] input closed loop PSD, on output contains the pseudo open loop error PSD ,
396  realT g ///< [in] the loop gain when PSD was measured.
397  );
398 
399  int nyquist( std::vector<realT> & re,
400  std::vector<realT> & im,
401  realT g
402  );
403 };
404 
405 template<typename realT>
406 clGainOpt<realT>::clGainOpt()
407 {
408  init();
409 }
410 
411 template<typename realT>
412 clGainOpt<realT>::clGainOpt(realT Ti, realT tau)
413 {
414  init();
415 
416  _Ti = Ti;
417  _tau = tau;
418 }
419 
420 template<typename realT>
421 void clGainOpt<realT>::init()
422 {
423  _N = 1;
424 
425  setLeakyIntegrator(1.0);
426 
427  _Ti = 1./1000.;
428  _tau = 2.5*_Ti;
429 
430  _maxFindMin = 0.0;
431 
432  _minFindMin = 1e-9;
433  _minFindMaxFact = 0.999;
434  _minFindBits = std::numeric_limits<realT>::digits;
435  _minFindMaxIter = 1000;
436 
437  _fChanged = true;
438  _changed = true;
439 
440 }
441 
442 template<typename realT>
443 int clGainOpt<realT>::N()
444 {
445  return _N;
446 }
447 
448 template<typename realT>
449 void clGainOpt<realT>::N( int newN )
450 {
451  _N = newN;
452  _changed = true;
453 }
454 
455 template<typename realT>
456 realT clGainOpt<realT>::Ti()
457 {
458  return _Ti;
459 }
460 
461 template<typename realT>
462 void clGainOpt<realT>::Ti( realT newTi)
463 {
464  _Ti = newTi;
465  _changed = true;
466 }
467 
468 template<typename realT>
469 realT clGainOpt<realT>::tau()
470 {
471  return _tau;
472 }
473 
474 template<typename realT>
475 void clGainOpt<realT>::tau(realT newTau)
476 {
477  _tau = newTau;
478  _changed = true;
479 }
480 
481 template<typename realT>
482 void clGainOpt<realT>::b( const std::vector<realT> & newB)
483 {
484  if( newB.size() > (size_t) _cs.cols() )
485  {
486  _fChanged = true;
487  }
488 
489  _b = newB;
490  _changed = true;
491 }
492 
493 template<typename realT>
494 void clGainOpt<realT>::b( const Eigen::Array<realT, -1, -1> & newB)
495 {
496  if( newB.cols() > _cs.cols() )
497  {
498  _fChanged = true;
499  }
500 
501  _b.resize( newB.cols());
502 
503  for(size_t i=0; i< _b.size(); ++i)
504  {
505  _b[i] = newB(0,i);
506  }
507 
508  _changed = true;
509 }
510 
511 template<typename realT>
512 void clGainOpt<realT>::a ( const std::vector<realT> & newA)
513 {
514  if( newA.size()+1 > (size_t) _cs.cols())
515  {
516  _fChanged = true;
517  }
518 
519  _a = newA;
520  _changed = true;
521 }
522 
523 template<typename realT>
524 void clGainOpt<realT>::a( const Eigen::Array<realT, -1, -1> & newA)
525 {
526  if( newA.cols() +1 > _cs.cols())
527  {
528  _fChanged = true;
529  }
530 
531  _a.resize( newA.cols());
532 
533  for(size_t i=0; i< _a.size(); ++i)
534  {
535  _a[i] = newA(0,i);
536  }
537 
538  _changed = true;
539 }
540 
541 template<typename realT>
542 void clGainOpt<realT>::setLeakyIntegrator(realT remember)
543 {
544  _b.resize(1);
545  _b[0] = 1.0;
546 
547  _a.resize(1);
548  _a[0] = remember;
549 
550  _fChanged = true;
551  _changed = true;
552 }
553 
554 template<typename realT>
555 void clGainOpt<realT>::f(realT * newF, size_t nF)
556 {
557  _f.resize(nF);
558  for(int i=0; i< nF; ++i) _f[i] = newF[i];
559 
560  _fChanged = true;
561  _changed = true;
562 }
563 
564 template<typename realT>
565 void clGainOpt<realT>::f(std::vector<realT> & newF)
566 {
567  _f = newF;
568  _fChanged = true;
569 
570  _changed = true;
571 }
572 
573 template<typename realT>
574 realT clGainOpt<realT>::f(size_t i)
575 {
576 
577  return _f[i];
578 }
579 
580 template<typename realT>
581 std::complex<realT> clGainOpt<realT>::olXfer(int fi)
582 {
583  complexT H_dm;
584  complexT H_del;
585  complexT H_con;
586 
587  return olXfer(fi, H_dm, H_del, H_con);
588 }
589 
590 //If PRECALC_TRIG is defined, then the cosine and sine tables are pre-calculated and used instead of repeated exp(-i) calls.
591 //This is much much faster, though uses more memory. In general, only undefine this for testing or debugging.
592 #define PRECALC_TRIG
593 
594 template<typename realT>
595 std::complex<realT> clGainOpt<realT>::olXfer(int fi, complexT & H_dm, complexT & H_del, complexT & H_con)
596 {
597  #ifndef ALLOW_F_ZERO
598  if(_f[fi] <= 0)
599  {
600  #else
601  if(_f[fi] < 0)
602  {
603  #endif
604 
605  H_dm = 0;
606  H_del = 0;
607  H_con = 0;
608  return 0;
609  }
610 
611  #ifdef PRECALC_TRIG
612  if(_fChanged)
613  {
614  size_t jmax = std::max(_a.size()+1, _b.size());
615 
616  _cs.resize(_f.size(), jmax);
617  _ss.resize(_f.size(), jmax);
618 
619  for(size_t i=0; i< _f.size(); ++i)
620  {
621  _cs(i,0) = 1.0;
622  _ss(i,0) = 0.0;
623 
624  for(size_t j = 1; j<jmax; ++j)
625  {
626  _cs(i,j) = cos(math::two_pi<realT>()*_f[i]*_Ti*realT(j));
627  _ss(i,j) = sin(math::two_pi<realT>()*_f[i]*_Ti*realT(j));
628  }
629  }
630 
631  _fChanged = false;
632  }
633  #endif
634 
635  if(_changed)
636  {
637  _H_dm.resize(_f.size(), 0);
638  _H_wfs.resize(_f.size(), 0);
639  _H_ma.resize(_f.size(), 0);
640  _H_del.resize(_f.size(), 0);
641  _H_con.resize(_f.size(), 0);
642 
643  size_t jmax = std::min(_a.size(), _b.size());
644 
645  //#pragma omp parallel for
646  for(size_t i=0; i<_f.size(); ++i)
647  {
648  #ifndef ALLOW_F_ZERO
649  if(_f[i] <= 0) continue;
650  #else
651  if(_f[i] < 0) continue;
652  #endif
653 
654  complexT s = complexT(0.0, math::two_pi<realT>()*_f[i]);
655 
656  complexT expsT = exp(-s*_Ti);
657 
658  if(_f[i] == 0)
659  {
660  _H_dm[i] = std::complex<realT>(1,0);
661  }
662  else
663  {
664  _H_dm[i] = (realT(1) - expsT)/(s*_Ti);
665  }
666 
667  _H_wfs[i] = _H_dm[i];
668 
669  _H_ma[i] = 1; //realT(1./_N)*(realT(1) - pow(expsT,_N))/(realT(1) - expsT);
670 
671  _H_del[i] = exp(-s*_tau);
672 
673  complexT FIR = complexT(_b[0],0);
674 
675  complexT IIR = complexT(0.0, 0.0);
676  for(size_t j = 1; j < jmax; ++j)
677  {
678  #ifdef PRECALC_TRIG
679  realT cs = _cs(i,j);
680  realT ss = _ss(i,j);
681  FIR += _b[j]*complexT(cs, -ss);
682  IIR += _a[j-1]*complexT(cs, -ss);
683  #else
684  complexT expZ = exp( -s*_Ti*realT(j));
685  FIR += _b[j]*expZ;
686  IIR += _a[j-1]*expZ;
687  #endif
688  }
689 
690  for(size_t jj=jmax; jj< _a.size()+1; ++jj)
691  {
692  #ifdef PRECALC_TRIG
693  realT cs = _cs(i,jj);
694  realT ss = _ss(i,jj);
695  IIR += _a[jj-1]*complexT(cs, -ss);
696  #else
697  complexT expZ = exp( -s*_Ti*realT(jj));
698  IIR += _a[jj-1]*expZ;
699  #endif
700  }
701 
702  for(size_t jj=jmax; jj<_b.size(); ++jj)
703  {
704  #ifdef PRECALC_TRIG
705  realT cs = _cs(i,jj);
706  realT ss = _ss(i,jj);
707  FIR += _b[jj]*complexT(cs, -ss);
708  #else
709  complexT expZ = exp( -s*_Ti*realT(jj));
710  FIR += _b[jj]*expZ;
711  #endif
712  }
713 
714  _H_con[i] = FIR/( realT(1.0) - IIR);
715 
716  /*if( i == 0 || i == 1)
717  {
718  std::cerr << i << " " << _f[fi] << " " << s << " " << expsT << " " << _H_wfs[i] << " " << _H_dm[i] << " " << _H_con[i] << " " << _H_del[i] << "\n";
719  //exit(0);
720  }/**/
721 
722  }
723 
724  _changed = false;
725  }
726 
727  H_dm = _H_dm[fi];
728  H_del = _H_del[fi];//*_H_ma[fi];
729  H_con = _H_con[fi];
730 
731  return ( _H_dm[fi]*_H_wfs[fi]*_H_del[fi]*_H_con[fi]);
732 
733 }
734 
735 template<typename realT>
736 std::complex<realT> clGainOpt<realT>::clETF(int fi, realT g)
737 {
738  #ifndef ALLOW_F_ZERO
739  if(_f[fi] <= 0) return 0;
740  #else
741  if(_f[fi] < 0) return 0;
742  #endif
743 
744  return (realT(1)/( realT(1) + g*olXfer(fi)));
745 }
746 
747 template<typename realT>
748 realT clGainOpt<realT>::clETFPhase(int fi, realT g)
749 {
750  #ifndef ALLOW_F_ZERO
751  if(_f[fi] <= 0) return 0;
752  #else
753  if(_f[fi] < 0) return 0;
754  #endif
755 
756  return std::arg((realT(1)/( realT(1) + g*olXfer(fi))));
757 }
758 
759 template<typename realT>
760 realT clGainOpt<realT>::clETF2(int fi, realT g)
761 {
762  #ifndef ALLOW_F_ZERO
763  if(_f[fi] <= 0) return 0;
764  #else
765  if(_f[fi] < 0) return 0;
766  #endif
767 
768  return norm(realT(1)/( realT(1) + g*olXfer(fi)));
769 }
770 
771 template<typename realT>
772 std::complex<realT> clGainOpt<realT>::clNTF( int fi,
773  realT g
774  )
775 {
776  #ifndef ALLOW_F_ZERO
777  if(_f[fi] <= 0) return 0;
778  #else
779  if(_f[fi] < 0) return 0;
780  #endif
781 
782  complexT H_dm, H_del, H_con;
783 
784  complexT olX = olXfer(fi, H_dm, H_del, H_con); //H_dm*H_wfs*H_ma*H_del*H_con;
785 
786  return -(H_dm*H_del*g*H_con)/(realT(1) + g*olX);
787 
788 }
789 
790 template<typename realT>
791 realT clGainOpt<realT>::clNTF2( int fi,
792  realT g
793  )
794 {
795  #ifndef ALLOW_F_ZERO
796  if(_f[fi] <= 0) return 0;
797  #else
798  if(_f[fi] < 0) return 0;
799  #endif
800 
801  complexT H_dm, H_del, H_con;
802 
803  complexT olX = olXfer(fi, H_dm, H_del, H_con); //H_dm*H_wfs*H_ma*H_del*H_con;
804 
805  complexT NTF = -(H_dm*H_del*g*H_con)/(realT(1) + g*olX);
806 
807  return norm(NTF);
808 }
809 
810 template<typename realT>
811 void clGainOpt<realT>::clTF2( realT & ETF,
812  realT & NTF,
813  int fi,
814  realT g
815  )
816 {
817  #ifndef ALLOW_F_ZERO
818  if(_f[fi] <= 0)
819  #else
820  if(_f[fi] < 0)
821  #endif
822  {
823  ETF = 0;
824  NTF = 0;
825  return;
826  }
827 
828  complexT H_dm, H_del, H_con;
829 
830  complexT olX = olXfer(fi, H_dm, H_del, H_con); //H_dm*H_wfs*H_ma*H_del*H_con;
831 
832  if(_f[fi] == 0)
833  {
834  }
835 
836  ETF = norm(realT(1)/( realT(1) + g*olX));
837  NTF = norm(-(H_dm*H_del*g*H_con) /( realT(1) + g*olX) );
838 
839  /*if(_f[fi] == 0)
840  {
841  std::cerr << "ETF: " << ETF << " NTF: " << NTF << "\n";
842  }*/
843 }
844 
845 template<typename realT>
846 realT clGainOpt<realT>::clVariance( realT & varErr,
847  realT & varNoise,
848  std::vector<realT> & PSDerr,
849  std::vector<realT> & PSDnoise,
850  realT g
851  )
852 {
853  if(_f.size() != PSDerr.size() || _f.size() != PSDnoise.size())
854  {
855  std::cerr << "clVariance: Frequency grid and PSDs must be same size." << std::endl;
856  return -1;
857  }
858 
859  realT ETF, NTF, df;
860 
861  varErr = 0;
862  varNoise = 0;
863 
864  df = _f[1] - _f[0];
865 
866  for(size_t i=0; i< PSDerr.size(); ++i)
867  {
868  if(g == 0)
869  {
870  ETF = 1;
871  NTF = 0;
872  }
873  else
874  {
875  clTF2(ETF, NTF, i, g);
876  }
877  varErr += ETF*PSDerr[i]*df;
878  varNoise += NTF*PSDnoise[i]*df;
879  }
880 
881  return varErr + varNoise;
882 }
883 
884 template<typename realT>
885 realT clGainOpt<realT>::clVariance( std::vector<realT> & PSDerr,
886  std::vector<realT> & PSDnoise,
887  realT g
888  )
889 {
890  realT varErr;
891  realT varNoise;
892 
893  return clVariance(varErr, varNoise, PSDerr, PSDnoise, g );
894 }
895 
896 
897 template<typename realT>
898 realT clGainOpt<realT>::maxStableGain( realT & ll, realT & ul)
899 {
900  static_cast<void>(ul);
901 
902  std::vector<realT> re, im;
903 
904  if(ll == 0) ll = _maxFindMin;
905 
906  nyquist( re, im, 1.0);
907 
908  int gi_c = re.size()-1;
909 
910  for(int gi=re.size()-2; gi >= 0; --gi)
911  {
912  if( -1.0/re[gi] < ll) continue;
913 
914  if( ( re[gi] < 0) && ( im[gi+1] >= 0 && im[gi] < 0) )
915  {
916  //Check for loop back in Nyquist diagram
917  if( re[gi] <= re[gi_c] ) gi_c = gi;
918  }
919  }
920 
921  return -1.0/ re[gi_c];
922 
923 }
924 
925 template<typename realT>
926 realT maxStableGain(const realT & ll, const realT & ul)
927 {
928  realT rll = ll;
929  realT rul = ul;
930 
931  maxStableGain(rll, rul);
932 }
933 
934 template<typename realT>
935 realT clGainOpt<realT>::maxStableGain()
936 {
937  realT ul = 0;
938  realT ll = _maxFindMin;
939 
940  return maxStableGain(ll, ul);
941 }
942 
943 //Implement the minimization, allowing pre-compiled specializations
944 namespace impl
945 {
946 
947 template<typename realT>
948 realT optGainOpenLoop( clGainOptOptGain_OL<realT> & olgo,
949  realT & var,
950  realT & gmax,
951  realT & minFindMin,
952  realT & minFindMaxFact,
953  int minFindBits,
954  uintmax_t minFindMaxIter
955  )
956 {
957  #ifdef MX_INCLUDE_BOOST
958  realT gopt;
959 
960  try
961  {
962  std::pair<realT,realT> brack;
963  brack = boost::math::tools::brent_find_minima<clGainOptOptGain_OL<realT>, realT>(olgo, minFindMin, minFindMaxFact*gmax, minFindBits, minFindMaxIter);
964  gopt = brack.first;
965  var = brack.second;
966  }
967  catch(...)
968  {
969  std::cerr << "optGainOpenLoop: No root found\n";
970  gopt = minFindMaxFact*gmax;
971  var = 0;
972  }
973 
974  return gopt;
975  #else
976  static_assert(std::is_fundamental<realT>::value || !std::is_fundamental<realT>::value, "impl::optGainOpenLoop<realT> is not specialized for type realT, and MX_INCLUDE_BOOST is not defined, so I can't just use boost.");
977  return 0;
978  #endif
979 }
980 
981 template<>
982 float optGainOpenLoop<float>( clGainOptOptGain_OL<float> & olgo,
983  float & var,
984  float & gmax,
985  float & minFindMin,
986  float & minFindMaxFact,
987  int minFindBits,
988  uintmax_t minFindMaxIter
989  );
990 
991 template<>
992 double optGainOpenLoop<double>( clGainOptOptGain_OL<double> & olgo,
993  double & var,
994  double & gmax,
995  double & minFindMin,
996  double & minFindMaxFact,
997  int minFindBits,
998  uintmax_t minFindMaxIter
999  );
1000 
1001 template<>
1002 long double optGainOpenLoop<long double>( clGainOptOptGain_OL<long double> & olgo,
1003  long double & var,
1004  long double & gmax,
1005  long double & minFindMin,
1006  long double & minFindMaxFact,
1007  int minFindBits,
1008  uintmax_t minFindMaxIter
1009  );
1010 
1011 #ifdef HASQUAD
1012 template<>
1013 __float128 optGainOpenLoop<__float128>( clGainOptOptGain_OL<__float128> & olgo,
1014  __float128 & var,
1015  __float128 & gmax,
1016  __float128 & minFindMin,
1017  __float128 & minFindMaxFact,
1018  int minFindBits,
1019  uintmax_t minFindMaxIter
1020  );
1021 #endif
1022 
1023 } //namespace impl
1024 
1025 template<typename realT>
1026 realT clGainOpt<realT>::optGainOpenLoop( realT & var,
1027  std::vector<realT> & PSDerr,
1028  std::vector<realT> & PSDnoise)
1029 {
1030  realT gmax = 0;
1031  return optGainOpenLoop(var, PSDerr, PSDnoise, gmax);
1032 }
1033 
1034 
1035 template<typename realT>
1036 realT clGainOpt<realT>::optGainOpenLoop( realT & var,
1037  std::vector<realT> & PSDerr,
1038  std::vector<realT> & PSDnoise,
1039  realT & gmax )
1040 {
1041  clGainOptOptGain_OL<realT> olgo;
1042  olgo.go = this;
1043  olgo.PSDerr = &PSDerr;
1044  olgo.PSDnoise = &PSDnoise;
1045 
1046  if(gmax <= 0) gmax = maxStableGain();
1047 
1048  return impl::optGainOpenLoop( olgo, var, gmax, _minFindMin, _minFindMaxFact, _minFindBits, _minFindMaxIter);
1049 }
1050 
1051 template<typename realT>
1052 int clGainOpt<realT>::pseudoOpenLoop( std::vector<realT> & PSD, realT g)
1053 {
1054  realT e;
1055  for(int f=0; f< _f.size(); ++f)
1056  {
1057  e = clETF2(f, g);
1058 
1059  if(e > 0) PSD[f] = PSD[f]/ e;
1060  }
1061 
1062  return 0;
1063 }
1064 
1065 template<typename realT>
1066 int clGainOpt<realT>::nyquist( std::vector<realT> & re,
1067  std::vector<realT> & im,
1068  realT g
1069  )
1070 {
1071  re.resize(_f.size());
1072  im.resize(_f.size());
1073 
1074  complexT etf;
1075 
1076  for(size_t f=0; f< _f.size(); ++f)
1077  {
1078  etf = g*olXfer(f);// clETF(f, g);
1079  re[f] = real(etf);
1080  im[f] = imag(etf);
1081  }
1082 
1083  return 0;
1084 }
1085 
1086 //------------ Workers ---------------------
1087 
1088 ///Bisection worker struct for finding optimum closed loop gain from open loop PSDs
1089 template<typename realT>
1090 struct clGainOptOptGain_OL
1091 {
1092  clGainOpt<realT> * go;
1093  std::vector<realT> * PSDerr;
1094  std::vector<realT> * PSDnoise;
1095 
1096  realT operator()(const realT & g)
1097  {
1098  return go->clVariance(*PSDerr, *PSDnoise, g);
1099  }
1100 };
1101 
1102 
1103 //Explicit Instantiation
1104 extern template
1105 class clGainOpt<float>;
1106 
1107 extern template
1108 class clGainOpt<double>;
1109 
1110 extern template
1111 class clGainOpt<long double>;
1112 
1113 #ifdef HASQUAD
1114 extern template
1115 class clGainOpt<__float128>;
1116 #endif
1117 
1118 } //namespace analysis
1119 } //namespace AO
1120 } //namespace mx
1121 
1122 #endif //clGainOpt_hpp
The mxlib c++ namespace.
Definition: mxError.hpp:107