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( 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 intial 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 intial 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( 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#ifndef ALLOW_F_ZERO
704 if( m_f[fi] <= 0 )
705 {
706#else
707 if( m_f[fi] < 0 )
708 {
709#endif
710
711 H_dm = 0;
712 H_del = 0;
713 H_con = 0;
714 return 0;
715 }
716
717#ifdef PRECALC_TRIG
718 if( m_fChanged )
719 {
720 size_t jmax = std::max( m_a.size() + 1, m_b.size() );
721
722 m_cs.resize( m_f.size(), jmax );
723 m_ss.resize( m_f.size(), jmax );
724
725 for( size_t i = 0; i < m_f.size(); ++i )
726 {
727 m_cs( i, 0 ) = 1.0;
728 m_ss( i, 0 ) = 0.0;
729
730 for( size_t j = 1; j < jmax; ++j )
731 {
732 m_cs( i, j ) = cos( math::two_pi<realT>() * m_f[i] * m_Ti * realT( j ) );
733 m_ss( i, j ) = sin( math::two_pi<realT>() * m_f[i] * m_Ti * realT( j ) );
734 }
735 }
736
737 m_fChanged = false;
738 }
739#endif
740
741 if( m_changed )
742 {
743 m_H_dm.resize( m_f.size(), 0 );
744 m_H_wfs.resize( m_f.size(), 0 );
745 m_H_ma.resize( m_f.size(), 0 );
746 m_H_del.resize( m_f.size(), 0 );
747 m_H_con.resize( m_f.size(), 0 );
748
749 size_t jmax = std::min( m_a.size(), m_b.size() );
750
751 // #pragma omp parallel for
752 for( size_t i = 0; i < m_f.size(); ++i )
753 {
754#ifndef ALLOW_F_ZERO
755 if( m_f[i] <= 0 )
756 continue;
757#else
758 if( m_f[i] < 0 )
759 continue;
760#endif
761
762 complexT s = complexT( 0.0, math::two_pi<realT>() * m_f[i] );
763
764 complexT expsT = exp( -s * m_Ti );
765
766 if( m_f[i] == 0 )
767 {
768 m_H_dm[i] = std::complex<realT>( 1, 0 );
769 }
770 else
771 {
772 m_H_dm[i] = ( realT( 1 ) - expsT ) / ( s * m_Ti );
773 }
774
775 m_H_wfs[i] = m_H_dm[i];
776
777 m_H_ma[i] = 1; // realT(1./m_N)*(realT(1) - pow(expsT,m_N))/(realT(1) - expsT);
778
779 m_H_del[i] = exp( -s * m_tau );
780
781 complexT FIR = complexT( m_b[0], 0 );
782
783 complexT IIR = complexT( 0.0, 0.0 );
784 for( size_t j = 1; j < jmax; ++j )
785 {
786#ifdef PRECALC_TRIG
787 realT cs = m_cs( i, j );
788 realT ss = m_ss( i, j );
789 FIR += m_b[j] * complexT( cs, -ss );
790 IIR += m_remember * m_a[j - 1] * complexT( cs, -ss );
791#else
792 complexT expZ = exp( -s * m_Ti * realT( j ) );
793 FIR += m_b[j] * expZ;
794 IIR += m_remember * m_a[j - 1] * expZ;
795#endif
796 }
797
798 for( size_t jj = jmax; jj < m_a.size() + 1; ++jj )
799 {
800#ifdef PRECALC_TRIG
801 realT cs = m_cs( i, jj );
802 realT ss = m_ss( i, jj );
803 IIR += m_remember * m_a[jj - 1] * complexT( cs, -ss );
804#else
805 complexT expZ = exp( -s * m_Ti * realT( jj ) );
806 IIR += m_remember * m_a[jj - 1] * expZ;
807#endif
808 }
809
810 for( size_t jj = jmax; jj < m_b.size(); ++jj )
811 {
812#ifdef PRECALC_TRIG
813 realT cs = m_cs( i, jj );
814 realT ss = m_ss( i, jj );
815 FIR += m_b[jj] * complexT( cs, -ss );
816#else
817 complexT expZ = exp( -s * m_Ti * realT( jj ) );
818 FIR += m_b[jj] * expZ;
819#endif
820 }
821
822 m_H_con[i] = FIR / ( realT( 1.0 ) - IIR );
823
824 /*if( i == 0 || i == 1)
825 {
826 std::cerr << i << " " << m_f[fi] << " " << s << " " << expsT << " " << m_H_wfs[i] << " " << m_H_dm[i] <<
827 " "
828 << m_H_con[i] << " " << m_H_del[i] << "\n";
829 //exit(0);
830 }/**/
831 }
832
833 m_changed = false;
834 }
835
836 H_dm = m_H_dm[fi];
837 H_del = m_H_del[fi]; //*m_H_ma[fi];
838 H_con = m_H_con[fi];
839
840 return ( m_H_dm[fi] * m_H_wfs[fi] * m_H_del[fi] * m_H_con[fi] );
841}
842
843template <typename realT>
844std::complex<realT> clGainOpt<realT>::clETF( int fi, realT g )
845{
846#ifndef ALLOW_F_ZERO
847 if( m_f[fi] <= 0 )
848 return 0;
849#else
850 if( m_f[fi] < 0 )
851 return 0;
852#endif
853
854 return ( realT( 1 ) / ( realT( 1 ) + g * olXfer( fi ) ) );
855}
856
857template <typename realT>
859{
860#ifndef ALLOW_F_ZERO
861 if( m_f[fi] <= 0 )
862 return 0;
863#else
864 if( m_f[fi] < 0 )
865 return 0;
866#endif
867
868 return std::arg( ( realT( 1 ) / ( realT( 1 ) + g * olXfer( fi ) ) ) );
869}
870
871template <typename realT>
873{
874#ifndef ALLOW_F_ZERO
875 if( m_f[fi] <= 0 )
876 return 0;
877#else
878 if( m_f[fi] < 0 )
879 return 0;
880#endif
881
882 return norm( realT( 1 ) / ( realT( 1 ) + g * olXfer( fi ) ) );
883}
884
885template <typename realT>
886std::complex<realT> clGainOpt<realT>::clNTF( int fi, realT g )
887{
888#ifndef ALLOW_F_ZERO
889 if( m_f[fi] <= 0 )
890 return 0;
891#else
892 if( m_f[fi] < 0 )
893 return 0;
894#endif
895
896 complexT H_dm, H_del, H_con;
897
898 complexT olX = olXfer( fi, H_dm, H_del, H_con ); // H_dm*H_wfs*H_ma*H_del*H_con;
899
900 return -( H_dm * H_del * g * H_con ) / ( realT( 1 ) + g * olX );
901}
902
903template <typename realT>
905{
906#ifndef ALLOW_F_ZERO
907 if( m_f[fi] <= 0 )
908 return 0;
909#else
910 if( m_f[fi] < 0 )
911 return 0;
912#endif
913
914 complexT H_dm, H_del, H_con;
915
916 complexT olX = olXfer( fi, H_dm, H_del, H_con ); // H_dm*H_wfs*H_ma*H_del*H_con;
917
918 complexT NTF = -( H_dm * H_del * g * H_con ) / ( realT( 1 ) + g * olX );
919
920 return norm( NTF );
921}
922
923template <typename realT>
924void clGainOpt<realT>::clTF2( realT &ETF, realT &NTF, int fi, realT g )
925{
926#ifndef ALLOW_F_ZERO
927 if( m_f[fi] <= 0 )
928#else
929 if( m_f[fi] < 0 )
930#endif
931 {
932 ETF = 0;
933 NTF = 0;
934 return;
935 }
936
937 complexT H_dm, H_del, H_con;
938
939 complexT olX = olXfer( fi, H_dm, H_del, H_con ); // H_dm*H_wfs*H_ma*H_del*H_con;
940
941 if( m_f[fi] == 0 )
942 {
943 }
944
945 ETF = norm( realT( 1 ) / ( realT( 1 ) + g * olX ) );
946 NTF = norm( -( H_dm * H_del * g * H_con ) / ( realT( 1 ) + g * olX ) );
947
948 /*if(m_f[fi] == 0)
949 {
950 std::cerr << "ETF: " << ETF << " NTF: " << NTF << "\n";
951 }*/
952}
953
954template <typename realT>
956 realT &varErr, realT &varNoise, const std::vector<realT> &PSDerr, const std::vector<realT> &PSDnoise, realT g )
957{
958 if( m_f.size() != PSDerr.size() || m_f.size() != PSDnoise.size() )
959 {
960 std::cerr << "clVariance: Frequency grid and PSDs must be same size." << std::endl;
961 return -1;
962 }
963
964 realT ETF, NTF, df;
965
966 varErr = 0;
967 varNoise = 0;
968
969 df = m_f[1] - m_f[0];
970
971 for( size_t i = 0; i < PSDerr.size(); ++i )
972 {
973 if( g == 0 )
974 {
975 ETF = 1;
976 NTF = 0;
977 }
978 else
979 {
980 clTF2( ETF, NTF, i, g );
981 }
982 varErr += ETF * PSDerr[i] * df;
983 varNoise += NTF * PSDnoise[i] * df;
984 }
985
986 return varErr + varNoise;
987}
988
989template <typename realT>
990realT clGainOpt<realT>::clVariance( const std::vector<realT> &PSDerr, const std::vector<realT> &PSDnoise, realT g )
991{
992 realT varErr;
993 realT varNoise;
994
995 return clVariance( varErr, varNoise, PSDerr, PSDnoise, g );
996}
997
998template <typename realT>
1000{
1001 static_cast<void>( ul );
1002
1003 std::vector<realT> re, im;
1004
1005 if( ll == 0 )
1006 ll = m_maxFindMin;
1007
1008 nyquist( re, im, 1.0 );
1009
1010 int gi_c = re.size() - 1;
1011
1012 for( int gi = re.size() - 2; gi >= 0; --gi )
1013 {
1014 if( -1.0 / re[gi] < ll )
1015 continue;
1016
1017 if( ( re[gi] < 0 ) && ( im[gi + 1] >= 0 && im[gi] < 0 ) )
1018 {
1019 // Check for loop back in Nyquist diagram
1020 if( re[gi] <= re[gi_c] )
1021 gi_c = gi;
1022 }
1023 }
1024
1025 return -1.0 / re[gi_c];
1026}
1027
1028template <typename realT>
1029realT maxStableGain( const realT &ll, const realT &ul )
1030{
1031 realT rll = ll;
1032 realT rul = ul;
1033
1034 maxStableGain( rll, rul );
1035}
1036
1037template <typename realT>
1039{
1040 realT ul = 0;
1041 realT ll = m_maxFindMin;
1042
1043 return maxStableGain( ll, ul );
1044}
1045
1046// Implement the minimization, allowing pre-compiled specializations
1047namespace impl
1048{
1049
1050template <typename realT>
1051realT optGainOpenLoop( clGainOptOptGain_OL<realT> &olgo,
1052 realT &var,
1053 const realT &gmax,
1054 const realT &minFindMin,
1055 const realT &minFindMaxFact,
1056 int minFindBits,
1057 uintmax_t minFindMaxIter,
1058 uintmax_t &iters )
1059{
1060#ifdef MX_INCLUDE_BOOST
1061 realT gopt;
1062
1063 try
1064 {
1065 std::pair<realT, realT> brack;
1066 brack = boost::math::tools::brentm_findm_minima<clGainOptOptGain_OL<realT>, realT>( olgo,
1067 minFindMin,
1068 minFindMaxFact * gmax,
1069 minFindBits,
1070 minFindMaxIter,
1071 iters );
1072 gopt = brack.first;
1073 var = brack.second;
1074 }
1075 catch( ... )
1076 {
1077 std::cerr << "optGainOpenLoop: No root found\n";
1078 gopt = minFindMaxFact * gmax;
1079 var = 0;
1080 }
1081
1082 return gopt;
1083#else
1084 staticm_assert( std::is_fundamental<realT>::value || !std::is_fundamental<realT>::value,
1085 "impl::optGainOpenLoop<realT> is not specialized for type realT, and MX_INCLUDE_BOOST is not "
1086 "defined, so I can't just use boost." );
1087 return 0;
1088#endif
1089}
1090
1091template <>
1092float optGainOpenLoop<float>( clGainOptOptGain_OL<float> &olgo,
1093 float &var,
1094 const float &gmax,
1095 const float &minFindMin,
1096 const float &minFindMaxFact,
1097 int minFindBits,
1098 uintmax_t minFindMaxIter,
1099 uintmax_t &iters );
1100
1101template <>
1102double optGainOpenLoop<double>( clGainOptOptGain_OL<double> &olgo,
1103 double &var,
1104 const double &gmax,
1105 const double &minFindMin,
1106 const double &minFindMaxFact,
1107 int minFindBits,
1108 uintmax_t minFindMaxIter,
1109 uintmax_t &iters );
1110
1111template <>
1112long double optGainOpenLoop<long double>( clGainOptOptGain_OL<long double> &olgo,
1113 long double &var,
1114 const long double &gmax,
1115 const long double &minFindMin,
1116 const long double &minFindMaxFact,
1117 int minFindBits,
1118 uintmax_t minFindMaxIter,
1119 uintmax_t &iters );
1120
1121#ifdef HASQUAD
1122template <>
1123_m_float128 optGainOpenLoop<_m_float128>( clGainOptOptGain_OL<_m_float128> &olgo,
1124 _m_float128 &var,
1125 const _m_float128 &gmax,
1126 const _m_float128 &minFindMin,
1127 const _m_float128 &minFindMaxFact,
1128 int minFindBits,
1129 uintmax_t minFindMaxIter,
1130 uintmax_t &iters );
1131#endif
1132
1133} // namespace impl
1134
1135template <typename realT>
1137 const std::vector<realT> &PSDerr,
1138 const std::vector<realT> &PSDnoise,
1139 bool gridSearch )
1140{
1141 realT gmax = 0;
1142 return optGainOpenLoop( var, PSDerr, PSDnoise, gmax, gridSearch );
1143}
1144
1145template <typename realT>
1147 realT &var, const std::vector<realT> &PSDerr, const std::vector<realT> &PSDnoise, realT &gmax, bool gridSearch )
1148{
1150 olgo.go = this;
1151 olgo.PSDerr = &PSDerr;
1152 olgo.PSDnoise = &PSDnoise;
1153
1154 if( gmax <= 0 )
1155 gmax = maxStableGain();
1156
1157 realT ming = m_minFindMin;
1158 realT maxg = gmax;
1159
1160 if( gridSearch )
1161 {
1162 realT gstpsz = 0.05;
1163 realT gg = m_minFindMaxFact * gmax;
1164 realT var0 = clVariance( PSDerr, PSDnoise, gg );
1165 realT mingg = gg;
1166
1167 while( gg > m_minFindMin )
1168 {
1169 gg -= gstpsz;
1170 realT var1 = clVariance( PSDerr, PSDnoise, gg );
1171
1172 if( var1 < var0 )
1173 {
1174 var0 = var1;
1175 mingg = gg;
1176 }
1177 }
1178
1179 ming = mingg - gstpsz;
1180 maxg = mingg + gstpsz;
1181
1182 if( ming < m_minFindMin )
1183 ming = m_minFindMin;
1184 if( maxg > gmax )
1185 maxg = gmax;
1186 }
1187
1188 uintmax_t iters;
1189 realT val =
1190 impl::optGainOpenLoop( olgo, var, maxg, ming, m_minFindMaxFact, m_minFindBits, m_minFindMaxIter, iters );
1191
1192 if( iters >= m_minFindMaxIter )
1193 {
1194 // #pragma omp critical
1195 {
1196 std::cerr << "\nclGainOpt<realT>::optGainOpenLoop: minFindMaxIter (" << m_minFindMaxIter << ") reached\n";
1197 }
1198 }
1199
1200 return val;
1201}
1202
1203template <typename realT>
1204int clGainOpt<realT>::pseudoOpenLoop( std::vector<realT> &PSD, realT g )
1205{
1206 realT e;
1207 for( int f = 0; f < m_f.size(); ++f )
1208 {
1209 e = clETF2( f, g );
1210
1211 if( e > 0 )
1212 PSD[f] = PSD[f] / e;
1213 }
1214
1215 return 0;
1216}
1217
1218template <typename realT>
1219int clGainOpt<realT>::nyquist( std::vector<realT> &re, std::vector<realT> &im, realT g )
1220{
1221 re.resize( m_f.size() );
1222 im.resize( m_f.size() );
1223
1224 complexT etf;
1225
1226 for( size_t f = 0; f < m_f.size(); ++f )
1227 {
1228 etf = g * olXfer( f ); // clETF(f, g);
1229 re[f] = real( etf );
1230 im[f] = imag( etf );
1231 }
1232
1233 return 0;
1234}
1235
1236//------------ Workers ---------------------
1237
1238/// Bisection worker struct for finding optimum closed loop gain from open loop PSDs
1239template <typename realT>
1241{
1242 clGainOpt<realT> *go;
1243 const std::vector<realT> *PSDerr;
1244 const std::vector<realT> *PSDnoise;
1245
1246 realT operator()( const realT &g )
1247 {
1248 return go->clVariance( *PSDerr, *PSDnoise, g );
1249 }
1250};
1251
1252// Explicit Instantiation
1253extern template class clGainOpt<float>;
1254
1255extern template class clGainOpt<double>;
1256
1257extern template class clGainOpt<long double>;
1258
1259#ifdef HASQUAD
1260extern template class clGainOpt<_m_float128>;
1261#endif
1262
1263} // namespace analysis
1264} // namespace AO
1265} // namespace mx
1266
1267#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 mxError.hpp:40
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 f(std::vector< realT > &newF)
Set the vector of frequencies.
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.
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