Skip to content

Commit e698e2f

Browse files
move c2d from math to control analysis
1 parent b5a082b commit e698e2f

15 files changed

Lines changed: 131 additions & 232 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Refer to the documentation to quickly integrate and utilize the library's signal
1717
| Category | Description |
1818
|--------------------------------------------------------------------|----------------------------------------------------------------------|
1919
| [Analysis](doc/analysis/README.md) | FFT, Real-Input FFT (RFFT), Power Spectral Density, DCT, Window Functions, Signal Detectors, Convolution & Correlation, Goertzel Algorithm, Decibels |
20-
| [Control Analysis](doc/control_analysis/README.md) | Frequency Response, Root Locus, Controllability/Observability Matrices & Gramians |
20+
| [Control Analysis](doc/control_analysis/README.md) | Frequency Response, Root Locus, Controllability/Observability Matrices & Gramians, Continuous-to-Discrete |
2121
| [Controllers](doc/controllers/README.md) | Bang-Bang/Hysteresis, PID, LQR, LQI (Integral/Servo State Feedback), MPC, Saturation, Rate Limiter, Slew-Limited Saturation, Feedforward/2-DOF, Gain-Scheduled Controller, Lead-Lag Compensator, Luenberger Observer |
2222
| [Dynamics](doc/dynamics/README.md) | Euler-Lagrange, Newton-Euler, Recursive Newton-Euler, ABA |
2323
| [Estimators](doc/estimators/README.md) | Linear Regression, Polynomial Fitting, Yule-Walker (offline), Recursive Least Squares, LMS / NLMS Adaptive Filter (online), Consistency Metrics / NEES / NIS |
@@ -26,7 +26,7 @@ Refer to the documentation to quickly integrate and utilize the library's signal
2626
| [Neural Network](doc/neural_network/README.md) | Layers, activations, losses, model |
2727
| [Optimization](doc/optimization/README.md) | Gradient Descent |
2828
| [Regularization](doc/regularization/README.md) | L1 (Lasso), L2 (Ridge) |
29-
| [Math](doc/math/README.md) | CORDIC, Quaternion, MatrixNorms, Step Response Metrics, MatrixExponential, ContinuousToDiscrete |
29+
| [Math](doc/math/README.md) | CORDIC, Quaternion, MatrixNorms, Step Response Metrics, MatrixExponential |
3030
| [Solvers](doc/solvers/README.md) | Gaussian Elimination, Levinson-Durbin, Durand-Kerner, Cholesky, DARE, Runge-Kutta ODE Integrators (RK4 + Dormand-Prince), Spectral Radius & Discrete Stability Margin, QR Decomposition (Householder / Givens), LU Decomposition with Partial Pivoting |
3131
| [Performance Optimization](doc/performance-optimization/README.md) | Compiler optimizations, SIMD |
3232

File renamed without changes.

doc/control_analysis/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ Control system analysis tools for characterizing dynamic systems and feedback lo
99
| [Frequency Response](FrequencyResponse.md) | Magnitude and phase response of digital filters across the frequency spectrum |
1010
| [Root Locus](RootLocus.md) | Closed-loop pole trajectory analysis as a function of loop gain |
1111
| [Controllability/Observability](ControllabilityObservability.md) | Structural reachability and detectability analysis with Gramians |
12+
| [Continuous-to-Discrete](ContinuousToDiscrete.md) | Convert continuous (A,B,C,D) state-space to discrete via ZOH (Van Loan), Tustin/bilinear, and forward/backward Euler |

doc/math/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,3 @@ Core mathematical primitives for numerical computation.
1515
| [Matrix Operations](MatrixOperations.md) | Structural matrix utilities — `Symmetrize` (closest symmetric matrix) |
1616
| [Step Response Metrics](StepResponseMetrics.md) | Rise time, settling time, percent overshoot, peak time, and steady-state error from a bounded step-response vector |
1717
| [Matrix Exponential](MatrixExponential.md) | Scaling-and-squaring with diagonal (6,6) Padé approximant — exact ODE solution operator and discretisation engine |
18-
| [Continuous-to-Discrete](ContinuousToDiscrete.md) | Convert continuous (A,B,C,D) state-space to discrete via ZOH (Van Loan), Tustin/bilinear, and forward/backward Euler |

