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