Controlpp
Loading...
Searching...
No Matches
math.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <Eigen/Core>
4#include <Eigen/Eigenvalues>
5
6#include <cstdint>
7
8namespace controlpp{
9
17 template<class T>
18 constexpr T product_over(T from, T to){
19 unsigned long product = 1;
20 for(; from < to; ++from){
21 product *= from;
22 }
23 return product;
24 }
25
29 template<class T>
30 constexpr T pow(const T& base, const int& exp){
31 T result = static_cast<T>(1);
32 if(exp >= 0){
33 for(int i = 0; i < exp; ++i){
34 result *= base;
35 }
36 }else{
37 for(int i = 0; i < -exp; ++i){
38 result /= base;
39 }
40 }
41 return result;
42 }
43
54 template<class T, int N>
55 Eigen::Vector<T, N> unwrap(const Eigen::Vector<T, N>& y, const T& modulo){
56 Eigen::Vector<T, N> result;
57 if constexpr (N == Eigen::Dynamic) result.resize(y.size());
58 T offset = 0;
59 result(0) = y(0);
60 for(int i = 1; i < y.size(); ++i){
61 while(((y(i) + offset) - result(i-1)) > (modulo/2)){
62 offset -= modulo;
63 }
64 while(((y(i) + offset) - result(i-1)) < (-modulo/2)){
65 offset += modulo;
66 }
67 result(i) = y(i) + offset;
68 }
69 return result;
70 }
71
79 template<class T, int N>
80 Eigen::Vector<T, N> unwrap_rad(const Eigen::Vector<T, N>& phases){
81 const T modulo = std::numbers::pi_v<T> * static_cast<T>(2);
82 return unwrap(phases, modulo);
83 }
84
92 template<class T, int N>
93 Eigen::Vector<T, N> unwrap_deg(const Eigen::Vector<T, N>& phases){
94 const T modulo = 360;
95 return unwrap(phases, modulo);
96 }
97
98
114 template<class T = double>
115 constexpr T pade_param_reccursice(T P_k, std::int32_t m, std::int32_t n, std::int32_t k){
116 const std::int64_t num = (m - k);
117 const std::int64_t den = (k + 1) * (m + n - k);
118 const double f = static_cast<T>(num) / static_cast<T>(den);
119 return P_k * f;
120 }
121
130 template<class T = double>
131 constexpr void pade_params(T* params, std::size_t size, std::uint32_t m, std::uint32_t n){
132 params[0] = T(1.0);
133 for(std::size_t k = 1; k < size; ++k){
134 params[k] = pade_param_reccursice(params[k-1], m, n, k-1);
135 }
136 }
137
164 template<class T = double>
165 constexpr T pade_num_param(std::int32_t m, std::int32_t n, std::int32_t k){
166 double P_k = 1.0;
167 for(std::int32_t i = 0; i < k; ++i){
168 P_k = pade_param_reccursice(P_k, m, n, i);
169 }
170 return P_k;
171 }
172
199 template<class T = double>
200 constexpr T pade_den_param(unsigned long m, unsigned long n, unsigned long k){
201 return pade_num_param(n, m, k);
202 }
203
204 template<class T, int Rows, int Cols, int Options, int MaxRows, int MaxCols>
205 Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols> identity_like([[maybe_unused]]const Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols>& unused){
206 if constexpr (Rows != Eigen::Dynamic && Cols != Eigen::Dynamic){
207 return Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols>::Identity();
208 }else{
209 Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols> result;
210 result.setIdentity();
211 return result;
212 }
213 }
214
215 template<class T, int LSize, int RSize>
216 Eigen::Matrix<T, LSize + RSize, LSize + RSize> join_to_diagonal(const Eigen::Vector<T, LSize>& l, const Eigen::Vector<T, RSize>& r){
217 Eigen::Matrix<T, LSize + RSize, LSize + RSize> result;
218 result.diagonal().head(LSize) = l;
219 result.diagonal().tail(RSize) = r;
220 return result;
221 }
222
223 template<class T, int LSize, int RSize>
224 requires(LSize != Eigen::Dynamic && RSize != Eigen::Dynamic)
225 Eigen::Vector<T, LSize + RSize> join_to_vector(const Eigen::Vector<T, LSize>& l, const Eigen::Vector<T, RSize>& r){
226 Eigen::Vector<T, LSize + RSize> result;
227 result.head(LSize) = l;
228 result.tail(RSize) = r;
229 return result;
230 }
231
232 template<class T, int LSize, int RSize>
233 requires(!(LSize != Eigen::Dynamic && RSize != Eigen::Dynamic))
234 Eigen::Vector<T, Eigen::Dynamic> join_to_vector(const Eigen::Vector<T, LSize>& l, const Eigen::Vector<T, RSize>& r){
235 Eigen::Vector<T, Eigen::Dynamic> result(l.size() + r.size());
236 result.head(l.size()) = l;
237 result.tail(r.size()) = r;
238 return result;
239 }
240
247 template<class T, int Rows, int Cols, int Options, int MaxRows, int MaxCols>
248 Eigen::Matrix<T, Rows-1, Cols-1> minor(const Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols>& A, size_t ex_row, size_t ex_col){
249 Eigen::Matrix<T, Rows-1, Cols-1> result;
250 size_t A_row = 0;
251 size_t A_col = 0;
252 for(size_t result_row = 0; result_row < (Rows-1); ++result_row, (void)++A_row){
253 for(size_t result_col = 0; result_col < (Cols-1); ++result_col, (void)++A_col){
254 A_row += (A_row == ex_row) ? 1 : 0;
255 A_col += (A_col == ex_col) ? 1 : 0;
256 result(result_row, result_col) = A(A_row, A_col);
257 }
258 }
259 return result;
260 }
261
262 template<class T, int Rows, int Cols, int Options, int MaxRows, int MaxCols>
263 Eigen::Matrix<T, Rows, Cols> adj(const Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols>& A){
264 Eigen::Matrix<T, Rows, Cols> result;
265 for(int row = 0; row < Rows; ++row){
266 const bool row_sign = ((row & 1) == 1); // even are positive, odd are negative
267 for(int col = 0; col < Cols; ++col){
268 const bool col_sign = ((col & 1) == 1); // even are positive, odd are negative
269 const bool value_sign = row_sign != col_sign; // sign of the value (both positive/negative: value is positive) (different signs: value is negative)
270 const auto min = minor(A, row, col);
271 const T det = min.determinant();
272 if(value_sign){
273 // negative sign
274 result(col, row) = -det;
275 }else{
276 // positive sign
277 result(col, row) = det;
278 }
279 }
280 }
281 return result;
282 }
283
313 template<class T, int N>
314 Eigen::Matrix<T, N-1, N-1> companion(const Eigen::Vector<T, N>& v){
315 Eigen::Matrix<T, N-1, N-1> result;
316 result.template block<N-2, N-2>(0, 1) = Eigen::Matrix<T, N-2, N-2>::Identity();
317 result.col(0).setZero();
318 result.row(N-2) = -v.head(N-1) / v(N-1);
319 return result;
320 }
321
341 template<class T, int N>
342 requires(N % 2 == 0)
343 Eigen::Matrix<T, N/2, N/2> hamilton_solver(
344 const Eigen::Matrix<T, N, N> H
345 ){
346 // 2. Compute stable eigenvectors
347 Eigen::ComplexEigenSolver<Eigen::Matrix<T, N, N>> ces;
348 ces.compute(H);
349 const auto& H_eigvals = ces.eigenvalues();
350 const auto& H_eigvecs = ces.eigenvectors();
351
352 // in a 2n hamilton matrix are exactly n stable ones
353 Eigen::Matrix<std::complex<T>, N, N/2> StableEigenVecs;
354 int si = 0; // stable eigenvalue iterator
355 int ei = 0; // eigen value iterator
356 for(; (si < N/2) && (ei < N); ++ei){
357 if(H_eigvals(ei).real() <= 0){// only stable ones
358 StableEigenVecs.col(si) = H_eigvecs.col(ei);
359 ++si;
360 }
361 }
362
363 // 3. Repartition
364 const auto TopPartition = StableEigenVecs.template block<N/2, N/2>(0, 0);
365 const auto BottomPartition = StableEigenVecs.template block<N/2, N/2>(N/2, 0);
366
367 // 4. Recover Solution (BottomPartition * TopPartition^-1)
368 const Eigen::Matrix<std::complex<T>, N/2, N/2> X = TopPartition.transpose().partialPivLu().solve(BottomPartition.transpose()).transpose();
369
370 // The result is real, make sure it is real because it should be
371 const Eigen::Matrix<T, N/2, N/2> realX = X.real();
372
373 // force the result X to be symetric to combat small numerical errors
374 const Eigen::Matrix<T, N/2, N/2> result = static_cast<T>(0.5) * (realX + realX.transpose());
375 return result;
376 }
377
397 template<class T, int N>
398 Eigen::Matrix<T, N/2, N/2> symplectic_solver(
399 const Eigen::Matrix<T, N, N> S
400 ){
401 // 2. Compute stable eigenvectors
402 Eigen::ComplexEigenSolver<Eigen::Matrix<T, N, N>> ces;
403 ces.compute(S);
404 const auto& eigvals = ces.eigenvalues();
405 const auto& eigvecs = ces.eigenvectors();
406
407 // in a 2n hamilton matrix are exactly n stable ones
408 Eigen::Matrix<std::complex<T>, N, N/2> StableEigenVecs;
409 StableEigenVecs.setZero();
410 int si = 0; // stable eigenvalue iterator
411 int ei = 0; // eigen value iterator
412 for(; (si < N/2) && (ei < N); ++ei){
413 if(std::abs(eigvals(ei)) <= 1){// only stable ones
414 StableEigenVecs.col(si) = eigvecs.col(ei);
415 ++si;
416 }
417 }
418
419 // 3. Repartition
420 const auto TopPartition = StableEigenVecs.template block<N/2, N/2>(0, 0);
421 const auto BottomPartition = StableEigenVecs.template block<N/2, N/2>(N/2, 0);
422
423 // 4. Recover Solution (BottomPartition * TopPartition^-1)
424 const Eigen::Matrix<std::complex<T>, N/2, N/2> X = TopPartition.transpose().partialPivLu().solve(BottomPartition.transpose()).transpose();
425
426 // The result is real, make sure it is real because it should be
427 const Eigen::Matrix<T, N/2, N/2> realX = X.real();
428
429 // force the result X to be symetric to combat small numerical errors
430 const Eigen::Matrix<T, N/2, N/2> result = static_cast<T>(0.5) * (realX + realX.transpose());
431
432 return result;
433 }
434
465 template<class T, int NStates,
466 int AOpt, int AMaxR, int AMaxC,
467 int QOpt, int QMaxR, int QMaxC
468 >
469 Eigen::Matrix<T, NStates, NStates> lyapunov_solver(
470 const Eigen::Matrix<T, NStates, NStates, AOpt, AMaxR, AMaxC>& A,
471 const Eigen::Matrix<T, NStates, NStates, QOpt, QMaxR, QMaxC>& Q
472 ){
473 // 1. Build the hamilton matrix
474 Eigen::Matrix<T, 2*NStates, 2*NStates> H;
475 H.topLeftCorner(NStates, NStates) = A;
476 H.topRightCorner(NStates, NStates).setZero();
477 H.bottomLeftCorner(NStates, NStates) = - Q;
478 H.bottomRightCorner(NStates, NStates) = -A.transpose();
479 return hamilton_solver(H);
480 }
481
506 template<class ValueType, int NStates, int NInputs, int NOutputs>
507 std::tuple<Eigen::Matrix<ValueType, NStates, NStates>, Eigen::Matrix<ValueType, NStates, NStates>> energy_scaling_matrix(
508 const Eigen::Matrix<ValueType, NStates, NStates> A,
509 const Eigen::Matrix<ValueType, NStates, NInputs> B,
510 const Eigen::Matrix<ValueType, NOutputs, NStates> C
511 ){
512 // Calculate the two gramians Wc and Wo
513 const Eigen::Matrix<ValueType, NStates, NStates> Bsqr = B * B.transpose();
514 const Eigen::Matrix<ValueType, NStates, NStates> Wc = lyapunov_solver(A.transpose(), Bsqr);
515
516 const Eigen::Matrix<ValueType, NStates, NStates> Csqr = C.transpose() * C;
517 const Eigen::Matrix<ValueType, NStates, NStates> Wo = lyapunov_solver(A, Csqr);
518
519 // force symetry and add some to the diagonal for numerical stability
520 const ValueType eps = std::numeric_limits<ValueType>::epsilon();
521
522 Eigen::Matrix<ValueType, NStates, NStates> Wc_sym = (Wc + Wc.transpose()) * static_cast<ValueType>(0.5);
523 {
524 ValueType jitter = std::max(ValueType(1), Wc_sym.diagonal().cwiseAbs().maxCoeff());
525 Wc_sym.diagonal().array() += jitter * eps;
526 }
527 Eigen::Matrix<ValueType, NStates, NStates> Wo_sym = (Wo + Wo.transpose()) * static_cast<ValueType>(0.5);
528 {
529 ValueType jitter = std::max(ValueType(1), Wo_sym.diagonal().cwiseAbs().maxCoeff());
530 Wo_sym.diagonal().array() += jitter * eps;
531 }
532
533 // calculate the cholesky factors using LLT
534 const Eigen::LLT<Eigen::Matrix<ValueType, NStates, NStates>> Wc_colesky(Wc_sym);
535 const Eigen::LLT<Eigen::Matrix<ValueType, NStates, NStates>> Wo_colesky(Wo_sym);
536
537 // partition into triangle forms
538 const Eigen::Matrix<ValueType, NStates, NStates> Rc = Wc_colesky.matrixU();
539 const Eigen::Matrix<ValueType, NStates, NStates> Ro = Wo_colesky.matrixL();
540
541 // calculate the SVD
542 const Eigen::Matrix<ValueType, NStates, NStates> M = Ro * Rc;
543 const Eigen::JacobiSVD<Eigen::Matrix<ValueType, NStates, NStates>> svd(M, Eigen::ComputeThinU | Eigen::ComputeThinV);
544 const Eigen::Matrix<ValueType, NStates, NStates> U = svd.matrixU();
545 const Eigen::Matrix<ValueType, NStates, 1> s = svd.singularValues();
546 const Eigen::Matrix<ValueType, NStates, NStates> V = svd.matrixV();
547
548
549 const Eigen::Matrix<ValueType, NStates, 1> s_sqrt = (s.array().max(eps)).sqrt().matrix();
550 const Eigen::Matrix<ValueType, NStates, NStates> S_sqrt = s_sqrt.asDiagonal();
551
552 // Finally!!! we can calculate the resulting transformation matrices
553 const Eigen::Matrix<ValueType, NStates, NStates> T = Rc.transpose().template triangularView<Eigen::Lower>().solve((S_sqrt * V.transpose()).transpose()).transpose();
554 const Eigen::Matrix<ValueType, NStates, NStates> T_inverse = Ro.template triangularView<Eigen::Lower>().solve(U * S_sqrt);
555
556 return {T, T_inverse};
557 }
558
559
560
602 template<class T, int NStates, int NInputs,
603 int AOpt, int AMaxR, int AMaxC,
604 int BOpt, int BMaxR, int BMaxC,
605 int ROpt, int RMaxR, int RMaxC,
606 int QOpt, int QMaxR, int QMaxC
607 >
608 Eigen::Matrix<T, NStates, NStates> care_solver(
609 const Eigen::Matrix<T, NStates, NStates, AOpt, AMaxR, AMaxC>& A,
610 const Eigen::Matrix<T, NStates, NInputs, BOpt, BMaxR, BMaxC>& B,
611 const Eigen::Matrix<T, NStates, NStates, QOpt, QMaxR, QMaxC>& Q,
612 const Eigen::Matrix<T, NInputs, NInputs, ROpt, RMaxR, RMaxC>& R
613 ){
614 Eigen::Matrix<T, 2*NStates, 2*NStates> H;
615 H.topLeftCorner(NStates, NStates) = A;
616 H.topRightCorner(NStates, NStates) = -B * R.ldlt().solve(B.transpose());
617 H.bottomLeftCorner(NStates, NStates) = -Q;
618 H.bottomRightCorner(NStates, NStates) = -A;
619 return hamilton_solver(H);
620 }
621
662 template<class T, int NStates, int NInputs,
663 int AOpt, int AMaxR, int AMaxC,
664 int BOpt, int BMaxR, int BMaxC,
665 int ROpt, int RMaxR, int RMaxC,
666 int QOpt, int QMaxR, int QMaxC
667 >
668 Eigen::Matrix<T, NStates, NStates> dare_solver(
669 const Eigen::Matrix<T, NStates, NStates, AOpt, AMaxR, AMaxC>& A,
670 const Eigen::Matrix<T, NStates, NInputs, BOpt, BMaxR, BMaxC>& B,
671 const Eigen::Matrix<T, NStates, NStates, QOpt, QMaxR, QMaxC>& Q,
672 const Eigen::Matrix<T, NInputs, NInputs, ROpt, RMaxR, RMaxC>& R
673 ){
674 const Eigen::Matrix<T, NStates, NStates> brb = B * R.llt().solve(B.transpose());
675 Eigen::ColPivHouseholderQR<Eigen::Matrix<T, NStates, NStates, AOpt, AMaxR, AMaxC>> At_qr(A.transpose());
676 const Eigen::Matrix<T, NStates, NStates> aq = At_qr.solve(Q);
677 Eigen::Matrix<T, 2*NStates, 2*NStates> S;
678 S.topLeftCorner(NStates, NStates) = A + brb * aq;
679 S.topRightCorner(NStates, NStates) = -A.colPivHouseholderQr().solve(brb.transpose()).transpose();
680 S.bottomLeftCorner(NStates, NStates) = - aq;
681 S.bottomRightCorner(NStates, NStates) = At_qr.solve(identity_like(A));
682 return symplectic_solver(S);
683 }
684
727 template<class T, int NStates, int NInputs,
728 int AOpt, int AMaxR, int AMaxC,
729 int BOpt, int BMaxR, int BMaxC,
730 int ROpt, int RMaxR, int RMaxC,
731 int QOpt, int QMaxR, int QMaxC,
732 int NOpt, int NMaxR, int NMaxC
733 >
734 Eigen::Matrix<T, NStates, NStates> care_solver(
735 const Eigen::Matrix<T, NStates, NStates, AOpt, AMaxR, AMaxC>& A,
736 const Eigen::Matrix<T, NStates, NInputs, BOpt, BMaxR, BMaxC>& B,
737 const Eigen::Matrix<T, NStates, NStates, QOpt, QMaxR, QMaxC>& Q,
738 const Eigen::Matrix<T, NInputs, NInputs, ROpt, RMaxR, RMaxC>& R,
739 const Eigen::Matrix<T, NStates, NInputs, NOpt, NMaxR, NMaxC>& N
740 ){
741 // 1. Build the hamilton matrix
742 const Eigen::Matrix<T, NInputs, NStates> R_inv_DT_C = R.ldlt().solve((N.transpose()));
743 const Eigen::Matrix<T, NStates, NStates> A_ = A - B * R_inv_DT_C;
744
745 Eigen::Matrix<T, 2*NStates, 2*NStates> H;
746 H.topLeftCorner(NStates, NStates) = A_;
747 H.topRightCorner(NStates, NStates) = - B * R.ldlt().solve(B.transpose());
748 H.bottomLeftCorner(NStates, NStates) = - (Q - N * R_inv_DT_C);
749 H.bottomRightCorner(NStates, NStates) = -A_.transpose();
750 return hamilton_solver(H);
751 }
752
753}
Eigen::Vector< T, Eigen::Dynamic > phases(const Bode< T > &bode)
Creates a vector of phases in rad.
Definition Bode.hpp:572
Definition Polynom.hpp:1012
The main namespace for the Control++ library.
Definition Bode.cpp:3
constexpr T product_over(T from, T to)
Calculates the product of all numbers in the closed open range [from, to)
Definition math.hpp:18
std::tuple< Eigen::Matrix< ValueType, NStates, NStates >, Eigen::Matrix< ValueType, NStates, NStates > > energy_scaling_matrix(const Eigen::Matrix< ValueType, NStates, NStates > A, const Eigen::Matrix< ValueType, NStates, NInputs > B, const Eigen::Matrix< ValueType, NOutputs, NStates > C)
Calculates the energy (Gramian) scaling via Lyapunov equations.
Definition math.hpp:507
Eigen::Matrix< T, N/2, N/2 > hamilton_solver(const Eigen::Matrix< T, N, N > H)
Solves the hamilton matrix.
Definition math.hpp:343
constexpr T pade_den_param(unsigned long m, unsigned long n, unsigned long k)
Calculates the numerator parameters of the pade approximation.
Definition math.hpp:200
Eigen::Matrix< T, LSize+RSize, LSize+RSize > join_to_diagonal(const Eigen::Vector< T, LSize > &l, const Eigen::Vector< T, RSize > &r)
Definition math.hpp:216
constexpr T pade_param_reccursice(T P_k, std::int32_t m, std::int32_t n, std::int32_t k)
Calculates the next pade parameter recursively.
Definition math.hpp:115
Eigen::Matrix< T, Rows, Cols > adj(const Eigen::Matrix< T, Rows, Cols, Options, MaxRows, MaxCols > &A)
Definition math.hpp:263
Eigen::Matrix< T, Rows, Cols, Options, MaxRows, MaxCols > identity_like(const Eigen::Matrix< T, Rows, Cols, Options, MaxRows, MaxCols > &unused)
Definition math.hpp:205
constexpr T pade_num_param(std::int32_t m, std::int32_t n, std::int32_t k)
Calculates the numerator parameters of the pade approximation.
Definition math.hpp:165
Eigen::Vector< T, N > unwrap(const Eigen::Vector< T, N > &y, const T &modulo)
Unwinds modulo jumps.
Definition math.hpp:55
Eigen::Matrix< T, N/2, N/2 > symplectic_solver(const Eigen::Matrix< T, N, N > S)
Solves the hamilton matrix.
Definition math.hpp:398
Eigen::Vector< T, N > unwrap_deg(const Eigen::Vector< T, N > &phases)
Unwinds phase jumps of in degrees.
Definition math.hpp:93
Eigen::Vector< T, LSize+RSize > join_to_vector(const Eigen::Vector< T, LSize > &l, const Eigen::Vector< T, RSize > &r)
Definition math.hpp:225
Eigen::Matrix< T, NStates, NStates > dare_solver(const Eigen::Matrix< T, NStates, NStates, AOpt, AMaxR, AMaxC > &A, const Eigen::Matrix< T, NStates, NInputs, BOpt, BMaxR, BMaxC > &B, const Eigen::Matrix< T, NStates, NStates, QOpt, QMaxR, QMaxC > &Q, const Eigen::Matrix< T, NInputs, NInputs, ROpt, RMaxR, RMaxC > &R)
Solves the discrete time riccati equation (DARE)
Definition math.hpp:668
constexpr T pow(const T &base, const int &exp)
Power function for integral exponents.
Definition math.hpp:30
constexpr void pade_params(T *params, std::size_t size, std::uint32_t m, std::uint32_t n)
Calculates all pade parameters up to the given size.
Definition math.hpp:131
Eigen::Matrix< T, Rows-1, Cols-1 > minor(const Eigen::Matrix< T, Rows, Cols, Options, MaxRows, MaxCols > &A, size_t ex_row, size_t ex_col)
returns the minor matrix excluding the provided column and row
Definition math.hpp:248
Eigen::Matrix< T, NStates, NStates > lyapunov_solver(const Eigen::Matrix< T, NStates, NStates, AOpt, AMaxR, AMaxC > &A, const Eigen::Matrix< T, NStates, NStates, QOpt, QMaxR, QMaxC > &Q)
Solves the continuous time Lyapunov equation.
Definition math.hpp:469
Eigen::Matrix< T, NStates, NStates > care_solver(const Eigen::Matrix< T, NStates, NStates, AOpt, AMaxR, AMaxC > &A, const Eigen::Matrix< T, NStates, NInputs, BOpt, BMaxR, BMaxC > &B, const Eigen::Matrix< T, NStates, NStates, QOpt, QMaxR, QMaxC > &Q, const Eigen::Matrix< T, NInputs, NInputs, ROpt, RMaxR, RMaxC > &R)
Solves the continuous time riccati equation (CARE)
Definition math.hpp:608
Eigen::Matrix< T, N-1, N-1 > companion(const Eigen::Vector< T, N > &v)
Creates a companion matrix from a vector.
Definition math.hpp:314
Eigen::Vector< T, N > unwrap_rad(const Eigen::Vector< T, N > &phases)
Unwinds phase jumps of in radiants.
Definition math.hpp:80