numerical/control_analysis/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ target_link_libraries(numerical.control_analysis ${NUMERICAL_VISIBILITY}
1212
)
1313

1414
target_sources(numerical.control_analysis PRIVATE
15+
ContinuousToDiscrete.hpp
1516
ControllabilityObservability.hpp
1617
FrequencyResponse.hpp
1718
RootLocus.hpp
1819
)
1920

2021
numerical_add_coverage_sources(numerical.control_analysis
22+
ContinuousToDiscrete.cpp
2123
ControllabilityObservability.cpp
2224
FrequencyResponse.cpp
2325
RootLocus.cpp
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include "numerical/control_analysis/ContinuousToDiscrete.hpp"
2+
3+
namespace control_analysis
4+
{
5+
template class ContinuousToDiscrete<float, 2, 1, 1>;
6+
}

numerical/math/ContinuousToDiscrete.hpp renamed to numerical/control_analysis/ContinuousToDiscrete.hpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
#endif
55
#include "numerical/math/CompilerOptimizations.hpp"
66
#include "numerical/math/LinearTimeInvariant.hpp"
7-
#include "numerical/math/LuFactorization.hpp"
87
#include "numerical/math/Matrix.hpp"
98
#include "numerical/math/MatrixExponential.hpp"
9+
#include "numerical/solvers/LuDecomposition.hpp"
1010
#include <cstddef>
1111
#include <type_traits>
1212

13-
namespace math
13+
namespace control_analysis
1414
{
1515
enum class DiscretizationMethod
1616
{
@@ -26,16 +26,16 @@ namespace math
2626
static_assert(std::is_floating_point_v<T>, "ContinuousToDiscrete supports floating-point types");
2727

2828
public:
29-
using SystemType = LinearTimeInvariant<T, StateSize, InputSize, OutputSize>;
29+
using SystemType = math::LinearTimeInvariant<T, StateSize, InputSize, OutputSize>;
3030

3131
ContinuousToDiscrete() = default;
3232

3333
OPTIMIZE_FOR_SPEED SystemType Convert(const SystemType& sys, T ts, DiscretizationMethod method);
3434

3535
private:
36-
MatrixExponential<T, StateSize + InputSize> expm{};
36+
[[no_unique_address]] math::MatrixExponential<T, StateSize + InputSize> expm{};
3737

38-
static SquareMatrix<T, StateSize> Invert(const SquareMatrix<T, StateSize>& a);
38+
static math::SquareMatrix<T, StateSize> Invert(const math::SquareMatrix<T, StateSize>& a);
3939

4040
SystemType Zoh(const SystemType& sys, T ts);
4141
SystemType Bilinear(const SystemType& sys, T ts);
@@ -44,10 +44,10 @@ namespace math
4444
};
4545

4646
template<typename T, std::size_t StateSize, std::size_t InputSize, std::size_t OutputSize>
47-
SquareMatrix<T, StateSize>
48-
ContinuousToDiscrete<T, StateSize, InputSize, OutputSize>::Invert(const SquareMatrix<T, StateSize>& a)
47+
math::SquareMatrix<T, StateSize>
48+
ContinuousToDiscrete<T, StateSize, InputSize, OutputSize>::Invert(const math::SquareMatrix<T, StateSize>& a)
4949
{
50-
LuFactorization<T, StateSize> lu{};
50+
solvers::LuDecomposition<T, StateSize> lu{};
5151
lu.Decompose(a);
5252
return lu.Inverse();
5353
}
@@ -74,7 +74,7 @@ namespace math
7474
typename ContinuousToDiscrete<T, StateSize, InputSize, OutputSize>::SystemType
7575
ContinuousToDiscrete<T, StateSize, InputSize, OutputSize>::Zoh(const SystemType& sys, T ts)
7676
{
77-
SquareMatrix<T, StateSize + InputSize> augmented{};
77+
math::SquareMatrix<T, StateSize + InputSize> augmented{};
7878

7979
for (std::size_t i = 0; i < StateSize; ++i)
8080
for (std::size_t j = 0; j < StateSize; ++j)
@@ -105,7 +105,7 @@ namespace math
105105
ContinuousToDiscrete<T, StateSize, InputSize, OutputSize>::Bilinear(const SystemType& sys, T ts)
106106
{
107107
const T alpha{ T{ 2 } / ts };
108-
const auto identity = SquareMatrix<T, StateSize>::Identity();
108+
const auto identity = math::SquareMatrix<T, StateSize>::Identity();
109109
const auto alphaI = identity * alpha;
110110
const auto lhs = alphaI - sys.A;
111111
const auto P = Invert(lhs);
@@ -128,7 +128,7 @@ namespace math
128128
ContinuousToDiscrete<T, StateSize, InputSize, OutputSize>::ForwardEuler(const SystemType& sys, T ts)
129129
{
130130
SystemType result{};
131-
const auto identity = SquareMatrix<T, StateSize>::Identity();
131+
const auto identity = math::SquareMatrix<T, StateSize>::Identity();
132132
result.A = identity + sys.A * ts;
133133
result.B = sys.B * ts;
134134
result.C = sys.C;
@@ -140,7 +140,7 @@ namespace math
140140
typename ContinuousToDiscrete<T, StateSize, InputSize, OutputSize>::SystemType
141141
ContinuousToDiscrete<T, StateSize, InputSize, OutputSize>::Backward(const SystemType& sys, T ts)
142142
{
143-
const auto identity = SquareMatrix<T, StateSize>::Identity();
143+
const auto identity = math::SquareMatrix<T, StateSize>::Identity();
144144
const auto lhs = identity - sys.A * ts;
145145
const auto P = Invert(lhs);
146146
const auto PB = P * sys.B;

numerical/control_analysis/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ target_link_libraries(numerical.control_analysis_test PUBLIC
99
)
1010

1111
target_sources(numerical.control_analysis_test PRIVATE
12+
TestContinuousToDiscrete.cpp
1213
TestControllabilityObservability.cpp
1314
TestFrequencyResponse.cpp
1415
TestRootLocus.cpp

numerical/math/test/TestContinuousToDiscrete.cpp renamed to numerical/control_analysis/test/TestContinuousToDiscrete.cpp

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#include "numerical/math/ContinuousToDiscrete.hpp"
1+
#include "numerical/control_analysis/ContinuousToDiscrete.hpp"
22
#include "numerical/math/Tolerance.hpp"
33
#include <cmath>
44
#include <gtest/gtest.h>
@@ -8,7 +8,7 @@ namespace
88
class TestContinuousToDiscrete : public ::testing::Test
99
{
1010
protected:
11-
math::ContinuousToDiscrete<float, 2, 1, 1> c2d{};
11+
control_analysis::ContinuousToDiscrete<float, 2, 1, 1> c2d{};
1212
math::LinearTimeInvariant<float, 2, 1, 1> continuousSys{};
1313
};
1414
}
@@ -21,7 +21,7 @@ TEST_F(TestContinuousToDiscrete, IntegratorZoh)
2121
continuousSys.D = math::Matrix<float, 1, 1>{ 0.0f };
2222

2323
const float ts{ 0.1f };
24-
auto result = c2d.Convert(continuousSys, ts, math::DiscretizationMethod::ZeroOrderHold);
24+
auto result = c2d.Convert(continuousSys, ts, control_analysis::DiscretizationMethod::ZeroOrderHold);
2525

2626
EXPECT_NEAR(result.A.at(0, 0), 1.0f, math::Tolerance<float>());
2727
EXPECT_NEAR(result.B.at(0, 0), ts, math::Tolerance<float>());
@@ -38,7 +38,7 @@ TEST_F(TestContinuousToDiscrete, FirstOrderZohMatchesAnalytic)
3838
continuousSys.C = math::Matrix<float, 1, 2>{ 1.0f, 0.0f };
3939
continuousSys.D = math::Matrix<float, 1, 1>{ 0.0f };
4040

41-
auto result = c2d.Convert(continuousSys, ts, math::DiscretizationMethod::ZeroOrderHold);
41+
auto result = c2d.Convert(continuousSys, ts, control_analysis::DiscretizationMethod::ZeroOrderHold);
4242

4343
const float expectedAd{ std::exp(-a * ts) };
4444
const float expectedBd{ (b / a) * (1.0f - std::exp(-a * ts)) };
@@ -54,7 +54,7 @@ TEST_F(TestContinuousToDiscrete, ZohPreservesCAndD)
5454
continuousSys.C = math::Matrix<float, 1, 2>{ 3.0f, 4.0f };
5555
continuousSys.D = math::Matrix<float, 1, 1>{ 5.0f };
5656

57-
auto result = c2d.Convert(continuousSys, 0.1f, math::DiscretizationMethod::ZeroOrderHold);
57+
auto result = c2d.Convert(continuousSys, 0.1f, control_analysis::DiscretizationMethod::ZeroOrderHold);
5858

5959
EXPECT_NEAR(result.C.at(0, 0), 3.0f, math::Tolerance<float>());
6060
EXPECT_NEAR(result.C.at(0, 1), 4.0f, math::Tolerance<float>());
@@ -69,7 +69,7 @@ TEST_F(TestContinuousToDiscrete, ForwardEulerFormula)
6969
continuousSys.D = math::Matrix<float, 1, 1>{ 0.0f };
7070

7171
const float ts{ 0.1f };
72-
auto result = c2d.Convert(continuousSys, ts, math::DiscretizationMethod::ForwardEuler);
72+
auto result = c2d.Convert(continuousSys, ts, control_analysis::DiscretizationMethod::ForwardEuler);
7373

7474
EXPECT_NEAR(result.A.at(0, 0), 1.0f + (-1.0f) * ts, math::Tolerance<float>());
7575
EXPECT_NEAR(result.A.at(1, 1), 1.0f + (-2.0f) * ts, math::Tolerance<float>());
@@ -87,7 +87,7 @@ TEST_F(TestContinuousToDiscrete, BackwardEulerFormula)
8787
continuousSys.C = math::Matrix<float, 1, 2>{ 1.0f, 0.0f };
8888
continuousSys.D = math::Matrix<float, 1, 1>{ 0.0f };
8989

90-
auto result = c2d.Convert(continuousSys, ts, math::DiscretizationMethod::BackwardEuler);
90+
auto result = c2d.Convert(continuousSys, ts, control_analysis::DiscretizationMethod::BackwardEuler);
9191

9292
const float expectedAd{ 1.0f / (1.0f + a * ts) };
9393
EXPECT_NEAR(result.A.at(0, 0), expectedAd, math::Tolerance<float>());
@@ -105,7 +105,7 @@ TEST_F(TestContinuousToDiscrete, TustinBilinearReference)
105105
continuousSys.C = math::Matrix<float, 1, 2>{ 1.0f, 0.0f };
106106
continuousSys.D = math::Matrix<float, 1, 1>{ 0.0f };
107107

108-
auto result = c2d.Convert(continuousSys, ts, math::DiscretizationMethod::Tustin);
108+
auto result = c2d.Convert(continuousSys, ts, control_analysis::DiscretizationMethod::Tustin);
109109

110110
const float expectedAd{ (alpha - a) / (alpha + a) };
111111
const float expectedBd{ b * 2.0f / (alpha + a) };
@@ -121,7 +121,7 @@ TEST_F(TestContinuousToDiscrete, TustinPreservesStability)
121121
continuousSys.C = math::Matrix<float, 1, 2>{ 1.0f, 0.0f };
122122
continuousSys.D = math::Matrix<float, 1, 1>{ 0.0f };
123123

124-
auto result = c2d.Convert(continuousSys, 0.05f, math::DiscretizationMethod::Tustin);
124+
auto result = c2d.Convert(continuousSys, 0.05f, control_analysis::DiscretizationMethod::Tustin);
125125

126126
EXPECT_LT(std::abs(result.A.at(0, 0)), 1.0f);
127127
EXPECT_LT(std::abs(result.A.at(1, 1)), 1.0f);
@@ -137,9 +137,9 @@ TEST_F(TestContinuousToDiscrete, SmallTsMethodsConverge)
137137
continuousSys.C = math::Matrix<float, 1, 2>{ 1.0f, 0.0f };
138138
continuousSys.D = math::Matrix<float, 1, 1>{ 0.0f };
139139

140-
auto zoh = c2d.Convert(continuousSys, ts, math::DiscretizationMethod::ZeroOrderHold);
141-
auto tustin = c2d.Convert(continuousSys, ts, math::DiscretizationMethod::Tustin);
142-
auto euler = c2d.Convert(continuousSys, ts, math::DiscretizationMethod::ForwardEuler);
140+
auto zoh = c2d.Convert(continuousSys, ts, control_analysis::DiscretizationMethod::ZeroOrderHold);
141+
auto tustin = c2d.Convert(continuousSys, ts, control_analysis::DiscretizationMethod::Tustin);
142+
auto euler = c2d.Convert(continuousSys, ts, control_analysis::DiscretizationMethod::ForwardEuler);
143143

144144
EXPECT_NEAR(zoh.A.at(0, 0), tustin.A.at(0, 0), 1e-3f);
145145
EXPECT_NEAR(zoh.A.at(0, 0), euler.A.at(0, 0), 1e-3f);
@@ -160,7 +160,7 @@ TEST_F(TestContinuousToDiscrete, DcGainPreserved)
160160

161161
const float continuousDcGain{ b / a };
162162

163-
auto discrete = c2d.Convert(continuousSys, ts, math::DiscretizationMethod::ZeroOrderHold);
163+
auto discrete = c2d.Convert(continuousSys, ts, control_analysis::DiscretizationMethod::ZeroOrderHold);
164164

165165
const float oneMinusAd{ 1.0f - discrete.A.at(0, 0) };
166166
const float discreteDcGain{ discrete.C.at(0, 0) * discrete.B.at(0, 0) / oneMinusAd + discrete.D.at(0, 0) };

numerical/math/CMakeLists.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,12 @@ target_link_libraries(numerical.math ${NUMERICAL_VISIBILITY}
1212
target_sources(numerical.math PRIVATE
1313
AdvancedFunctions.hpp
1414
ComplexNumber.hpp
15-
ContinuousToDiscrete.hpp
1615
Cordic.hpp
1716
Geometry3D.hpp
1817
GivensRotation.hpp
1918
HouseholderTransform.hpp
2019
HyperbolicFunctions.hpp
2120
LinearTimeInvariant.hpp
22-
LuFactorization.hpp
2321
Matrix.hpp
2422
MatrixExponential.hpp
2523
MatrixNorms.hpp
@@ -38,10 +36,8 @@ target_sources(numerical.math PRIVATE
3836

3937
numerical_add_coverage_sources(numerical.math
4038
ComplexNumber.cpp
41-
ContinuousToDiscrete.cpp
4239
Cordic.cpp
4340
LinearTimeInvariant.cpp
44-
LuFactorization.cpp
4541
Matrix.cpp
4642
MatrixExponential.cpp
4743
QNumber.cpp

0 commit comments

Comments
 (0)