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 <algorithm>
35#include <cmath>
36#include <limits>
37#include <type_traits>
38
39#include <Eigen/Dense>
40
42
43#include "../../math/constants.hpp"
45#include <error/error_t.hpp>
46
47// #define ALLOW_F_ZERO
48
49namespace mx
50{
51namespace AO
52{
53namespace analysis
54{
55
56// forward declaration of worker functor
57template <typename realT>
59
60/// A class to manage optimizing closed-loop gains
61/**
62 * \tparam _realT the real floating point type in which to do all arithmetic. Is used to define the complex type as
63 * well.
64 *
65 * \ingroup mxAOAnalytic
66 */
67template <typename _realT>
69{
70 typedef _realT realT; ///< The real data type
71 typedef std::complex<_realT> complexT; ///< The complex data type
72
73 /// Termination state of a maximum-stable-gain search.
75 {
76 notRun, ///< No search has been attempted.
77 crossingFound, ///< A qualifying Nyquist crossing was found.
78 invalidInput, ///< The frequency grid or derived Nyquist values were invalid.
79 noCrossing ///< No qualifying Nyquist crossing was found.
80 };
81
82 /// Diagnostic summary of a maximum-stable-gain search.
84 {
85 maxStableGainStatus status{ maxStableGainStatus::notRun }; ///< Search termination state.
86 size_t lowerIndex{ std::numeric_limits<size_t>::max() }; ///< Index below the selected crossing.
87 size_t upperIndex{ std::numeric_limits<size_t>::max() }; ///< Index above the selected crossing.
88 realT lowerFrequency{ std::numeric_limits<realT>::quiet_NaN() }; ///< Frequency below the crossing.
89 realT upperFrequency{ std::numeric_limits<realT>::quiet_NaN() }; ///< Frequency above the crossing.
90 realT crossingFrequency{ std::numeric_limits<realT>::quiet_NaN() }; ///< Interpolated crossing frequency.
91 realT crossingReal{ std::numeric_limits<realT>::quiet_NaN() }; ///< Interpolated real Nyquist value.
92 realT gain{ std::numeric_limits<realT>::quiet_NaN() }; ///< Maximum stable gain at the crossing.
93 };
94
95 /// Termination state of an open-loop optimum-gain search.
96 enum class optGainStatus
97 {
98 notRun, ///< No search has been attempted.
99 converged, ///< The minimizer converged inside the search interval.
100 boundaryLimited, ///< The reported minimum lies on a search boundary.
101 invalidInput, ///< The PSDs, search controls, or requested interval were invalid.
102 stabilityFailure, ///< The automatic maximum-stable-gain search failed.
103 iterationLimit, ///< The minimizer exhausted its iteration limit.
104 calculationFailure ///< The minimizer threw or returned invalid output.
105 };
106
107 /// Diagnostic summary of an open-loop optimum-gain search.
109 {
110 optGainStatus status{ optGainStatus::notRun }; ///< Search termination state.
111 uintmax_t iterations{ 0 }; ///< Minimizer iterations attempted.
112 size_t evaluations{ 0 }; ///< Objective evaluations performed.
113 realT requestedMaximumGain{ std::numeric_limits<realT>::quiet_NaN() }; ///< Caller-supplied gain limit.
114 realT searchMinimumGain{ std::numeric_limits<realT>::quiet_NaN() }; ///< Final minimizer lower bound.
115 realT searchMaximumGain{ std::numeric_limits<realT>::quiet_NaN() }; ///< Final minimizer upper bound.
116 realT minimumEvaluatedGain{ std::numeric_limits<realT>::quiet_NaN() }; ///< Smallest evaluated gain.
117 realT maximumEvaluatedGain{ std::numeric_limits<realT>::quiet_NaN() }; ///< Largest evaluated gain.
118 realT gain{ std::numeric_limits<realT>::quiet_NaN() }; ///< Best gain returned by the minimizer.
119 realT variance{ std::numeric_limits<realT>::quiet_NaN() }; ///< Variance at the best gain.
120 maxStableGainReport stability; ///< Automatic stability-search diagnostics, when requested.
121 };
122
123 protected:
124 int m_N; ///< Number of integrations in the (optional) moving average. Default is 1.
125 realT m_Ti; ///< The loop sampling interval
126 realT m_tau; ///< The loop delay
127
128 realT m_remember{ 1.0 }; ///< The leaky integrator forget factor
129 std::vector<realT> m_b; ///< Vector of FIR coefficients
130 std::vector<realT> m_a; ///< Vector of IIR coefficients
131
132 std::vector<realT> m_f; ///< Vector of frequencies
133
134 /// True when frequency, sampling interval, or required controller tap count invalidates m_cs and m_ss.
135 bool m_trigCacheChanged{ true };
136
137 bool m_changed{ true }; ///< True if any of the members which make up the basic transfer functions are changed
138
139 Eigen::Array<realT, -1, -1> m_cs;
140 Eigen::Array<realT, -1, -1> m_ss;
141
142 std::vector<std::complex<realT>> m_H_dm;
143 std::vector<std::complex<realT>> m_H_wfs;
144 std::vector<std::complex<realT>> m_H_ma;
145 std::vector<std::complex<realT>> m_H_del;
146 std::vector<std::complex<realT>> m_H_con;
147
148 public:
149 /** Parameters for stability analysis
150 * @{
151 */
152
153 realT m_maxFindMin; ///< The Minimum value for the maximum stable gain finding algorithm.
154
155 ///@}
156
157 /** Parameters for minimization finding
158 * @{
159 */
160
161 realT m_minFindMin; ///< The Minimum value for the minimum finding algorithm.
162 realT m_minFindMaxFact; ///< The maximum value, as a multiplicative factor of maximum gain
163 int m_minFindBits; ///< The bits of precision to use for minimum finding. Defaults to
164 ///< std::numeric_limits<realT>::digits.
165 uintmax_t m_minFindMaxIter; ///< The maximum iterations allowed for minimization.
166
167 ///@}
168
169 /// Default c'tor.
171
172 /// C'tor setting the loop timings.
173 /**
174 */
175 clGainOpt( realT Ti, ///< [in] the desired loop sampling interval.
176 realT tau ///< [in] the desired loop delay.
177 );
178
179 /// Initialize this instance.
180 void init();
181
182 /// Get the number of integrations in the (optional) moving average
183 /**
184 * \returns the current value of m_N.
185 */
186 int N();
187
188 /// Set the number of integrations in the moving average
189 /**
190 */
191 void N( int newN /**< [in] the value of m_N. */ );
192
193 /// Get the loop sampling interval
194 /**
195 * \returns the current value of m_Ti.
196 */
198
199 /// Set the loop sampling interval
200 /**
201 */
202 void Ti( realT newTi /**< [in] the new value of m_Ti. */ );
203
204 /// Get the loop delay
205 /**
206 * \returns the current value of m_tau.
207 */
209
210 /// Set the loop delay
211 /**
212 */
213 void tau( realT newTau /**< [in] the new value of m_tau.*/ );
214
215 /// Set the vector of FIR coefficients
216 /**
217 */
218 void b( const std::vector<realT> &newB /**< [in] a vector of coefficients, which is copied to m_b.*/ );
219
220 /// Set the vector of FIR coefficients
221 /**
222 */
223 void b( const Eigen::Array<realT, -1, -1>
224 &newB /**< [in] a column-vector Eigen::Array of coefficients,
225 which is copied to m_b.*/ );
226
227 /// Get a single FIR coefficient
228 /**
229 * \returns a single FIR coefficient
230 */
231 realT b( size_t i /**< [in] the index of the FIR coefficient*/ )
232 {
233 return m_b[i];
234 }
235
236 /// Get the vector of FIR coefficients
237 /**
238 */
239 const std::vector<realT> &b()
240 {
241 return m_b;
242 }
243
244 void bScale( realT scale );
245
246 /// Set the vector of IIR coefficients
247 /**
248 */
249 void a( const std::vector<realT> &newA /**< [in] a vector of coefficients, which is copied to m_a. */ );
250
251 /// Set the vector of IIR coefficients
252 /**
253 */
254 void a( const Eigen::Array<realT, -1, -1> &newA /**< [in] a column-vector Eigen::Array of
255 coefficients, which is copied to m_a.*/ );
256 /// Get a single IIR coefficient
257 /**
258 * \returns a single IIR coefficient
259 */
260 realT a( size_t i )
261 {
262 return m_a[i];
263 }
264
265 /// Get the vector of IIR coefficients
266 /**
267 */
268 const std::vector<realT> &a()
269 {
270 return m_a;
271 }
272
273 void aScale( realT scale );
274
275 /// Set the remember factor for a leaky integrator
276 void remember( const realT &rem );
277
278 /// Get the remember factor
279 realT remember();
280
281 /// Set the FIR and IIR coefficients so that the control law is a leaky integrator.
282 /** Set remember to 1.0 for a pure integrator control law.
283 *
284 */
285 void setLeakyIntegrator( realT remember /**< [in] a number usually close to 1 setting the amount "remembered"
286 from previous iterations.*/);
287
288 /// Set the vector of frequencies
289 /**
290 */
291 void f( realT *newF, ///< [in] a pointer to an array containing the new frequencies
292 size_t nF ///< [in] the number of elements of size(realT) in newF.
293 );
294
295 /// Set the vector of frequencies
296 /**
297 */
298 void f( const std::vector<realT> &newF /**< [in] a vector containing the new frequencies */ );
299
300 /// Get the size of the frequency vector
301 /**
302 * \returns m_f.size()
303 */
304 size_t f_size()
305 {
306 return m_f.size();
307 }
308
309 /// Get the i-th value of frequency.
310 /** No range checks are conducted.
311 *
312 * \returns the value of m_f[i]
313 *
314 */
315 realT f( size_t i /**< [in] the index of the frequency to return*/ );
316
317 /// Calculate the open-loop transfer function
318 /**
319 * \return the complex value of the open-loop transfer function at f.
320 */
321 complexT olXfer( int fi, ///< [in] the index of the frequency
322 complexT &H_dm, ///< [out] the transfer function of the DM
323 complexT &H_del, ///< [out] the delay transfer function
324 complexT &H_con ///< [out] the controller transfer function.
325 );
326
327 /// Calculate the open-loop transfer function
328 /**
329 * \overload
330 *
331 * \returns the complex value of the open-loop transfer function at f[fi].
332 */
333 complexT olXfer( int fi /**< [in] the index of the frequency */ );
334
335 /// Return the closed loop error transfer function (ETF) at frequency f for gain g.
336 /**
337 * \returns the closed loop ETF at f and g.
338 */
339 complexT clETF( int fi, ///< [in] the index of the frequency at which to calculate the ETF
340 realT g ///< [in] the loop gain.
341 );
342
343 /// Return the closed loop error transfer function (ETF) phase at frequency f for gain g.
344 /**
345 * \returns the phase of the closed loop ETF at f and g.
346 */
347 realT clETFPhase( int fi, ///< [in] the index of the frequency at which to calculate the ETF
348 realT g ///< [in] the loop gain.
349 );
350
351 /// Return the norm of the closed loop error transfer function (ETF) at frequency f for gain g.
352 /**
353 * \returns the norm of the closed loop ETF at f and g.
354 */
355 realT clETF2( int fi, ///< [in] the index of the frequency at which to calculate the ETF.
356 realT g ///< [in] the loop gain.
357 );
358
359 /// Return the closed loop noise transfer function (NTF) at frequency f for gain g.
360 /**
361 * \returns the closed loop NTF at f and g.
362 */
363 complexT clNTF( int fi, ///< [in] the index of the frequency at which to calculate the NTF
364 realT g ///< [in] the loop gain.
365 );
366
367 /// Return the norm of the closed loop noise transfer function (NTF) at frequency f for gain g.
368 /**
369 * \returns the value of the closed loop NTF at f and g.
370 */
371 realT clNTF2( int fi, ///< [in] the index of the frequency at which to calculate the NTF
372 realT g ///< [in] the loop gain.
373 );
374
375 /// Return the norm of the closed loop transfer functions at frequency f for gain g.
376 /** Calculates both the error transfer function (ETF) and the noise transfer function (NTF).
377 * This minimizes the various complex number operations, compared to calling both clETF2 and clNTF2.
378 *
379 */
380 void clTF2( realT &ETF, ///< [out] is set to the ETF at f and g
381 realT &NTF, ///< [out] is set to the NTF at f and g
382 int fi, ///< [in] is the index of the frequency at which to calculate the ETF
383 realT g ///< [in] is the loop gain.
384 );
385
386 /// Calculate the closed loop variance given open-loop PSDs and gain
387 /** Calculates the following quantities.
388 \f[
389 \sigma_{err}^2 = \sum_i \left| ETF(f_i) \right|^2 PSD_{err}(fi) \Delta f\\
390 \sigma_{noise}^2 = \sum_i \left| NTF(f_i) \right|^2 PSD_{noise}(fi) \Delta f\\
391 \sigma^2 = \sigma_{err}^2 + \sigma_{noise}^2
392 \f]
393 * \f$ \sigma^2 \f$ is returned, and \f$ \sigma_{err}^2 \f$ and \f$ \sigma_{noise}^2 \f$ are available as the
394 optional
395 * arguments varErr and varNoise.
396 *
397 * \returns the total variance (error + noise) in closed loop
398 */
399 realT clVariance( realT &varErr, ///< [out] the variance in the residual process error.
400 realT &varNoise, ///< [out] the variance in the residual measurement noise.
401 const std::vector<realT> &PSDerr, ///< [in] the open-loop process error PSD.
402 const std::vector<realT> &PSDnoise, ///< [in] the open-loop measurement noise PSD.
403 realT g ///< [in] the gain.
404 );
405
406 /// Calculate the closed loop variance given open-loop PSDs and gain
407 /** Overload of clVariance without the varErr and varNoise output parameters.
408 *
409 * \overload
410 *
411 * \returns the total variance (error + noise) in closed loop
412 */
413 realT clVariance( const std::vector<realT> &PSDerr, ///< [in] the open-loop process error PSD.
414 const std::vector<realT> &PSDnoise, ///< [in] the open-loop measurement noise PSD.
415 realT g ///< [in] the gain.
416 );
417
418 /// Find the maximum stable gain for the loop parameters
419 /** Conducts a search along the Nyquist contour of the open-loop transfer function to find
420 * the most-negative crossing of the real axis.
421 *
422 * Crossings below m_maxFindMin are ignored.
423 *
424 * \returns `error_t::noerror` when a crossing is found, `error_t::notfound` when none is found, or an input error.
425 */
426 mx::error_t maxStableGain( realT &gain, /**< [out] maximum stable gain; NaN on failure */
427 maxStableGainReport *report = nullptr /**< [out] optional search diagnostics */ );
428
429 /// Return the optimum closed loop gain given an open loop PSD
430 /** Determines the maximum stable gain before minimizing the variance.
431 * \returns `error_t::noerror` on convergence or a boundary-limited result, otherwise an explicit failure status.
432 */
433 mx::error_t optGainOpenLoop( realT &gain, ///< [out] optimum gain; NaN on failure
434 realT &var, ///< [out] variance at the optimum gain; NaN on failure
435 const std::vector<realT> &PSDerr, ///< [in] open-loop error PSD
436 const std::vector<realT> &PSDnoise, ///< [in] open-loop measurement-noise PSD
437 bool gridSearch, ///< [in] whether to perform a coarse initial search
438 optGainReport *report = nullptr ///< [out] optional search diagnostics
439 );
440
441 /// Return the optimum closed loop gain given an open loop PSD
442 /**
443 * \returns `error_t::noerror` on convergence or a boundary-limited result, otherwise an explicit failure status.
444 */
445 mx::error_t optGainOpenLoop( realT &gain, ///< [out] optimum gain; best estimate on timeout
446 realT &var, ///< [out] variance at the optimum gain
447 const std::vector<realT> &PSDerr, ///< [in] open-loop error PSD
448 const std::vector<realT> &PSDnoise, ///< [in] open-loop measurement-noise PSD
449 realT maximumGain, ///< [in] maximum stable gain bounding the search
450 bool gridSearch, ///< [in] whether to perform a coarse initial search
451 optGainReport *report = nullptr ///< [out] optional search diagnostics
452 );
453
454 /// Calculate the pseudo open-loop PSD given a closed loop PSD
455 /**
456 * \returns 0 on success
457 */
458 int pseudoOpenLoop( std::vector<realT> &PSD, /**< [in.out] input closed loop PSD, on output contains the pseudo open
459 loop error PSD */
460 realT g ///< [in] the loop gain when PSD was measured.
461 );
462
463 int nyquist( std::vector<realT> &re, std::vector<realT> &im, realT g );
464};
465
466template <typename realT>
471
472template <typename realT>
474{
475 init();
476
477 m_Ti = Ti;
478 m_tau = tau;
479}
480
481template <typename realT>
483{
484 m_N = 1;
485
486 setLeakyIntegrator( 1.0 );
487
488 m_Ti = 1. / 1000.;
489 m_tau = 2.5 * m_Ti;
490
491 m_maxFindMin = 0.0;
492
493 m_minFindMin = 1e-9;
494 m_minFindMaxFact = 0.999;
495 m_minFindBits = std::numeric_limits<realT>::digits;
496 m_minFindMaxIter = 10000;
497
498 m_trigCacheChanged = true;
499 m_changed = true;
500}
501
502template <typename realT>
504{
505 return m_N;
506}
507
508template <typename realT>
509void clGainOpt<realT>::N( int newN )
510{
511 if( m_N == newN )
512 {
513 return;
514 }
515
516 m_N = newN;
517 m_changed = true;
518}
519
520template <typename realT>
522{
523 return m_Ti;
524}
525
526template <typename realT>
528{
529 if( m_Ti == newTi )
530 {
531 return;
532 }
533
534 m_Ti = newTi;
535 m_trigCacheChanged = true;
536 m_changed = true;
537}
538
539template <typename realT>
541{
542 return m_tau;
543}
544
545template <typename realT>
547{
548 if( m_tau == newTau )
549 {
550 return;
551 }
552
553 m_tau = newTau;
554 m_changed = true;
555}
556
557template <typename realT>
558void clGainOpt<realT>::b( const std::vector<realT> &newB )
559{
560 if( newB.size() > (size_t)m_cs.cols() )
561 {
562 m_trigCacheChanged = true;
563 }
564
565 m_b = newB;
566 m_changed = true;
567}
568
569template <typename realT>
570void clGainOpt<realT>::b( const Eigen::Array<realT, -1, -1> &newB )
571{
572 if( newB.cols() > m_cs.cols() )
573 {
574 m_trigCacheChanged = true;
575 }
576
577 m_b.resize( newB.cols() );
578
579 for( size_t i = 0; i < m_b.size(); ++i )
580 {
581 m_b[i] = newB( 0, i );
582 }
583
584 m_changed = true;
585}
586
587template <typename realT>
588void clGainOpt<realT>::bScale( realT scale )
589{
590 for( size_t n = 0; n < m_b.size(); ++n )
591 {
592 m_b[n] *= scale;
593 }
594
595 m_changed = true;
596}
597
598template <typename realT>
599void clGainOpt<realT>::a( const std::vector<realT> &newA )
600{
601 if( newA.size() + 1 > (size_t)m_cs.cols() )
602 {
603 m_trigCacheChanged = true;
604 }
605
606 m_a = newA;
607 m_changed = true;
608}
609
610template <typename realT>
611void clGainOpt<realT>::a( const Eigen::Array<realT, -1, -1> &newA )
612{
613 if( newA.cols() + 1 > m_cs.cols() )
614 {
615 m_trigCacheChanged = true;
616 }
617
618 m_a.resize( newA.cols() );
619
620 for( size_t i = 0; i < m_a.size(); ++i )
621 {
622 m_a[i] = newA( 0, i );
623 }
624
625 m_changed = true;
626}
627
628template <typename realT>
629void clGainOpt<realT>::aScale( realT scale )
630{
631 for( size_t n = 0; n < m_a.size(); ++n )
632 {
633 m_a[n] *= scale;
634 }
635
636 m_changed = true;
637}
638
639template <typename realT>
641{
642 if( m_remember != rem )
643 {
644 m_remember = rem;
645
646 m_changed = true;
647 }
648}
649
650template <typename realT>
655
656template <typename realT>
658{
659 if( m_b.size() != 1 || m_a.size() != 1 || m_b[0] != 1.0 || m_a[0] != 1.0 || m_remember != remember )
660 {
661 if( m_b.size() != 1 )
662 {
663 m_b.resize( 1 );
664 m_trigCacheChanged = true;
665 }
666
667 m_b[0] = 1.0;
668
669 if( m_a.size() != 1 )
670 {
671 m_a.resize( 1 );
672 m_trigCacheChanged = true;
673 }
674
675 m_a[0] = 1.0;
676
678
679 m_changed = true;
680 }
681}
682
683template <typename realT>
684void clGainOpt<realT>::f( realT *newF, size_t nF )
685{
686 m_f.resize( nF );
687 for( int i = 0; i < nF; ++i )
688 {
689 m_f[i] = newF[i];
690 }
691
692 m_trigCacheChanged = true;
693 m_changed = true;
694}
695
696template <typename realT>
697void clGainOpt<realT>::f( const std::vector<realT> &newF )
698{
699 m_f = newF;
700 m_trigCacheChanged = true;
701
702 m_changed = true;
703}
704
705template <typename realT>
707{
708
709 return m_f[i];
710}
711
712template <typename realT>
713std::complex<realT> clGainOpt<realT>::olXfer( int fi )
714{
715 complexT H_dm;
716 complexT H_del;
717 complexT H_con;
718
719 return olXfer( fi, H_dm, H_del, H_con );
720}
721
722// If PRECALC_TRIG is defined, then the cosine and sine tables are pre-calculated and used instead of repeated exp(-i)
723// calls. This is much much faster, though uses more memory. In general, only undefine this for testing or debugging.
724#define PRECALC_TRIG
725
726template <typename realT>
727std::complex<realT> clGainOpt<realT>::olXfer( int fi, complexT &H_dm, complexT &H_del, complexT &H_con )
728{
729 // clang-format off
730 #ifndef ALLOW_F_ZERO
731 if( m_f[fi] <= 0 )
732 {
733 #else
734 if( m_f[fi] < 0 )
735 {
736 #endif // clang-format on
737
738 H_dm = 0;
739 H_del = 0;
740 H_con = 0;
741 return 0;
742 }
743
744#ifdef PRECALC_TRIG
746 {
747 size_t jmax = std::max( m_a.size() + 1, m_b.size() );
748
749 m_cs.resize( m_f.size(), jmax );
750 m_ss.resize( m_f.size(), jmax );
751
752 for( size_t i = 0; i < m_f.size(); ++i )
753 {
754 m_cs( i, 0 ) = 1.0;
755 m_ss( i, 0 ) = 0.0;
756
757 for( size_t j = 1; j < jmax; ++j )
758 {
759 m_cs( i, j ) = cos( math::two_pi<realT>() * m_f[i] * m_Ti * realT( j ) );
760 m_ss( i, j ) = sin( math::two_pi<realT>() * m_f[i] * m_Ti * realT( j ) );
761 }
762 }
763
764 m_trigCacheChanged = false;
765 }
766#endif
767
768 if( m_changed )
769 {
770 m_H_dm.resize( m_f.size(), 0 );
771 m_H_wfs.resize( m_f.size(), 0 );
772 m_H_ma.resize( m_f.size(), 0 );
773 m_H_del.resize( m_f.size(), 0 );
774 m_H_con.resize( m_f.size(), 0 );
775
776 size_t jmax = std::min( m_a.size(), m_b.size() );
777
778 // #pragma omp parallel for
779 for( size_t i = 0; i < m_f.size(); ++i )
780 {
781 // clang-format off
782 #ifndef ALLOW_F_ZERO
783 if( m_f[i] <= 0 )
784 {
785 continue;
786 }
787 #else
788 if( m_f[i] < 0 )
789 {
790 continue;
791 }
792 #endif // clang-format on
793
794 complexT s = complexT( 0.0, math::two_pi<realT>() * m_f[i] );
795
796 complexT expsT = exp( -s * m_Ti );
797
798 if( m_f[i] == 0 )
799 {
800 m_H_dm[i] = std::complex<realT>( 1, 0 );
801 }
802 else
803 {
804 m_H_dm[i] = ( realT( 1 ) - expsT ) / ( s * m_Ti );
805 }
806
807 m_H_wfs[i] = m_H_dm[i];
808
809 m_H_ma[i] = 1; // realT(1./m_N)*(realT(1) - pow(expsT,m_N))/(realT(1) - expsT);
810
811 m_H_del[i] = exp( -s * m_tau );
812
813 complexT FIR = complexT( m_b[0], 0 );
814
815 complexT IIR = complexT( 0.0, 0.0 );
816 for( size_t j = 1; j < jmax; ++j )
817 {
818#ifdef PRECALC_TRIG
819 realT cs = m_cs( i, j );
820 realT ss = m_ss( i, j );
821 FIR += m_b[j] * complexT( cs, -ss );
822 IIR += m_remember * m_a[j - 1] * complexT( cs, -ss );
823#else
824 complexT expZ = exp( -s * m_Ti * realT( j ) );
825 FIR += m_b[j] * expZ;
826 IIR += m_remember * m_a[j - 1] * expZ;
827#endif
828 }
829
830 for( size_t jj = jmax; jj < m_a.size() + 1; ++jj )
831 {
832 // clang-format off
833 #ifdef PRECALC_TRIG
834 realT cs = m_cs( i, jj );
835 realT ss = m_ss( i, jj );
836 IIR += m_remember * m_a[jj - 1] * complexT( cs, -ss );
837 #else
838 complexT expZ = exp( -s * m_Ti * realT( jj ) );
839 IIR += m_remember * m_a[jj - 1] * expZ;
840 #endif // clang-format on
841 }
842
843 for( size_t jj = jmax; jj < m_b.size(); ++jj )
844 {
845#ifdef PRECALC_TRIG
846 realT cs = m_cs( i, jj );
847 realT ss = m_ss( i, jj );
848 FIR += m_b[jj] * complexT( cs, -ss );
849#else
850 complexT expZ = exp( -s * m_Ti * realT( jj ) );
851 FIR += m_b[jj] * expZ;
852#endif
853 }
854
855 m_H_con[i] = FIR / ( realT( 1.0 ) - IIR );
856
857 /*if( i == 0 || i == 1)
858 {
859 std::cerr << i << " " << m_f[fi] << " " << s << " " << expsT << " " << m_H_wfs[i] << " " << m_H_dm[i] <<
860 " "
861 << m_H_con[i] << " " << m_H_del[i] << "\n";
862 //exit(0);
863 }*/
864 }
865
866 m_changed = false;
867 }
868
869 H_dm = m_H_dm[fi];
870 H_del = m_H_del[fi]; //*m_H_ma[fi];
871 H_con = m_H_con[fi];
872
873 return ( m_H_dm[fi] * m_H_wfs[fi] * m_H_del[fi] * m_H_con[fi] );
874}
875
876template <typename realT>
877std::complex<realT> clGainOpt<realT>::clETF( int fi, realT g )
878{
879#ifndef ALLOW_F_ZERO
880 if( m_f[fi] <= 0 )
881 return 0;
882#else
883 if( m_f[fi] < 0 )
884 return 0;
885#endif
886
887 return ( realT( 1 ) / ( realT( 1 ) + g * olXfer( fi ) ) );
888}
889
890template <typename realT>
892{
893#ifndef ALLOW_F_ZERO
894 if( m_f[fi] <= 0 )
895 return 0;
896#else
897 if( m_f[fi] < 0 )
898 return 0;
899#endif
900
901 return std::arg( ( realT( 1 ) / ( realT( 1 ) + g * olXfer( fi ) ) ) );
902}
903
904template <typename realT>
906{
907#ifndef ALLOW_F_ZERO
908 if( m_f[fi] <= 0 )
909 return 0;
910#else
911 if( m_f[fi] < 0 )
912 return 0;
913#endif
914
915 return norm( realT( 1 ) / ( realT( 1 ) + g * olXfer( fi ) ) );
916}
917
918template <typename realT>
919std::complex<realT> clGainOpt<realT>::clNTF( int fi, realT g )
920{
921#ifndef ALLOW_F_ZERO
922 if( m_f[fi] <= 0 )
923 return 0;
924#else
925 if( m_f[fi] < 0 )
926 return 0;
927#endif
928
929 complexT H_dm, H_del, H_con;
930
931 complexT olX = olXfer( fi, H_dm, H_del, H_con ); // H_dm*H_wfs*H_ma*H_del*H_con;
932
933 return -( H_dm * H_del * g * H_con ) / ( realT( 1 ) + g * olX );
934}
935
936template <typename realT>
938{
939#ifndef ALLOW_F_ZERO
940 if( m_f[fi] <= 0 )
941 return 0;
942#else
943 if( m_f[fi] < 0 )
944 return 0;
945#endif
946
947 complexT H_dm, H_del, H_con;
948
949 complexT olX = olXfer( fi, H_dm, H_del, H_con ); // H_dm*H_wfs*H_ma*H_del*H_con;
950
951 complexT NTF = -( H_dm * H_del * g * H_con ) / ( realT( 1 ) + g * olX );
952
953 return norm( NTF );
954}
955
956template <typename realT>
957void clGainOpt<realT>::clTF2( realT &ETF, realT &NTF, int fi, realT g )
958{
959#ifndef ALLOW_F_ZERO
960 if( m_f[fi] <= 0 )
961#else
962 if( m_f[fi] < 0 )
963#endif
964 {
965 ETF = 0;
966 NTF = 0;
967 return;
968 }
969
970 complexT H_dm, H_del, H_con;
971
972 complexT olX = olXfer( fi, H_dm, H_del, H_con ); // H_dm*H_wfs*H_ma*H_del*H_con;
973
974 if( m_f[fi] == 0 )
975 {
976 }
977
978 ETF = norm( realT( 1 ) / ( realT( 1 ) + g * olX ) );
979 NTF = norm( -( H_dm * H_del * g * H_con ) / ( realT( 1 ) + g * olX ) );
980
981 /*if(m_f[fi] == 0)
982 {
983 std::cerr << "ETF: " << ETF << " NTF: " << NTF << "\n";
984 }*/
985}
986
987template <typename realT>
989 realT &varErr, realT &varNoise, const std::vector<realT> &PSDerr, const std::vector<realT> &PSDnoise, realT g )
990{
991 if( m_f.size() != PSDerr.size() || m_f.size() != PSDnoise.size() )
992 {
993 std::cerr << "clVariance: Frequency grid and PSDs must be same size." << std::endl;
994 return -1;
995 }
996
997 realT ETF, NTF, df;
998
999 varErr = 0;
1000 varNoise = 0;
1001
1002 df = m_f[1] - m_f[0];
1003
1004 for( size_t i = 0; i < PSDerr.size(); ++i )
1005 {
1006 if( g == 0 )
1007 {
1008 ETF = 1;
1009 NTF = 0;
1010 }
1011 else
1012 {
1013 clTF2( ETF, NTF, i, g );
1014 }
1015 varErr += ETF * PSDerr[i] * df;
1016 varNoise += NTF * PSDnoise[i] * df;
1017 }
1018
1019 return varErr + varNoise;
1020}
1021
1022template <typename realT>
1023realT clGainOpt<realT>::clVariance( const std::vector<realT> &PSDerr, const std::vector<realT> &PSDnoise, realT g )
1024{
1025 realT varErr;
1026 realT varNoise;
1027
1028 return clVariance( varErr, varNoise, PSDerr, PSDnoise, g );
1029}
1030
1031template <typename realT>
1033{
1034 maxStableGainReport localReport;
1035 maxStableGainReport &activeReport = report == nullptr ? localReport : *report;
1036 activeReport = {};
1037 gain = std::numeric_limits<realT>::quiet_NaN();
1038
1039 if( m_f.size() < 2 )
1040 {
1042 return error_t::sizeerr;
1043 }
1044
1045 for( size_t index = 0; index < m_f.size(); ++index )
1046 {
1047 if( !math::isFinite( m_f[index] ) || m_f[index] < 0 || ( index > 0 && m_f[index] <= m_f[index - 1] ) )
1048 {
1050 return error_t::invalidarg;
1051 }
1052 }
1053
1054 std::vector<realT> re, im;
1055
1056 nyquist( re, im, 1.0 );
1057
1058 for( size_t index = 0; index < re.size(); ++index )
1059 {
1060 if( !math::isFinite( re[index] ) || !math::isFinite( im[index] ) )
1061 {
1063 return error_t::error;
1064 }
1065 }
1066
1067 bool crossingFound = false;
1068 for( size_t index = 0; index + 1 < re.size(); ++index )
1069 {
1070 if( !( im[index] < 0 && im[index + 1] >= 0 ) )
1071 {
1072 continue;
1073 }
1074
1075 const realT fraction = -im[index] / ( im[index + 1] - im[index] );
1076 const realT crossingReal = re[index] + fraction * ( re[index + 1] - re[index] );
1077 const realT crossingGain = -realT( 1 ) / crossingReal;
1078 if( crossingReal >= 0 || !math::isFinite( crossingGain ) || crossingGain < m_maxFindMin )
1079 {
1080 continue;
1081 }
1082
1083 if( crossingFound && crossingReal >= activeReport.crossingReal )
1084 {
1085 continue;
1086 }
1087
1088 crossingFound = true;
1089 activeReport.lowerIndex = index;
1090 activeReport.upperIndex = index + 1;
1091 activeReport.lowerFrequency = m_f[index];
1092 activeReport.upperFrequency = m_f[index + 1];
1093 activeReport.crossingFrequency = m_f[index] + fraction * ( m_f[index + 1] - m_f[index] );
1094 activeReport.crossingReal = crossingReal;
1095 activeReport.gain = crossingGain;
1096 }
1097
1098 if( !crossingFound )
1099 {
1101 return error_t::notfound;
1102 }
1103
1105 gain = activeReport.gain;
1106 return error_t::noerror;
1107}
1108
1109// Implement the minimization, allowing pre-compiled specializations
1110namespace impl
1111{
1112
1113template <typename realT>
1114/// Minimize an open-loop variance objective on a bounded gain interval.
1115mx::error_t optGainOpenLoop( realT &gain, ///< [out] best gain estimate
1116 realT &var, ///< [out] variance at the best gain estimate
1117 clGainOptOptGain_OL<realT> &olgo, ///< [in,out] variance objective and diagnostics
1118 const realT &minimumGain, ///< [in] lower gain bound
1119 const realT &maximumGain, ///< [in] upper gain bound
1120 int minFindBits, ///< [in] requested precision in binary digits
1121 uintmax_t minFindMaxIter, ///< [in] maximum minimizer iterations
1122 uintmax_t &iters ///< [out] minimizer iterations used
1123)
1124{
1125#ifdef MX_INCLUDE_BOOST
1126 gain = std::numeric_limits<realT>::quiet_NaN();
1127 var = std::numeric_limits<realT>::quiet_NaN();
1128
1129 try
1130 {
1131 std::pair<realT, realT> brack;
1132 brack = boost::math::tools::brentm_findm_minima<clGainOptOptGain_OL<realT>, realT>( olgo,
1133 minimumGain,
1134 maximumGain,
1135 minFindBits,
1136 minFindMaxIter,
1137 iters );
1138 gain = brack.first;
1139 var = brack.second;
1140 }
1141 catch( ... )
1142 {
1143 return error_t::exception;
1144 }
1145
1146 if( iters >= minFindMaxIter )
1147 {
1148 return error_t::timeout;
1149 }
1150
1151 return error_t::noerror;
1152#else
1153 static_assert( std::is_fundamental<realT>::value || !std::is_fundamental<realT>::value,
1154 "impl::optGainOpenLoop<realT> is not specialized for type realT, and MX_INCLUDE_BOOST is not "
1155 "defined, so I can't just use boost." );
1156 return error_t::notimpl;
1157#endif
1158}
1159
1160template <>
1161/// Float specialization of the bounded open-loop gain minimizer.
1162mx::error_t optGainOpenLoop<float>( float &gain, ///< [out] best gain estimate
1163 float &var, ///< [out] variance at the best gain estimate
1164 clGainOptOptGain_OL<float> &olgo, ///< [in,out] variance objective and diagnostics
1165 const float &minimumGain, ///< [in] lower gain bound
1166 const float &maximumGain, ///< [in] upper gain bound
1167 int minFindBits, ///< [in] requested precision in binary digits
1168 uintmax_t minFindMaxIter, ///< [in] maximum minimizer iterations
1169 uintmax_t &iters ///< [out] minimizer iterations used
1170);
1171
1172template <>
1173/// Double specialization of the bounded open-loop gain minimizer.
1174mx::error_t optGainOpenLoop<double>( double &gain, ///< [out] best gain estimate
1175 double &var, ///< [out] variance at the best gain estimate
1176 clGainOptOptGain_OL<double> &olgo, ///< [in,out] variance objective and diagnostics
1177 const double &minimumGain, ///< [in] lower gain bound
1178 const double &maximumGain, ///< [in] upper gain bound
1179 int minFindBits, ///< [in] requested precision in binary digits
1180 uintmax_t minFindMaxIter, ///< [in] maximum minimizer iterations
1181 uintmax_t &iters ///< [out] minimizer iterations used
1182);
1183
1184template <>
1185/// Long-double specialization of the bounded open-loop gain minimizer.
1187optGainOpenLoop<long double>( long double &gain, ///< [out] best gain estimate
1188 long double &var, ///< [out] variance at the best gain estimate
1189 clGainOptOptGain_OL<long double> &olgo, ///< [in,out] variance objective and diagnostics
1190 const long double &minimumGain, ///< [in] lower gain bound
1191 const long double &maximumGain, ///< [in] upper gain bound
1192 int minFindBits, ///< [in] requested precision in binary digits
1193 uintmax_t minFindMaxIter, ///< [in] maximum minimizer iterations
1194 uintmax_t &iters ///< [out] minimizer iterations used
1195);
1196
1197#ifdef HASQUAD
1198template <>
1199/// Quad-precision specialization of the bounded open-loop gain minimizer.
1201optGainOpenLoop<_m_float128>( _m_float128 &gain, ///< [out] best gain estimate
1202 _m_float128 &var, ///< [out] variance at the best gain estimate
1203 clGainOptOptGain_OL<_m_float128> &olgo, ///< [in,out] variance objective and diagnostics
1204 const _m_float128 &minimumGain, ///< [in] lower gain bound
1205 const _m_float128 &maximumGain, ///< [in] upper gain bound
1206 int minFindBits, ///< [in] requested precision in binary digits
1207 uintmax_t minFindMaxIter, ///< [in] maximum minimizer iterations
1208 uintmax_t &iters ///< [out] minimizer iterations used
1209);
1210#endif
1211
1212} // namespace impl
1213
1214template <typename realT>
1216 realT &var,
1217 const std::vector<realT> &PSDerr,
1218 const std::vector<realT> &PSDnoise,
1219 bool gridSearch,
1220 optGainReport *report )
1221{
1222 maxStableGainReport stabilityReport;
1223 realT maximumGain;
1224 error_t rv = maxStableGain( maximumGain, &stabilityReport );
1225 if( rv != error_t::noerror )
1226 {
1227 gain = std::numeric_limits<realT>::quiet_NaN();
1228 var = std::numeric_limits<realT>::quiet_NaN();
1229 if( report != nullptr )
1230 {
1231 *report = {};
1233 report->stability = stabilityReport;
1234 }
1235 return rv;
1236 }
1237
1238 optGainReport optimizationReport;
1239 rv = optGainOpenLoop( gain, var, PSDerr, PSDnoise, maximumGain, gridSearch, &optimizationReport );
1240 optimizationReport.stability = stabilityReport;
1241 if( report != nullptr )
1242 {
1243 *report = optimizationReport;
1244 }
1245 return rv;
1246}
1247
1248template <typename realT>
1250 realT &var,
1251 const std::vector<realT> &PSDerr,
1252 const std::vector<realT> &PSDnoise,
1253 realT maximumGain,
1254 bool gridSearch,
1255 optGainReport *report )
1256{
1257 optGainReport localReport;
1258 optGainReport &activeReport = report == nullptr ? localReport : *report;
1259 activeReport = {};
1260 activeReport.requestedMaximumGain = maximumGain;
1261 gain = std::numeric_limits<realT>::quiet_NaN();
1262 var = std::numeric_limits<realT>::quiet_NaN();
1263
1264 if( m_f.size() < 2 || PSDerr.size() != m_f.size() || PSDnoise.size() != m_f.size() )
1265 {
1266 activeReport.status = optGainStatus::invalidInput;
1267 return error_t::sizeerr;
1268 }
1269
1270 if( !math::isFinite( maximumGain ) || maximumGain <= 0 || !math::isFinite( m_minFindMin ) || m_minFindMin < 0 ||
1271 !math::isFinite( m_minFindMaxFact ) || m_minFindMaxFact <= 0 || m_minFindMaxFact > 1 || m_minFindBits <= 0 ||
1272 m_minFindMaxIter == 0 )
1273 {
1274 activeReport.status = optGainStatus::invalidInput;
1276 }
1277
1278 const realT requestedMinimum = m_minFindMin;
1279 const realT requestedMaximum = m_minFindMaxFact * maximumGain;
1280 if( !math::isFinite( requestedMaximum ) || requestedMaximum <= requestedMinimum )
1281 {
1282 activeReport.status = optGainStatus::invalidInput;
1284 }
1285
1287 olgo.go = this;
1288 olgo.PSDerr = &PSDerr;
1289 olgo.PSDnoise = &PSDnoise;
1290
1291 realT minimumGain = requestedMinimum;
1292 realT maximumSearchGain = requestedMaximum;
1293 bool searchBoundarySelected = false;
1294
1295 if( gridSearch )
1296 {
1297 const realT gainStep = std::min( realT( 0.05 ), requestedMaximum - requestedMinimum );
1298 realT currentGain = requestedMaximum;
1299 realT minimumVariance = olgo( currentGain );
1300 realT gainAtMinimum = currentGain;
1301
1302 while( currentGain > requestedMinimum )
1303 {
1304 const realT nextGain = std::max( requestedMinimum, currentGain - gainStep );
1305 if( nextGain >= currentGain )
1306 {
1307 break;
1308 }
1309
1310 currentGain = nextGain;
1311 const realT candidateVariance = olgo( currentGain );
1312
1313 if( candidateVariance < minimumVariance )
1314 {
1315 minimumVariance = candidateVariance;
1316 gainAtMinimum = currentGain;
1317 }
1318 }
1319
1320 minimumGain = std::max( requestedMinimum, gainAtMinimum - gainStep );
1321 maximumSearchGain = std::min( requestedMaximum, gainAtMinimum + gainStep );
1322 searchBoundarySelected = gainAtMinimum == requestedMinimum || gainAtMinimum == requestedMaximum;
1323 }
1324
1325 activeReport.searchMinimumGain = minimumGain;
1326 activeReport.searchMaximumGain = maximumSearchGain;
1327
1328 uintmax_t iterations = m_minFindMaxIter;
1329 error_t rv = impl::optGainOpenLoop( gain,
1330 var,
1331 olgo,
1332 minimumGain,
1333 maximumSearchGain,
1336 iterations );
1337
1338 activeReport.iterations = iterations;
1339 activeReport.evaluations = olgo.evaluations;
1340 activeReport.minimumEvaluatedGain = olgo.minimumEvaluatedGain;
1341 activeReport.maximumEvaluatedGain = olgo.maximumEvaluatedGain;
1342 activeReport.gain = gain;
1343 activeReport.variance = var;
1344
1345 if( rv == error_t::timeout )
1346 {
1348 return rv;
1349 }
1350
1351 if( rv != error_t::noerror || !math::isFinite( gain ) || !math::isFinite( var ) )
1352 {
1354 gain = std::numeric_limits<realT>::quiet_NaN();
1355 var = std::numeric_limits<realT>::quiet_NaN();
1356 activeReport.gain = gain;
1357 activeReport.variance = var;
1358 return rv == error_t::noerror ? error_t::error : rv;
1359 }
1360
1361 if( searchBoundarySelected || gain <= minimumGain || gain >= maximumSearchGain )
1362 {
1364 }
1365 else
1366 {
1367 activeReport.status = optGainStatus::converged;
1368 }
1369
1370 return error_t::noerror;
1371}
1372
1373template <typename realT>
1374int clGainOpt<realT>::pseudoOpenLoop( std::vector<realT> &PSD, realT g )
1375{
1376 realT e;
1377 for( int f = 0; f < m_f.size(); ++f )
1378 {
1379 e = clETF2( f, g );
1380
1381 if( e > 0 )
1382 PSD[f] = PSD[f] / e;
1383 }
1384
1385 return 0;
1386}
1387
1388template <typename realT>
1389int clGainOpt<realT>::nyquist( std::vector<realT> &re, std::vector<realT> &im, realT g )
1390{
1391 re.resize( m_f.size() );
1392 im.resize( m_f.size() );
1393
1394 complexT etf;
1395
1396 for( size_t f = 0; f < m_f.size(); ++f )
1397 {
1398 etf = g * olXfer( f ); // clETF(f, g);
1399 re[f] = real( etf );
1400 im[f] = imag( etf );
1401 }
1402
1403 return 0;
1404}
1405
1406//------------ Workers ---------------------
1407
1408/// Bisection worker struct for finding optimum closed loop gain from open loop PSDs
1409template <typename realT>
1411{
1412 clGainOpt<realT> *go{ nullptr }; ///< Gain optimizer used to evaluate variance.
1413 const std::vector<realT> *PSDerr{ nullptr }; ///< Open-loop disturbance PSD.
1414 const std::vector<realT> *PSDnoise{ nullptr }; ///< Measurement-noise PSD.
1415 size_t evaluations{ 0 }; ///< Objective evaluations performed.
1416 realT minimumEvaluatedGain{ std::numeric_limits<realT>::max() }; ///< Smallest gain evaluated.
1417 realT maximumEvaluatedGain{ std::numeric_limits<realT>::lowest() }; ///< Largest gain evaluated.
1418
1419 /// Evaluate closed-loop variance at a candidate gain and update diagnostics.
1420 realT operator()( const realT &g /**< [in] candidate gain */ )
1421 {
1422 ++evaluations;
1425 return go->clVariance( *PSDerr, *PSDnoise, g );
1426 }
1427};
1428
1429// Explicit Instantiation
1430extern template class clGainOpt<float>;
1431
1432extern template class clGainOpt<double>;
1433
1434extern template class clGainOpt<long double>;
1435
1436#ifdef HASQUAD
1437extern template class clGainOpt<_m_float128>;
1438#endif
1439
1440} // namespace analysis
1441} // namespace AO
1442} // namespace mx
1443
1444#endif // clGainOpt_hpp
mx::error_t optGainOpenLoop(realT &gain, realT &var, clGainOptOptGain_OL< realT > &olgo, const realT &minimumGain, const realT &maximumGain, int minFindBits, uintmax_t minFindMaxIter, uintmax_t &iters)
Minimize an open-loop variance objective on a bounded gain interval.
The mxlib error_t type and utilities.
Floating-point classification utilities that remain reliable under fast-math optimization.
error_t
The mxlib error codes.
Definition error_t.hpp:26
@ notimpl
A component or technique is not implemented.
Definition error_t.hpp:31
@ noerror
No error has occurred.
Definition error_t.hpp:27
@ sizeerr
A size was invalid or calculated incorrectly.
Definition error_t.hpp:35
@ exception
An exception was thrown.
Definition error_t.hpp:51
@ timeout
A timeout occurred.
Definition error_t.hpp:49
@ invalidconfig
A config setting was invalid.
Definition error_t.hpp:30
@ invalidarg
An argument was invalid.
Definition error_t.hpp:29
@ notfound
An item was not found.
Definition error_t.hpp:34
@ error
A general error has occurred.
Definition error_t.hpp:28
bool isFinite(realT value)
Test whether a floating-point value is finite, including under finite-math-only optimization.
constexpr T two_pi()
Get the value of 2pi.
The mxlib c++ namespace.
Definition mxlib.hpp:37
Bisection worker struct for finding optimum closed loop gain from open loop PSDs.
realT maximumEvaluatedGain
Largest gain evaluated.
size_t evaluations
Objective evaluations performed.
realT minimumEvaluatedGain
Smallest gain evaluated.
const std::vector< realT > * PSDnoise
Measurement-noise PSD.
realT operator()(const realT &g)
Evaluate closed-loop variance at a candidate gain and update diagnostics.
clGainOpt< realT > * go
Gain optimizer used to evaluate variance.
const std::vector< realT > * PSDerr
Open-loop disturbance PSD.
Diagnostic summary of a maximum-stable-gain search.
Definition clGainOpt.hpp:84
realT lowerFrequency
Frequency below the crossing.
Definition clGainOpt.hpp:88
realT crossingFrequency
Interpolated crossing frequency.
Definition clGainOpt.hpp:90
maxStableGainStatus status
Search termination state.
Definition clGainOpt.hpp:85
realT crossingReal
Interpolated real Nyquist value.
Definition clGainOpt.hpp:91
realT upperFrequency
Frequency above the crossing.
Definition clGainOpt.hpp:89
size_t upperIndex
Index above the selected crossing.
Definition clGainOpt.hpp:87
realT gain
Maximum stable gain at the crossing.
Definition clGainOpt.hpp:92
size_t lowerIndex
Index below the selected crossing.
Definition clGainOpt.hpp:86
Diagnostic summary of an open-loop optimum-gain search.
realT maximumEvaluatedGain
Largest evaluated gain.
realT searchMinimumGain
Final minimizer lower bound.
maxStableGainReport stability
Automatic stability-search diagnostics, when requested.
size_t evaluations
Objective evaluations performed.
realT searchMaximumGain
Final minimizer upper bound.
uintmax_t iterations
Minimizer iterations attempted.
realT gain
Best gain returned by the minimizer.
realT requestedMaximumGain
Caller-supplied gain limit.
optGainStatus status
Search termination state.
realT minimumEvaluatedGain
Smallest evaluated gain.
realT variance
Variance at the best gain.
A class to manage optimizing closed-loop gains.
Definition clGainOpt.hpp:69
void init()
Initialize this instance.
void a(const std::vector< realT > &newA)
Set the vector of IIR 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.
bool m_trigCacheChanged
True when frequency, sampling interval, or required controller tap count invalidates m_cs and m_ss.
std::vector< realT > m_f
Vector of frequencies.
void b(const std::vector< realT > &newB)
Set the vector of FIR coefficients.
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.
realT m_tau
The loop delay.
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.
mx::error_t maxStableGain(realT &gain, maxStableGainReport *report=nullptr)
Find the maximum stable gain for the loop parameters.
bool m_changed
True if any of the members which make up the basic transfer functions are changed.
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.
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:70
void f(realT *newF, size_t nF)
Set the vector of frequencies.
std::complex< _realT > complexT
The complex data type.
Definition clGainOpt.hpp:71
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.
realT m_Ti
The loop sampling interval.
optGainStatus
Termination state of an open-loop optimum-gain search.
Definition clGainOpt.hpp:97
@ boundaryLimited
The reported minimum lies on a search boundary.
@ notRun
No search has been attempted.
Definition clGainOpt.hpp:98
@ invalidInput
The PSDs, search controls, or requested interval were invalid.
@ converged
The minimizer converged inside the search interval.
Definition clGainOpt.hpp:99
@ stabilityFailure
The automatic maximum-stable-gain search failed.
@ iterationLimit
The minimizer exhausted its iteration limit.
@ calculationFailure
The minimizer threw or returned invalid output.
const std::vector< realT > & a()
Get the vector of IIR coefficients.
maxStableGainStatus
Termination state of a maximum-stable-gain search.
Definition clGainOpt.hpp:75
@ crossingFound
A qualifying Nyquist crossing was found.
Definition clGainOpt.hpp:77
@ noCrossing
No qualifying Nyquist crossing was found.
Definition clGainOpt.hpp:79
@ notRun
No search has been attempted.
Definition clGainOpt.hpp:76
@ invalidInput
The frequency grid or derived Nyquist values were invalid.
Definition clGainOpt.hpp:78
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.
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.
mx::error_t optGainOpenLoop(realT &gain, realT &var, const std::vector< realT > &PSDerr, const std::vector< realT > &PSDnoise, bool gridSearch, optGainReport *report=nullptr)
Return the optimum closed loop gain given an open loop PSD.
Utilities for working with time.