Linear time-invariant (LTI) system representation (tf, ss, zpk), frequency response (Bode, Nyquist), time response (step, impulse), and stability. More...
Files | |
| file | analyze.hpp |
| file | connect.hpp |
| file | conversion.hpp |
| file | discretize.hpp |
| file | freq.hpp |
| file | numerics.hpp |
| file | lti.hpp |
| file | lyapunov.hpp |
| file | props.hpp |
| LTI-system predicates and analytic properties. | |
| file | response.hpp |
| file | riccati.hpp |
| file | place.hpp |
| file | state.hpp |
Classes | |
| struct | nk::control::MarginResult |
| Result of margin(sys) — gain / phase margin pair plus the frequencies at which they occur. More... | |
| struct | nk::control::Tf2ZpResult |
| Result of tf2zp — zeros, poles, and gain from tf coefficients. More... | |
| struct | nk::control::StateSpace |
| State-space realisation (A, B, C, D), the result of tf2ss. More... | |
| struct | nk::control::BodeResult |
| Result of bode(sys, w). More... | |
| struct | nk::control::NyquistResult |
| Result of nyquist(sys, w). More... | |
| struct | nk::control::DampResult |
| Result of damp. More... | |
| struct | nk::control::RiccatiResult |
| Result of an algebraic Riccati solve, matching MATLAB's [X, L, G] output order for care / dare. More... | |
| struct | nk::control::CovarResult |
| Output and state covariance of an LTI system driven by white noise. More... | |
Typedefs | |
| using | nk::control::internal::Mat = std::vector<double> |
| using | nk::control::internal::Vec = std::vector<double> |
Functions | |
| Value | nk::control::dcgain (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| DC (steady-state) gain of an LTI system. | |
| MarginResult | nk::control::margin (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| Gain and phase margins ([Gm, Pm, Wcg, Wcp] = margin(sys)). | |
| Value | nk::control::stepinfo (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| Step-response quality metrics (S = stepinfo(sys)). | |
| Value | nk::control::allmargin (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| All stability margins as a struct (S = allmargin(sys)). | |
| Value | nk::control::hinfnorm (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| H-infinity norm of a continuous-time LTI system (g = hinfnorm(sys)). | |
| Value | nk::control::series (const Value &sys1, const Value &sys2, std::pmr::memory_resource *mr=nullptr) |
| Cascade two LTI blocks (series(sys1, sys2)). | |
| Value | nk::control::parallel (const Value &sys1, const Value &sys2, std::pmr::memory_resource *mr=nullptr) |
| Parallel connection of two LTI blocks (parallel(sys1, sys2)). | |
| Value | nk::control::feedback (const Value &G, const Value &H, int sign, std::pmr::memory_resource *mr=nullptr) |
| Closed-loop transfer function under feedback (feedback(G, H, sign)). | |
| Tf2ZpResult | nk::control::tf2zp (const Value &num, const Value &den, std::pmr::memory_resource *mr=nullptr) |
| Convert tf coefficients to zero-pole-gain form. | |
| std::pair< Value, Value > | nk::control::zp2tf (const Value &z, const Value &p, const Value &k, std::pmr::memory_resource *mr=nullptr) |
| Convert zero-pole-gain form to tf coefficients. | |
| StateSpace | nk::control::tf2ss (const Value &num, const Value &den, std::pmr::memory_resource *mr=nullptr) |
| Convert tf coefficients to controllable canonical state-space. | |
| std::pair< Value, Value > | nk::control::ss2tf (const Value &A, const Value &B, const Value &C, const Value &D, int iu, std::pmr::memory_resource *mr=nullptr) |
| Convert state-space to tf coefficients. | |
| Value | nk::control::minreal (const Value &sys, double tol=0.0, std::pmr::memory_resource *mr=nullptr) |
| Minimal realization — cancel pole/zero pairs (sysr = minreal(sys, tol)). | |
| Value | nk::control::c2d (const Value &sys, double Ts, const std::string &method, std::pmr::memory_resource *mr=nullptr) |
| Continuous-to-discrete conversion (c2d(sys, Ts, method)). | |
| Value | nk::control::d2c (const Value &sys, const std::string &method, std::pmr::memory_resource *mr=nullptr) |
| Discrete-to-continuous conversion (d2c(sys, method)). | |
| Value | nk::control::evalfr (const Value &sys, double f, std::pmr::memory_resource *mr=nullptr) |
| Single-point frequency response (H = evalfr(sys, f)). | |
| Value | nk::control::freqresp (const Value &sys, const Value &w, std::pmr::memory_resource *mr=nullptr) |
| Frequency response on a user-supplied grid (H = freqresp(sys, w)). | |
| BodeResult | nk::control::bode (const Value &sys, const Value &wArg, std::pmr::memory_resource *mr=nullptr) |
| Bode magnitude and phase response ([mag, phase, w] = bode(sys, w)). | |
| NyquistResult | nk::control::nyquist (const Value &sys, const Value &wArg, std::pmr::memory_resource *mr=nullptr) |
| Nyquist plot data ([re, im, w] = nyquist(sys, w)). | |
| std::pair< Value, Value > | nk::control::rlocus (const Value &sys, const Value &kVec, std::pmr::memory_resource *mr=nullptr) |
| Root locus ([r, k] = rlocus(sys, k)). | |
| bool | nk::control::internal::solveInPlace (Mat &A, Mat &B, std::size_t n, std::size_t nrhs) |
| In-place partial-pivot LU decomposition + back-substitution. | |
| Mat | nk::control::internal::expm (const Mat &A, std::size_t n) |
| Matrix exponential via the canonical [6/6] Padé approximant with scaling and squaring. | |
| Vec | nk::control::internal::charPoly (const Mat &A, std::size_t n) |
| Faddeev–LeVerrier characteristic polynomial of an n×n column-major matrix. | |
| Mat | nk::control::internal::matmulSq (const Mat &A, const Mat &B, std::size_t n) |
| Square matrix multiply C = A·B (all n×n column-major). | |
| Value | nk::control::tf (const Value &num, const Value &den, double Ts, std::pmr::memory_resource *mr=nullptr) |
| Transfer-function LTI constructor (tf(num, den, Ts)). | |
| Value | nk::control::zpk (const Value &z, const Value &p, double k, double Ts, std::pmr::memory_resource *mr=nullptr) |
| Zero-pole-gain LTI constructor (zpk(z, p, k, Ts)). | |
| Value | nk::control::ss (const Value &A, const Value &B, const Value &C, const Value &D, double Ts, std::pmr::memory_resource *mr=nullptr) |
| State-space LTI constructor (ss(A, B, C, D, Ts)). | |
| Value | nk::control::filt (const Value &num, const Value &den, double Ts, std::pmr::memory_resource *mr=nullptr) |
| Discrete tf with z^-1 variable convention (filt(num, den, Ts)). | |
| Value | nk::control::frd (const Value &response, const Value &frequency, double Ts, std::pmr::memory_resource *mr=nullptr) |
| Frequency-response data model (frd(response, frequency, Ts)). | |
| std::tuple< Value, Value > | nk::control::frdata (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| Extract (resp, freq) from an frd model. | |
| std::tuple< Value, Value > | nk::control::tfdata (const Value &sys, bool asVector, std::pmr::memory_resource *mr=nullptr) |
| Extract numerator / denominator from any LTI form (tfdata). | |
| std::tuple< Value, Value, Value > | nk::control::zpkdata (const Value &sys, bool asVector, std::pmr::memory_resource *mr=nullptr) |
| Extract zeros, poles, and gain (zpkdata). | |
| std::tuple< Value, Value, Value, Value > | nk::control::ssdata (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| Extract A, B, C, D from any LTI form (ssdata). | |
| Value | nk::control::ss2ss (const Value &sys, const Value &T, std::pmr::memory_resource *mr=nullptr) |
| Similarity transform of a state-space model (ss2ss(sys, T)). | |
| Value | nk::control::lyap (const Value &A, const Value &Q, std::pmr::memory_resource *mr=nullptr) |
| Continuous-time Lyapunov equation solver (X = lyap(A, Q)). | |
| Value | nk::control::dlyap (const Value &A, const Value &Q, std::pmr::memory_resource *mr=nullptr) |
| Discrete-time Lyapunov (Stein) equation solver (X = dlyap(A, Q)). | |
| Value | nk::control::isct (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| Continuous-time predicate (isct(sys)). | |
| Value | nk::control::isdt (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| Discrete-time predicate (isdt(sys)). | |
| Value | nk::control::issiso (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| SISO predicate (issiso(sys)). | |
| Value | nk::control::isproper (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| Proper rational predicate (isproper(sys)). | |
| Value | nk::control::isstable (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| Stability predicate (isstable(sys)). | |
| Value | nk::control::order (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| System order (order(sys)). | |
| Value | nk::control::pole (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| Poles of the system (pole(sys)). | |
| Value | nk::control::zero (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| (Transmission) zeros of the system (zero(sys)). | |
| DampResult | nk::control::damp (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| Natural frequencies and damping ratios ([wn, zeta, p] = damp(sys)). | |
| std::pair< Value, Value > | nk::control::pzmap (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| Pole / zero map ([p, z] = pzmap(sys)). | |
| Value | nk::control::isstatic (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| Static-system predicate (isstatic(sys)). | |
| Value | nk::control::tzero (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| Transmission zeros (tzero(sys)). | |
| std::pair< Value, Value > | nk::control::step_response (const Value &sys, const Value &tArg, std::pmr::memory_resource *mr=nullptr, Value *xOut=nullptr) |
| Unit-step response ([y, t] = step(sys, tFinal_or_t)). | |
| std::pair< Value, Value > | nk::control::impulse_response (const Value &sys, const Value &tArg, std::pmr::memory_resource *mr=nullptr, Value *xOut=nullptr) |
| Unit-impulse response ([y, t] = impulse(sys, tFinal_or_t)). | |
| Value | nk::control::lsim (const Value &sys, const Value &u, const Value &t, const Value &x0, std::pmr::memory_resource *mr=nullptr, Value *xOut=nullptr) |
| Linear simulation with arbitrary input (y = lsim(sys, u, t, x0)). | |
| std::pair< Value, Value > | nk::control::initial_response (const Value &sys, const Value &x0, const Value &tArg, std::pmr::memory_resource *mr=nullptr, Value *xOut=nullptr) |
| Initial-condition response ([y, t] = initial(sys, x0, tArg)). | |
| RiccatiResult | nk::control::care (const Value &A, const Value &B, const Value &Q, const Value &R, std::pmr::memory_resource *mr=nullptr) |
| Continuous-time algebraic Riccati equation ([X, L, G] = care(A, B, Q, R)). | |
| RiccatiResult | nk::control::dare (const Value &A, const Value &B, const Value &Q, const Value &R, std::pmr::memory_resource *mr=nullptr) |
| Discrete-time algebraic Riccati equation ([X, L, G] = dare(A, B, Q, R)). | |
| Value | nk::control::acker (const Value &A, const Value &B, const Value &p, std::pmr::memory_resource *mr=nullptr) |
| Ackermann's pole placement (K = acker(A, B, p)). | |
| Value | nk::control::place (const Value &A, const Value &B, const Value &p, std::pmr::memory_resource *mr=nullptr) |
| SISO alias for acker — K = place(A, B, p). | |
| Value | nk::control::ctrb_AB (const Value &A, const Value &B, std::pmr::memory_resource *mr=nullptr) |
| Controllability matrix from raw (A, B) (Co = ctrb(A, B)). | |
| Value | nk::control::ctrb_sys (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| Controllability matrix from a system struct (Co = ctrb(sys)). | |
| Value | nk::control::obsv_AC (const Value &A, const Value &C, std::pmr::memory_resource *mr=nullptr) |
| Observability matrix from raw (A, C) (O = obsv(A, C)). | |
| Value | nk::control::obsv_sys (const Value &sys, std::pmr::memory_resource *mr=nullptr) |
| Observability matrix from a system struct (O = obsv(sys)). | |
| Value | nk::control::gram (const Value &sys, const std::string &type, std::pmr::memory_resource *mr=nullptr) |
| Controllability / observability gramian (W = gram(sys, type)). | |
| CovarResult | nk::control::covar (const Value &sys, const Value &W, std::pmr::memory_resource *mr=nullptr) |
| Steady-state covariance under white-noise excitation ([P, Q] = covar(sys, W)). | |
Linear time-invariant (LTI) system representation (tf, ss, zpk), frequency response (Bode, Nyquist), time response (step, impulse), and stability.
| using nk::control::internal::Mat = std::vector<double> |
Definition at line 25 of file numerics.hpp.
| using nk::control::internal::Vec = std::vector<double> |
Definition at line 26 of file numerics.hpp.
| Value nk::control::acker | ( | const Value & | A, |
| const Value & | B, | ||
| const Value & | p, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Ackermann's pole placement (K = acker(A, B, p)).
Computes the SISO state-feedback gain row K such that eig(A − B·K) = p. Implements:
\[ K = [0\ 0\ \cdots\ 0\ 1]\ \mathcal{C}(A,B)^{-1}\ \phi(A) \]
where \( \mathcal{C} \) is the controllability matrix and \( \phi(s) = \prod_i (s - p_i) \) is the desired characteristic polynomial.
Single-input only. For multi-input systems use a Riccati-based design (lqr) or split into multiple SISO loops.
| A | n×n state matrix. |
| B | n×1 input column. |
| p | n-element vector of desired closed-loop poles. |
| mr | Memory resource (nullptr → process default). |
| Error | if B has more than one column, dimensions disagree, or (A, B) is uncontrollable. |
All stability margins as a struct (S = allmargin(sys)).
Scans the open-loop frequency response for every gain crossover (|G|=1 → phase margins) and every phase crossover (∠G = −180°+360°k → gain margins), and returns a struct with the seven MATLAB fields: GainMargin, GMFrequency, PhaseMargin, PMFrequency, DelayMargin, DMFrequency (row vectors), and Stable (logical — closed-loop unity- feedback stability from the roots of den+num). The single-crossover values coincide with margin; DelayMargin = PM(rad)/ω_gc.
| sys | LTI struct (tf / zpk / ss). |
| mr | Memory resource (nullptr → process default). |
| BodeResult nk::control::bode | ( | const Value & | sys, |
| const Value & | wArg, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Bode magnitude and phase response ([mag, phase, w] = bode(sys, w)).
Evaluates the response on wArg (or a default logarithmic grid covering the dominant pole/zero magnitudes if wArg is empty). Phase is unwrapped sample-by-sample so it doesn't jump by 360°.
| sys | LTI struct. |
| wArg | Frequency vector (rad/s) or empty to auto-pick. |
| mr | Memory resource (nullptr → process default). |
| Value nk::control::c2d | ( | const Value & | sys, |
| double | Ts, | ||
| const std::string & | method, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Continuous-to-discrete conversion (c2d(sys, Ts, method)).
Discretises a continuous-time LTI model at sample time Ts. The output struct's kind matches the input (tf → tf, zpk → zpk, ss → ss); the conversion is always done through an internal ss form, then converted back if needed.
Supported methods:
| sys | Continuous LTI struct (Ts == 0). |
| Ts | Sample time in seconds (positive). |
| method | "zoh" or "tustin" (case-sensitive). |
| mr | Memory resource (nullptr → process default). |
| Error | on unknown method, non-continuous input, or singularity in the Tustin matrix I − A·Ts/2. |
| RiccatiResult nk::control::care | ( | const Value & | A, |
| const Value & | B, | ||
| const Value & | Q, | ||
| const Value & | R, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Continuous-time algebraic Riccati equation ([X, L, G] = care(A, B, Q, R)).
Solves \( A^{T}X + XA - XBR^{-1}B^{T}X + Q = 0 \) for the unique stabilizing \( X = X^{T} \succeq 0 \) via the matrix sign function of the Hamiltonian \( H = \begin{bmatrix} A & -BR^{-1}B^{T} \\ -Q & -A^{T}\end{bmatrix} \). The gain is \( G = R^{-1}B^{T}X \) and the closed-loop poles are \( \mathrm{eig}(A - BG) \).
| A | n×n state matrix. |
| B | n×m input matrix. |
| Q | n×n state-weight (symmetric PSD). |
| R | m×m input-weight (symmetric positive-definite). |
| mr | Memory resource (nullptr → process default). |
| Error | on shape mismatch or a singular Hamiltonian embedding. |
| Vec nk::control::internal::charPoly | ( | const Mat & | A, |
| std::size_t | n ) |
Faddeev–LeVerrier characteristic polynomial of an n×n column-major matrix.
Returns coefficients [1, c1, c2, …, cn] in standard order (descending powers, leading 1): det(sI − A) = sⁿ + c1·sⁿ⁻¹ + … + cn. O(n⁴) work — fine for the n ≲ 32 typical of textbook problems.
| A | Column-major n×n input. |
| n | Matrix dimension. |
| CovarResult nk::control::covar | ( | const Value & | sys, |
| const Value & | W, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Steady-state covariance under white-noise excitation ([P, Q] = covar(sys, W)).
For an LTI system driven by white noise of intensity W, solves the gramian Lyapunov equation for the state covariance Q and projects it to the output covariance P:
| sys | LTI struct (ss / tf / zpk — converted to state space). |
| W | Noise intensity (scalar → W·I, or an m×m matrix). |
| mr | Memory resource (nullptr → process default). |
| Error | on a non-LTI input or a shape mismatch. |
| Value nk::control::ctrb_AB | ( | const Value & | A, |
| const Value & | B, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Controllability matrix from raw (A, B) (Co = ctrb(A, B)).
Builds \( \mathcal{C} = [B,\ AB,\ A^2 B,\ \ldots,\ A^{n-1} B] \) — shape n × (n·m) where n = rows(A) and m = cols(B).
The system is controllable iff this matrix has full row rank (rank(Co) == n). NK doesn't run the rank check here — that's left to whatever rank tool the caller prefers.
| A | n×n state matrix. |
| B | n×m input matrix. |
| mr | Memory resource (nullptr → process default). |
Controllability matrix from a system struct (Co = ctrb(sys)).
Convenience wrapper: pulls (A, B) from the LTI struct (converting tf / zpk inputs through tf2ss internally) and delegates to ctrb_AB.
| sys | LTI struct. |
| mr | Memory resource (nullptr → process default). |
| Value nk::control::d2c | ( | const Value & | sys, |
| const std::string & | method, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Discrete-to-continuous conversion (d2c(sys, method)).
Inverse of c2d. The default method is "tustin"; ZOH inverse is not implemented (would require a numerically robust matrix logarithm).
| sys | Discrete LTI struct (Ts > 0). |
| method | "tustin" (default). "zoh" is rejected. |
| mr | Memory resource (nullptr → process default). |
| Error | on unknown method or non-discrete input. |
| DampResult nk::control::damp | ( | const Value & | sys, |
| std::pmr::memory_resource * | mr = nullptr ) |
Natural frequencies and damping ratios ([wn, zeta, p] = damp(sys)).
For each pole \( s_i \): \( \omega_{n,i} = |s_i|,\ \zeta_i = -\text{Re}(s_i)/\omega_{n,i} \). Discrete-time poles are first converted to the s-plane via \( s = \log(z) / T_s \).
| sys | LTI struct. |
| mr | Memory resource (nullptr → process default). |
| RiccatiResult nk::control::dare | ( | const Value & | A, |
| const Value & | B, | ||
| const Value & | Q, | ||
| const Value & | R, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Discrete-time algebraic Riccati equation ([X, L, G] = dare(A, B, Q, R)).
Solves \( A^{T}XA - X - A^{T}XB(R + B^{T}XB)^{-1}B^{T}XA + Q = 0 \) for the stabilizing \( X \). Builds the symplectic matrix and applies a Cayley transform (unit disk → left half-plane) so the same sign-function machinery as care selects the stable subspace. The gain is \( G = (R + B^{T}XB)^{-1}B^{T}XA \), closed-loop poles \( \mathrm{eig}(A - BG) \) (inside the unit circle).
| A | n×n state matrix (must be nonsingular). |
| B | n×m input matrix. |
| Q | n×n state-weight (symmetric PSD). |
| R | m×m input-weight (symmetric positive-definite). |
| mr | Memory resource (nullptr → process default). |
| Error | on shape mismatch, singular A, or a singular embedding. |
DC (steady-state) gain of an LTI system.
Evaluates the transfer function at the steady-state frequency:
| sys | LTI struct (tf / zpk / ss as built by toolboxes/control/lti). |
| mr | Memory resource (nullptr → process default). |
| Value nk::control::dlyap | ( | const Value & | A, |
| const Value & | Q, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Discrete-time Lyapunov (Stein) equation solver (X = dlyap(A, Q)).
Solves \( A X A^{T} - X + Q = 0 \) via the same Kronecker approach as lyap.
| A | n×n state matrix (typically Schur-stable, |λ_i| < 1). |
| Q | n×n right-hand side. |
| mr | Memory resource (nullptr → process default). |
| Error | if dimensions disagree or the Kronecker system is singular (A has eigenvalues λ_i, λ_j with λ_i·λ_j = 1). |
Single-point frequency response (H = evalfr(sys, f)).
Returns the complex value of the transfer function at a single frequency f (rad/s):
| sys | LTI struct (tf / zpk / ss). |
| f | Frequency in rad/s. |
| mr | Memory resource (nullptr → process default). |
| Mat nk::control::internal::expm | ( | const Mat & | A, |
| std::size_t | n ) |
Matrix exponential via the canonical [6/6] Padé approximant with scaling and squaring.
Coefficient set: c_k = (12 − k)! · 6! / (12! · k! · (6 − k)!). Falls back to a 30-term truncated power series if the Padé denominator is numerically singular. O(n³) per matrix multiply.
| A | Column-major n×n input. |
| n | Matrix dimension. |
| Value nk::control::feedback | ( | const Value & | G, |
| const Value & | H, | ||
| int | sign, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Closed-loop transfer function under feedback (feedback(G, H, sign)).
Computes:
H defaults to unity feedback at the call site (pass tf({1},{1})).
| G | Forward path block. |
| H | Feedback path block. |
| sign | −1 (negative feedback) or +1 (positive feedback). |
| mr | Memory resource (nullptr → process default). |
| Value nk::control::filt | ( | const Value & | num, |
| const Value & | den, | ||
| double | Ts, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Discrete tf with z^-1 variable convention (filt(num, den, Ts)).
Equivalent to tf except:
| num | Numerator coefficients (z^-1 ascending powers). |
| den | Denominator coefficients. |
| Ts | Sample time (−1 = unspecified, > 0 = explicit). |
| mr | Memory resource (nullptr → process default). |
| Value nk::control::frd | ( | const Value & | response, |
| const Value & | frequency, | ||
| double | Ts, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Frequency-response data model (frd(response, frequency, Ts)).
Builds a struct {kind="frd", resp, freq, Ts}. response may be complex; frequency is a real vector (rad/s). Both are stored as column vectors.
| response | Complex response samples. |
| frequency | Real frequency grid (rad/s). |
| Ts | Sample time (0 = continuous). |
| mr | Memory resource (nullptr → process default). |
| Value nk::control::gram | ( | const Value & | sys, |
| const std::string & | type, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Controllability / observability gramian (W = gram(sys, type)).
Solves the appropriate Lyapunov equation for the LTI system sys:
Continuous vs discrete is taken from the system's sample time (Ts).
| sys | LTI struct (ss / tf / zpk — converted to state space). |
| type | "c" (controllability) or "o" (observability). |
| mr | Memory resource (nullptr → process default). |
| Error | on a non-LTI input or an unknown type. |
H-infinity norm of a continuous-time LTI system (g = hinfnorm(sys)).
Returns \( \|G\|_\infty = \sup_\omega \sigma_{\max}(G(j\omega)) \) via the Bruinsma–Steinbuch Hamiltonian test with bisection on \( \gamma \) ( \( \gamma \) is an upper bound iff the Hamiltonian \( M(\gamma) \) has no purely imaginary eigenvalue). Returns Inf for an unstable or marginally stable system (a pole on/right of the jω axis).
| sys | Continuous-time LTI struct (ss / tf / zpk). |
| mr | Memory resource (nullptr → process default). |
| Error | on a non-LTI input or a discrete-time system (discrete H∞ is not yet supported). |
| std::pair< Value, Value > nk::control::impulse_response | ( | const Value & | sys, |
| const Value & | tArg, | ||
| std::pmr::memory_resource * | mr = nullptr, | ||
| Value * | xOut = nullptr ) |
Unit-impulse response ([y, t] = impulse(sys, tFinal_or_t)).
Continuous: simulates with u ≡ 0 and initial condition x(0+) = B (the differentiation-through-delta trick). Discrete: applies u[0] = 1, u[k>0] = 0.
Time argument semantics match step_response.
| sys | LTI struct. |
| tArg | Time argument. |
| mr | Memory resource (nullptr → process default). |
| std::pair< Value, Value > nk::control::initial_response | ( | const Value & | sys, |
| const Value & | x0, | ||
| const Value & | tArg, | ||
| std::pmr::memory_resource * | mr = nullptr, | ||
| Value * | xOut = nullptr ) |
Initial-condition response ([y, t] = initial(sys, x0, tArg)).
Time response of sys to the initial state x0 with zero input — the same integrator as step_response / impulse_response run with u ≡ 0 and x(0) = x0, output y = C·x. The time argument tArg follows the same semantics as step_response (Value::Empty → auto grid from the system poles; scalar → final time; vector → explicit grid). The optional state trajectory is returned via xOut.
| sys | LTI struct (tf / zpk / ss). |
| x0 | Initial state (length = number of states). |
| tArg | Time argument (Empty / scalar tFinal / explicit grid). |
| mr | Memory resource (nullptr → process default). |
| xOut | Optional state-trajectory output (one row per sample). |
Continuous-time predicate (isct(sys)).
True when Ts == 0.
| sys | LTI struct. |
| mr | Memory resource (nullptr → process default). |
Discrete-time predicate (isdt(sys)).
True when Ts > 0 (explicit sample time) or Ts == -1 ("unspecified discrete").
| sys | LTI struct. |
| mr | Memory resource (nullptr → process default). |
| Value nk::control::lsim | ( | const Value & | sys, |
| const Value & | u, | ||
| const Value & | t, | ||
| const Value & | x0, | ||
| std::pmr::memory_resource * | mr = nullptr, | ||
| Value * | xOut = nullptr ) |
Linear simulation with arbitrary input (y = lsim(sys, u, t, x0)).
Simulates \( \dot x = Ax + Bu,\ y = Cx + Du \) (or its discrete analogue) along the explicit time grid t. u must have length(t) rows (one row per sample). x0 is the initial state (length n); pass Value::Empty to default to zero.
| sys | LTI struct. |
| u | Input samples, one row per time step. |
| t | Time grid (at least 2 samples). |
| x0 | Initial state (or empty for zero IC). |
| mr | Memory resource (nullptr → process default). |
| Error | if length(u) != length(t) or length(t) < 2. |
| Value nk::control::lyap | ( | const Value & | A, |
| const Value & | Q, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Continuous-time Lyapunov equation solver (X = lyap(A, Q)).
Solves \( A X + X A^{T} + Q = 0 \) by vectorising the equation (Kronecker form) into a dense n²×n² linear system and applying Gaussian elimination. Practical for n up to ~32; for larger problems a Bartels–Stewart implementation would be wanted.
| A | n×n state matrix. |
| Q | n×n right-hand side (typically symmetric PSD). |
| mr | Memory resource (nullptr → process default). |
| Error | if dimensions disagree or the Kronecker system is singular (A has an eigenvalue λ with λ + λ* = 0). |
| MarginResult nk::control::margin | ( | const Value & | sys, |
| std::pmr::memory_resource * | mr = nullptr ) |
Gain and phase margins ([Gm, Pm, Wcg, Wcp] = margin(sys)).
Builds a dense Bode grid via bode(sys) and scans for the first crossings of phase = −180° (gain margin) and |H| = 1 (phase margin) from low to high frequency. If a crossing isn't found the corresponding margin is +Inf.
| sys | LTI struct (tf / zpk / ss). |
| mr | Memory resource (nullptr → process default). |
| Mat nk::control::internal::matmulSq | ( | const Mat & | A, |
| const Mat & | B, | ||
| std::size_t | n ) |
Square matrix multiply C = A·B (all n×n column-major).
| A | Column-major n×n left operand. |
| B | Column-major n×n right operand. |
| n | Matrix dimension. |
| Value nk::control::minreal | ( | const Value & | sys, |
| double | tol = 0.0, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Minimal realization — cancel pole/zero pairs (sysr = minreal(sys, tol)).
Removes common factors of a model to produce a minimal (controllable + observable) realization:
| sys | LTI struct (tf / zpk / ss). |
| tol | Relative pole/zero cancellation tolerance (≤0 → sqrt(eps)). |
| mr | Memory resource (nullptr → process default). |
| Error | on a non-LTI input or a MIMO state-space. |
| NyquistResult nk::control::nyquist | ( | const Value & | sys, |
| const Value & | wArg, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Nyquist plot data ([re, im, w] = nyquist(sys, w)).
Same grid logic as bode but returns the Cartesian components of H instead of magnitude / phase.
| sys | LTI struct. |
| wArg | Frequency vector (rad/s) or empty to auto-pick. |
| mr | Memory resource (nullptr → process default). |
| Value nk::control::obsv_AC | ( | const Value & | A, |
| const Value & | C, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Observability matrix from raw (A, C) (O = obsv(A, C)).
Builds \( \mathcal{O} = [C;\ CA;\ CA^2;\ \ldots;\ CA^{n-1}] \) — shape (n·p) × n where n = rows(A) and p = rows(C).
The system is observable iff this matrix has full column rank.
| A | n×n state matrix. |
| C | p×n output matrix. |
| mr | Memory resource (nullptr → process default). |
| Value nk::control::parallel | ( | const Value & | sys1, |
| const Value & | sys2, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Parallel connection of two LTI blocks (parallel(sys1, sys2)).
Both blocks see the same input; their outputs are summed: \( H(s) = H_1(s) + H_2(s) \). In coefficient form, H = (n1·d2 + n2·d1) / (d1·d2) with leading-coefficient normalisation on the denominator.
| sys1 | First block. |
| sys2 | Second block. |
| mr | Memory resource (nullptr → process default). |
| Value nk::control::place | ( | const Value & | A, |
| const Value & | B, | ||
| const Value & | p, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
SISO alias for acker — K = place(A, B, p).
The robust multi-input variant (Kautsky–Nichols eigenvector assignment) is intentionally not re-implemented here; calling place on a multi-input system therefore behaves identically to the SISO Ackermann path and will throw on B with more than one column.
| A | n×n state matrix. |
| B | n×1 input column. |
| p | n-element vector of desired closed-loop poles. |
| mr | Memory resource (nullptr → process default). |
| Error | if B has more than one column, dimensions disagree, or (A, B) is uncontrollable. |
| std::pair< Value, Value > nk::control::rlocus | ( | const Value & | sys, |
| const Value & | kVec, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Root locus ([r, k] = rlocus(sys, k)).
For each gain \( k \) in kVec, compute the n closed-loop poles of the negative-feedback system \( T = G / (1 + k\,G) \), i.e. roots of den(s) + k · num(s) = 0.
If kVec is empty, a default sweep is built: 0 plus 100 log-spaced points from 1e-2 to 1e3 (101 gains total).
| sys | LTI struct. |
| kVec | Gain vector or empty for the default sweep. |
| mr | Memory resource (nullptr → process default). |
| Value nk::control::series | ( | const Value & | sys1, |
| const Value & | sys2, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Cascade two LTI blocks (series(sys1, sys2)).
Implements the topology u → sys1 → sys2 → y, equivalent to sys2 * sys1 in transfer-function math ( \( H(s) = H_2(s)\,H_1(s) \)).
Either input may be in any of the three LTI forms (tf / zpk / ss); they are reduced to (num, den) coefficient pairs internally and the polynomial product is taken. The result is always a tf struct, even if both inputs were zpk or ss.
| sys1 | First block (input side). |
| sys2 | Second block (output side). |
| mr | Memory resource (nullptr → process default). |
| bool nk::control::internal::solveInPlace | ( | Mat & | A, |
| Mat & | B, | ||
| std::size_t | n, | ||
| std::size_t | nrhs ) |
In-place partial-pivot LU decomposition + back-substitution.
Operates on a column-major n×n matrix A, with nrhs right-hand-side columns held column-major in B.
| A | Column-major n×n matrix; overwritten with LU. |
| B | Column-major n×nrhs RHS; overwritten with solution. |
| n | Matrix dimension. |
| nrhs | Number of RHS columns. |
| Value nk::control::ss | ( | const Value & | A, |
| const Value & | B, | ||
| const Value & | C, | ||
| const Value & | D, | ||
| double | Ts, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
State-space LTI constructor (ss(A, B, C, D, Ts)).
Builds an ss-tagged struct value with fields {kind="ss", A, B, C, D, Ts}. Dimensions:
| A | State matrix (n×n). |
| B | Input matrix (n×nin). |
| C | Output matrix (nout×n). |
| D | Feedthrough matrix (nout×nin); empty → broadcast 0. |
| Ts | Sample time (0 for continuous). |
| mr | Memory resource (nullptr → process default). |
| Value nk::control::ss2ss | ( | const Value & | sys, |
| const Value & | T, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Similarity transform of a state-space model (ss2ss(sys, T)).
Applies the change of basis x' = T · x: \( A' = T A T^{-1},\ B' = T B,\ C' = C T^{-1},\ D' = D \). Returns a fresh ss struct; the original is untouched.
| sys | Input ss struct. |
| T | n×n invertible similarity matrix. |
| mr | Memory resource (nullptr → process default). |
| Error | if T is singular. |
| std::pair< Value, Value > nk::control::ss2tf | ( | const Value & | A, |
| const Value & | B, | ||
| const Value & | C, | ||
| const Value & | D, | ||
| int | iu, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Convert state-space to tf coefficients.
Uses the Faddeev–LeVerrier expansion to build \( H(s) = C\,(sI - A)^{-1}\,B + D \) without ever forming the matrix inverse. The output denominator is the characteristic polynomial \( \det(sI - A) \).
iu selects the input column when B has multiple inputs (1-based). Only single-output systems (C with 1 row) are supported; MIMO would need a generalized eigenproblem and is not yet exposed.
| A | n×n state matrix. |
| B | n×nin input matrix. |
| C | 1×n output row. |
| D | 1×nin feedthrough. |
| iu | Input column to use (1-based; default 1). |
| mr | Memory resource (nullptr → process default). |
| Error | if A is non-square, iu is out of range, or C has more than 1 row (MIMO not supported). |
| std::pair< Value, Value > nk::control::step_response | ( | const Value & | sys, |
| const Value & | tArg, | ||
| std::pmr::memory_resource * | mr = nullptr, | ||
| Value * | xOut = nullptr ) |
Unit-step response ([y, t] = step(sys, tFinal_or_t)).
Simulates the response of sys to a unit step input. The time argument is overloaded:
Continuous models are discretised via ZOH using a Van Loan matrix-exponential expansion; discrete models iterate the recurrence directly.
| sys | LTI struct (tf / zpk / ss). |
| tArg | Time argument (scalar / vector / empty). |
| mr | Memory resource (nullptr → process default). |
Step-response quality metrics (S = stepinfo(sys)).
Runs the system through step_response with a 2× extended horizon and returns a struct with the fields:
All times are in seconds. Defaults: 10–90 % rise band, 2 % settling band.
| sys | LTI struct (tf / zpk / ss). |
| mr | Memory resource (nullptr → process default). |
| Value nk::control::tf | ( | const Value & | num, |
| const Value & | den, | ||
| double | Ts, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Transfer-function LTI constructor (tf(num, den, Ts)).
Builds a tf-tagged struct value with fields {kind="tf", num, den, Ts, variable}. The numerator and denominator are row coefficient vectors with the leading coefficient first. Ts selects the time domain:
| num | Numerator coefficients. |
| den | Denominator coefficients (leading coefficient nonzero). |
| Ts | Sample time in seconds; 0 for continuous. |
| mr | Memory resource (nullptr → process default). |
| StateSpace nk::control::tf2ss | ( | const Value & | num, |
| const Value & | den, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Convert tf coefficients to controllable canonical state-space.
Produces the controllable canonical realisation (companion-matrix form):
\[ A = \begin{pmatrix} 0 & 1 & 0 & \cdots & 0 \\ \vdots & & & & \\ 0 & 0 & \cdots & 0 & 1 \\ -a_n & -a_{n-1} & \cdots & & -a_1 \end{pmatrix},\quad B = \begin{pmatrix}0 \\ \vdots \\ 0 \\ 1\end{pmatrix} \]
with C[k] = b[n − k] − b[0]·a[n − k] and D = b[0]. The numerator must be no longer than the denominator (proper rational).
| num | Numerator (length ≤ length(den)). |
| den | Denominator (leading coefficient nonzero). |
| mr | Memory resource (nullptr → process default). |
| Error | if length(num) > length(den) (improper). |
| Tf2ZpResult nk::control::tf2zp | ( | const Value & | num, |
| const Value & | den, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Convert tf coefficients to zero-pole-gain form.
Roots are computed via nk::builtin::roots; complex conjugate pairs and trailing-zero roots are fully handled.
| num | Numerator polynomial (row, descending powers). |
| den | Denominator polynomial (leading coefficient must be nonzero). |
| mr | Memory resource (nullptr → process default). |
| Error | if the denominator's leading coefficient is zero. |
| std::tuple< Value, Value > nk::control::tfdata | ( | const Value & | sys, |
| bool | asVector, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Extract numerator / denominator from any LTI form (tfdata).
With asVector = true returns row vectors padded so num and den have equal length (leading zeros on num). With asVector = false wraps each row vector in a 1×1 cell (the default).
Accepts tf inputs directly; zpk and ss inputs are converted via zp2tf and ss2tf.
| sys | LTI struct (tf / zpk / ss). |
| asVector | true for raw row vectors, false for 1×1 cells. |
| mr | Memory resource (nullptr → process default). |
| Error | on unrecognised kind. |
Transmission zeros (tzero(sys)).
For SISO inputs equivalent to zero. For MIMO ss inputs a generalised eigenproblem would be required; that path throws.
| sys | LTI struct. |
| mr | Memory resource (nullptr → process default). |
| Error | MIMO ss input. |
(Transmission) zeros of the system (zero(sys)).
For tf this is roots(num); for zpk it is the z field directly; for SISO ss it goes through ss2tf and roots. MIMO ss would require a generalised eigenproblem (QZ) and is not yet supported.
| sys | LTI struct. |
| mr | Memory resource (nullptr → process default). |
| Error | MIMO ss input. |
| std::pair< Value, Value > nk::control::zp2tf | ( | const Value & | z, |
| const Value & | p, | ||
| const Value & | k, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Convert zero-pole-gain form to tf coefficients.
Expands num = k · ∏(s − z_i) and den = ∏(s − p_i) via nk::builtin::poly. The result preserves complex coefficients if the inputs are complex.
| z | Zeros (column or row vector). |
| p | Poles (column or row vector). |
| k | Gain (scalar). |
| mr | Memory resource (nullptr → process default). |
| Value nk::control::zpk | ( | const Value & | z, |
| const Value & | p, | ||
| double | k, | ||
| double | Ts, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Zero-pole-gain LTI constructor (zpk(z, p, k, Ts)).
Builds a zpk-tagged struct value with fields {kind="zpk", z, p, k, Ts}. z and p are vectors of zeros and poles (real or complex); k is a real scalar gain.
| z | Zero list. |
| p | Pole list. |
| k | Gain scalar. |
| Ts | Sample time (0 for continuous). |
| mr | Memory resource (nullptr → process default). |
| std::tuple< Value, Value, Value > nk::control::zpkdata | ( | const Value & | sys, |
| bool | asVector, | ||
| std::pmr::memory_resource * | mr = nullptr ) |
Extract zeros, poles, and gain (zpkdata).
z and p are column vectors (or 1×1 cells without asVector). k is always a numeric scalar.
| sys | LTI struct (tf / zpk / ss). |
| asVector | true for raw vectors, false for cell-wrapped. |
| mr | Memory resource (nullptr → process default). |