From 377a033b75c2a0ff7edb9428e372a31794de3a48 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Sat, 27 Jun 2026 15:55:12 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20`improve?= =?UTF-8?q?-numerical-accuracy`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docstrings generation was requested by @MitchellThompkins. * https://github.com/MitchellThompkins/constfilt/pull/47#issuecomment-4803378801 The following files were modified: * `include/constfilt/analog_filter.hpp` * `include/constfilt/butterworth.hpp` * `include/constfilt/discretize.hpp` * `include/constfilt/elliptic.hpp` --- include/constfilt/analog_filter.hpp | 39 ++++++++++++ include/constfilt/butterworth.hpp | 31 ++++++++-- include/constfilt/discretize.hpp | 93 +++++++++++++++++++++++++++++ include/constfilt/elliptic.hpp | 64 ++++++++++++++++++-- 4 files changed, 218 insertions(+), 9 deletions(-) diff --git a/include/constfilt/analog_filter.hpp b/include/constfilt/analog_filter.hpp index f6ce156..b6eb4d2 100644 --- a/include/constfilt/analog_filter.hpp +++ b/include/constfilt/analog_filter.hpp @@ -79,6 +79,13 @@ class AnalogFilter : public Filter "method-tag constructor with constfilt::prewarp(wc_hz)"); } + /** + * @brief Discretizes a continuous-time transfer function. + * + * @param continuous_tf Continuous-time transfer function to convert. + * @param sample_rate_hz Sampling rate in hertz. + * @param method_tag Discretization method tag. + */ constexpr AnalogFilter(TransferFunction continuous_tf, T sample_rate_hz, BoundMethod method_tag) : AnalogFilter(checked_discretize(continuous_tf.b, continuous_tf.a, @@ -86,6 +93,14 @@ class AnalogFilter : public Filter { } + /** + * @brief Discretizes an analog transfer function using factored pole and zero data. + * + * @param continuous_tf Continuous-time transfer function to discretize. + * @param factored_tf Factored pole and zero representation used during discretization. + * @param sample_rate_hz Sampling rate in hertz. + * @param method_tag Discretization method tag. + */ constexpr AnalogFilter(TransferFunction continuous_tf, const FactoredTF &factored_tf, T sample_rate_hz, BoundMethod method_tag) @@ -96,12 +111,26 @@ class AnalogFilter : public Filter } private: + /** + * @brief Initializes an analog filter from a digital transfer function. + * + * @param digital_tf Digital transfer function to store in the base filter. + */ constexpr explicit AnalogFilter( TransferFunction digital_tf) : Filter(digital_tf.b, digital_tf.a) { } + /** + * @brief Discretizes an analog transfer function. + * + * @param b_c Continuous-time numerator coefficients. + * @param a_c Continuous-time denominator coefficients. + * @param sample_rate_hz Sampling rate in hertz. + * @param method_tag Discretization method tag. + * @return TransferFunction Discrete transfer function. + */ static constexpr TransferFunction checked_discretize( const T (&b_c)[N + 1u], const T (&a_c)[N + 1u], T sample_rate_hz, BoundMethod method_tag) @@ -137,6 +166,16 @@ class AnalogFilter : public Filter b_c, a_c, static_cast(1) / sample_rate_hz, method_tag); } + /** + * @brief Discretizes an analog transfer function using factored pole-zero data. + * + * @param b_c Continuous-time numerator coefficients. + * @param a_c Continuous-time denominator coefficients. + * @param factored_tf Factored pole-zero representation of the continuous transfer function. + * @param sample_rate_hz Sampling rate in hertz. + * @param method_tag Discretization method tag. + * @return TransferFunction Discrete transfer function coefficients. + */ static constexpr TransferFunction checked_discretize_factored(const T (&b_c)[N + 1u], const T (&a_c)[N + 1u], const FactoredTF &factored_tf, diff --git a/include/constfilt/butterworth.hpp b/include/constfilt/butterworth.hpp index d150a01..00df55c 100644 --- a/include/constfilt/butterworth.hpp +++ b/include/constfilt/butterworth.hpp @@ -31,7 +31,12 @@ class Butterworth using BoundMethod = typename bind_method::type; public: - // Construct from filter specification; all math is constexpr. + /** + * @brief Constructs a Butterworth filter from its cutoff and sample rate. + * + * @param cutoff_hz Cutoff frequency in hertz. + * @param sample_rate_hz Sampling frequency in hertz. + */ constexpr Butterworth(T cutoff_hz, T sample_rate_hz) : AnalogFilter( compute_continuous_tf(cutoff_hz), @@ -78,7 +83,13 @@ class Butterworth // // Denominator: a[k] = p[N-k] * wc^k. The normalized Butterworth // coefficients appear in reversed order, each scaled by wc^k (p[0] = 1 - // so a[0] = 1, keeping the denominator monic). + /** + * @brief Computes the continuous-time transfer function for a high-pass Butterworth filter. + * + * @param wc Cutoff angular frequency in radians per second. + * @param b Numerator coefficients in ascending power order. + * @param a Denominator coefficients in ascending power order. + */ static constexpr void continuous_tf(T wc, T (&b)[N + 1u], T (&a)[N + 1u], HighPass) { @@ -92,7 +103,13 @@ class Butterworth } } - // LP: poles at wc*exp(j*theta_k), no finite zeros, gain = wc^N. + /** + * @brief Computes the factored transfer function for a low-pass Butterworth filter. + * + * @param cutoff_hz Cutoff frequency in hertz. + * @param LowPass Low-pass filter selector. + * @return FactoredTF Factored transfer function with no finite zeros, gain set to the cutoff frequency raised to the filter order, and poles on the Butterworth circle. + */ static constexpr FactoredTF compute_factored_tf(T cutoff_hz, LowPass) { const T wc = static_cast(2) * static_cast(GCEM_PI) * cutoff_hz; @@ -110,7 +127,13 @@ class Butterworth return factored_tf; } - // HP: poles at wc*exp(-j*theta_k) (magnitude wc), N zeros at s=0, gain=1. + /** + * @brief Computes the factored transfer function for a high-pass Butterworth filter. + * + * @param cutoff_hz Cutoff frequency in hertz. + * @param HighPass High-pass filter tag. + * @return FactoredTF Factored transfer function with N zeros at the origin, unit gain, and poles on the Butterworth pole circle. + */ static constexpr FactoredTF compute_factored_tf(T cutoff_hz, HighPass) { using Complex = consteig::Complex; diff --git a/include/constfilt/discretize.hpp b/include/constfilt/discretize.hpp index 666154b..4abca56 100644 --- a/include/constfilt/discretize.hpp +++ b/include/constfilt/discretize.hpp @@ -123,6 +123,13 @@ template struct FactoredTF // exponential factors, compute V * diag(exp_factors) * V^{-1} and return // the real part. template +/** + * @brief Forms a real matrix exponential from an eigenvector basis and per-mode exponential factors. + * + * @param V Complex eigenvector matrix. + * @param exp_factors Exponential value for each eigenmode. + * @return Real part of V * diag(exp_factors) * V^{-1}. + */ constexpr consteig::Matrix spectral_matrix_exp( const consteig::Matrix, N, N> &V, const consteig::Complex (&exp_factors)[N]) @@ -173,6 +180,11 @@ constexpr consteig::Matrix spectral_matrix_exp( // where V = eigenvectors, lam_i = eigenvalues (complex). // Real part extracted at the end (imaginary parts cancel for real A). template +/** + * @brief Computes the matrix exponential of a square matrix. + * + * @return consteig::Matrix The matrix exponential of @p A. + */ constexpr consteig::Matrix matrix_exp( const consteig::Matrix &A) { @@ -205,6 +217,13 @@ constexpr consteig::Matrix matrix_exp( // V[r][i] = p_i^r (Vandermonde, built from UNSCALED poles) // exp factor = exp(Ts * p_i) (spectral scaling) template +/** + * @brief Computes the state transition matrix for a controllable canonical form system. + * + * @param factored_tf Analog poles used to form the canonical eigenvector basis. + * @param Ts Sampling period. + * @return Matrix exponential of the continuous-time controllable canonical form state matrix. + */ constexpr consteig::Matrix matrix_exp_ccf( const FactoredTF &factored_tf, T Ts) { @@ -241,6 +260,13 @@ constexpr consteig::Matrix matrix_exp_ccf( // discrete matrices for callers that need them (e.g. state estimation, observer // design). template +/** + * @brief Discretizes a continuous-time state-space model using zero-order hold. + * + * @param sys_c Continuous-time system. + * @param Ts Sampling period. + * @return State-space model of the zero-order-hold discretization. + */ constexpr StateSpace zoh_discretize(const StateSpace &sys_c, T Ts, ZOH /*tag*/) { @@ -272,6 +298,14 @@ constexpr StateSpace zoh_discretize(const StateSpace &sys_c, T Ts, // Bypasses both the QR eigenvalue search and eigenvector inverse iteration // by exploiting the Vandermonde structure of the CCF eigenvectors. template +/** + * @brief Discretizes a continuous-time state-space system with ZOH using factored poles. + * + * @param sys_c Continuous-time system. + * @param Ts Sampling period. + * @param factored_tf Analog pole data used to compute the state transition matrix. + * @return State-space system with discrete-time A and B matrices and unchanged C and D. + */ constexpr StateSpace zoh_discretize_with_evals( const StateSpace &sys_c, T Ts, const FactoredTF &factored_tf) { @@ -456,6 +490,16 @@ constexpr StateSpace tf_to_ss(const T (&b)[N + 1u], const T (&a)[N + 1u]) // and sample period, maps to z-domain, pads missing zeros at z=-1, matches // gain, and assembles the discrete TF. template +/** + * @brief Assembles a matched-Z discrete transfer function from analog poles, zeros, and gain. + * + * @param poles Analog poles to map to the z-plane. + * @param zeros Analog finite zeros to map to the z-plane. + * @param nz Number of valid entries in @p zeros. + * @param k_c Analog gain used for gain matching. + * @param Ts Sampling period. + * @return TransferFunction Discrete transfer function with matched poles, zeros, and gain. + */ constexpr TransferFunction matched_z_assemble( const consteig::Complex (&poles)[N], const consteig::Complex (&zeros)[N], consteig::Size nz, T k_c, T Ts) @@ -604,6 +648,15 @@ constexpr TransferFunction matched_z_assemble( // Steps 1-3: extract poles and zeros from polynomial coefficients via companion // matrix eigenvalues, then delegate to matched_z_assemble. template +/** + * @brief Discretizes an analog transfer function using the matched-Z method. + * + * @param b_c Continuous-time numerator coefficients. + * @param a_c Continuous-time denominator coefficients. + * @param Ts Sampling period. + * @param tag Matched-Z method tag. + * @return TransferFunction Discrete-time transfer function. + */ constexpr TransferFunction matched_z_discretize_tf( const T (&b_c)[N + 1u], const T (&a_c)[N + 1u], T Ts, MatchedZ /*tag*/) { @@ -695,6 +748,13 @@ constexpr TransferFunction matched_z_discretize_tf( // Matched-Z discretization from a FactoredTF: poles and zeros are known // analytically, so companion-matrix eigendecompositions are not needed. template +/** + * @brief Discretizes a factored transfer function using matched-Z mapping. + * + * @param factored_tf Analog poles, zeros, gain, and zero count. + * @param Ts Sampling period. + * @return TransferFunction Discrete transfer function coefficients. + */ constexpr TransferFunction matched_z_discretize_factored( const FactoredTF &factored_tf, T Ts) { @@ -819,6 +879,12 @@ constexpr TransferFunction analog_to_digital( } template +/** + * @brief Discretizes a continuous-time state-space system using prewarped Tustin and converts it to a transfer function. + * + * @param tag Prewarp data containing the warp frequency. + * @return Discrete-time transfer function obtained from the prewarped bilinear transform. + */ constexpr TransferFunction analog_to_digital( const StateSpace &sys_c, T Ts, TustinPWData tag) { @@ -830,6 +896,20 @@ constexpr TransferFunction analog_to_digital( // path. template +/** + * @brief Discretizes a transfer function with ZOH using factored poles and zeros. + * + * Converts the analog transfer function to controllable canonical form, applies + * zero-order-hold discretization with the stored pole factors, and converts the + * result back to transfer-function form. + * + * @param b_c Continuous-time numerator coefficients. + * @param a_c Continuous-time denominator coefficients. + * @param factored_tf Analog pole/zero/gain data used to accelerate discretization. + * @param Ts Sampling interval. + * + * @return Discrete-time transfer function. + */ constexpr TransferFunction discretize_with_factored( const T (&b_c)[N + 1u], const T (&a_c)[N + 1u], const FactoredTF &factored_tf, T Ts, ZOH) @@ -839,6 +919,13 @@ constexpr TransferFunction discretize_with_factored( } template +/** + * @brief Discretizes a factored analog transfer function with matched-z mapping. + * + * @param factored_tf Analog poles, zeros, and gain. + * @param Ts Sampling period. + * @return TransferFunction Discrete transfer function. + */ constexpr TransferFunction discretize_with_factored( const T (& /*b_c*/)[N + 1u], const T (& /*a_c*/)[N + 1u], const FactoredTF &factored_tf, T Ts, MatchedZ) @@ -847,6 +934,12 @@ constexpr TransferFunction discretize_with_factored( } template +/** + * @brief Discretizes a continuous transfer function using the selected method. + * + * @param method_tag Discretization method tag. + * @return TransferFunction Discrete transfer function coefficients. + */ constexpr TransferFunction discretize_with_factored( const T (&b_c)[N + 1u], const T (&a_c)[N + 1u], const FactoredTF & /*factored_tf*/, T Ts, M method_tag) diff --git a/include/constfilt/elliptic.hpp b/include/constfilt/elliptic.hpp index af052af..6da2dc3 100644 --- a/include/constfilt/elliptic.hpp +++ b/include/constfilt/elliptic.hpp @@ -39,6 +39,14 @@ class Elliptic using BoundMethod = typename bind_method::type; public: + /** + * @brief Constructs an elliptic filter from the specified design parameters. + * + * @param cutoff_hz Cutoff frequency in hertz. + * @param ripple_db Passband ripple in decibels. + * @param attenuation_db Stopband attenuation in decibels. + * @param sample_rate_hz Sample rate in hertz. + */ constexpr Elliptic(T cutoff_hz, T ripple_db, T attenuation_db, T sample_rate_hz) : AnalogFilter( @@ -296,7 +304,13 @@ class Elliptic // In-place multiply polynomial poly (ascending order, currently degree // deg-1) by (s - root), bringing it to degree deg. Builds up the full // polynomial one root at a time: start with (s - r1), call with r2 to get - // (s - r1)(s - r2), call again with r3 to get (s - r1)(s - r2)(s - r3). + /** + * @brief Multiplies a polynomial by a linear factor in place. + * + * @param poly Polynomial coefficients in ascending order. + * @param deg Current polynomial degree. + * @param root Root of the factor to apply. + */ static constexpr void poly_mul_root(Complex (&poly)[N + 1u], consteig::Size deg, Complex root) { @@ -311,7 +325,20 @@ class Elliptic // Fills poles[] and zeros[] with the normalized (wc=1) prototype poles and // zeros derived from the elliptic machinery (steps 3-4 of elliptic_tf). // Poles: M conjugate pairs + optional real pole for odd N. - // Zeros: M conjugate pairs on the imaginary axis (+/-j*omega_z). + /** + * @brief Computes the normalized elliptic prototype poles and zeros. + * + * Fills the output arrays with the conjugate pole pairs and imaginary-axis zero pairs for the + * low-pass prototype, and appends the extra real pole for odd orders. + * + * @param q Nome parameter of the elliptic prototype. + * @param sig0 Pole-shift parameter derived from the passband ripple. + * @param k Elliptic modulus used by the prototype. + * @param poles Output array for the prototype poles. + * @param pole_cnt Number of poles written to @p poles. + * @param zeros Output array for the prototype zeros. + * @param zero_cnt Number of zeros written to @p zeros. + */ static constexpr void compute_prototype_poles_zeros( T q, T sig0, T k, Complex (&poles)[N], consteig::Size &pole_cnt, Complex (&zeros)[N], consteig::Size &zero_cnt) @@ -474,7 +501,16 @@ class Elliptic // // Computes the normalized LP prototype (wc = 1 rad/s), then: // a_hp[j] = a_lp[N-j] * wc^j - // b_hp[j] = b_lp[N-j] * wc^j + /** + * @brief Builds high-pass elliptic transfer-function coefficients. + * + * Produces the high-pass numerator and denominator polynomials from the normalized low-pass prototype + * and scales them by the cutoff frequency. + * + * @param wc Cutoff angular frequency. + * @param ripple_db Passband ripple in dB. + * @param attenuation_db Stopband attenuation in dB. + */ static constexpr void elliptic_tf(T wc, T ripple_db, T attenuation_db, T (&b)[N + 1u], T (&a)[N + 1u], HighPass) { @@ -491,7 +527,18 @@ class Elliptic } } - // LP FactoredTF: prototype poles/zeros scaled by wc; gain from polynomial. + /** + * @brief Builds the low-pass factored elliptic prototype. + * + * Computes the normalized prototype poles and zeros, scales them by the + * cutoff frequency, and derives the gain from the corresponding polynomial + * transfer function. + * + * @param cutoff_hz Passband cutoff frequency in hertz. + * @param ripple_db Passband ripple in decibels. + * @param attenuation_db Stopband attenuation in decibels. + * @return FactoredTF Factorized transfer function with scaled poles, zeros, and gain. + */ static constexpr FactoredTF compute_factored_tf(T cutoff_hz, T ripple_db, T attenuation_db, @@ -545,7 +592,14 @@ class Elliptic // HP FactoredTF: derive from LP prototype via LP-to-HP transform (s -> // wc/s). LP pole p_lp -> HP pole wc/p_lp; LP zero j*omega_z -> HP zero // -j*wc/omega_z. For odd N: one extra zero at s=0 (LP strictly proper -> HP - // has zero at origin). + /** + * @brief Computes the factored high-pass elliptic filter transfer function. + * + * @param cutoff_hz Cutoff frequency in hertz. + * @param ripple_db Passband ripple in decibels. + * @param attenuation_db Stopband attenuation in decibels. + * @return FactoredTF High-pass poles, zeros, gain, and zero count. + */ static constexpr FactoredTF compute_factored_tf(T cutoff_hz, T ripple_db, T attenuation_db,