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