blob: e07c4691cd240e5ee06c2b4b3af343bbdadab12e [file] [log] [blame]
Austin Schuhacd335a2017-01-01 16:20:54 -08001#include "frc971/control_loops/runge_kutta.h"
2
3#include "gtest/gtest.h"
4
5namespace frc971 {
6namespace control_loops {
7namespace testing {
8
9// Tests that integrating dx/dt = e^x works.
10TEST(RungeKuttaTest, Exponential) {
11 ::Eigen::Matrix<double, 1, 1> y0;
Austin Schuhf7466732023-02-20 22:11:41 -080012 y0(0, 0) = 1.0;
Austin Schuhacd335a2017-01-01 16:20:54 -080013
14 ::Eigen::Matrix<double, 1, 1> y1 = RungeKutta(
15 [](::Eigen::Matrix<double, 1, 1> x) {
16 ::Eigen::Matrix<double, 1, 1> y;
17 y(0, 0) = ::std::exp(x(0, 0));
18 return y;
19 },
20 y0, 0.1);
Austin Schuhf7466732023-02-20 22:11:41 -080021 EXPECT_NEAR(y1(0, 0), -std::log(std::exp(-1.0) - 0.1), 1e-5);
22}
23
24// Now do it with sub steps.
25TEST(RungeKuttaTest, ExponentialSteps) {
26 ::Eigen::Matrix<double, 1, 1> y0;
27 y0(0, 0) = 1.0;
28
29 ::Eigen::Matrix<double, 1, 1> y1 = RungeKuttaSteps(
30 [](::Eigen::Matrix<double, 1, 1> x) {
31 ::Eigen::Matrix<double, 1, 1> y;
32 y(0, 0) = ::std::exp(x(0, 0));
33 return y;
34 },
35 y0, 0.1, 10);
36 EXPECT_NEAR(y1(0, 0), -std::log(std::exp(-1.0) - 0.1), 1e-8);
Austin Schuhacd335a2017-01-01 16:20:54 -080037}
38
Austin Schuh268a94f2018-02-17 17:10:19 -080039// Tests that integrating dx/dt = e^x works when we provide a U.
40TEST(RungeKuttaTest, ExponentialWithU) {
41 ::Eigen::Matrix<double, 1, 1> y0;
42 y0(0, 0) = 0.0;
43
Austin Schuh9edb5df2018-12-23 09:03:15 +110044 ::Eigen::Matrix<double, 1, 1> y1 = RungeKuttaU(
Austin Schuh268a94f2018-02-17 17:10:19 -080045 [](::Eigen::Matrix<double, 1, 1> x, ::Eigen::Matrix<double, 1, 1> u) {
46 ::Eigen::Matrix<double, 1, 1> y;
47 y(0, 0) = ::std::exp(u(0, 0) * x(0, 0));
48 return y;
49 },
50 y0, (::Eigen::Matrix<double, 1, 1>() << 1.0).finished(), 0.1);
51 EXPECT_NEAR(y1(0, 0), ::std::exp(0.1) - ::std::exp(0), 1e-3);
52}
53
Austin Schuhca52a242018-12-23 09:19:29 +110054::Eigen::Matrix<double, 1, 1> RungeKuttaTimeVaryingSolution(double t) {
55 return (::Eigen::Matrix<double, 1, 1>()
56 << 12.0 * ::std::exp(t) / (::std::pow(::std::exp(t) + 1.0, 2.0)))
57 .finished();
58}
59
60// Tests RungeKutta with a time varying solution.
61// Now, lets test RK4 with a time varying solution. From
62// http://www2.hawaii.edu/~jmcfatri/math407/RungeKuttaTest.html:
63// x' = x (2 / (e^t + 1) - 1)
64//
65// The true (analytical) solution is:
66//
67// x(t) = 12 * e^t / ((e^t + 1)^2)
68TEST(RungeKuttaTest, RungeKuttaTimeVarying) {
69 ::Eigen::Matrix<double, 1, 1> y0 = RungeKuttaTimeVaryingSolution(5.0);
70
71 ::Eigen::Matrix<double, 1, 1> y1 = RungeKutta(
72 [](double t, ::Eigen::Matrix<double, 1, 1> x) {
73 return (::Eigen::Matrix<double, 1, 1>()
74 << x(0, 0) * (2.0 / (::std::exp(t) + 1.0) - 1.0))
75 .finished();
76 },
77 y0, 5.0, 1.0);
78 EXPECT_NEAR(y1(0, 0), RungeKuttaTimeVaryingSolution(6.0)(0, 0), 1e-3);
79}
80
Austin Schuhf7466732023-02-20 22:11:41 -080081// Now do it with a ton of sub steps.
82TEST(RungeKuttaTest, RungeKuttaTimeVaryingSteps) {
83 ::Eigen::Matrix<double, 1, 1> y0 = RungeKuttaTimeVaryingSolution(5.0);
84
85 ::Eigen::Matrix<double, 1, 1> y1 = RungeKuttaSteps(
86 [](double t, ::Eigen::Matrix<double, 1, 1> x) {
87 return (::Eigen::Matrix<double, 1, 1>()
88 << x(0, 0) * (2.0 / (::std::exp(t) + 1.0) - 1.0))
89 .finished();
90 },
91 y0, 5.0, 1.0, 10);
92 EXPECT_NEAR(y1(0, 0), RungeKuttaTimeVaryingSolution(6.0)(0, 0), 1e-7);
93}
94
Austin Schuhacd335a2017-01-01 16:20:54 -080095} // namespace testing
96} // namespace control_loops
97} // namespace frc971