SCRIPT Move subprojects

This commit is contained in:
PJ Reiniger
2025-11-07 19:55:36 -05:00
committed by Peter Johnson
parent 8cfc158790
commit a5492d30da
431 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <gtest/gtest.h>
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
return ret;
}

View File

@@ -0,0 +1,14 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <gtest/gtest.h>
#include "sysid/analysis/AnalysisType.h"
TEST(AnalysisTypeTest, FromName) {
EXPECT_EQ(sysid::analysis::kElevator, sysid::analysis::FromName("Elevator"));
EXPECT_EQ(sysid::analysis::kArm, sysid::analysis::FromName("Arm"));
EXPECT_EQ(sysid::analysis::kSimple, sysid::analysis::FromName("Simple"));
EXPECT_EQ(sysid::analysis::kSimple, sysid::analysis::FromName("Random"));
}

View File

@@ -0,0 +1,171 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <gtest/gtest.h>
#include "sysid/analysis/FeedbackAnalysis.h"
#include "sysid/analysis/FeedbackControllerPreset.h"
TEST(FeedbackAnalysisTest, VelocitySystem1) {
auto Kv = 3.060;
auto Ka = 0.327;
sysid::LQRParameters params{1, 1.5, 7};
auto [Kp, Kd] = sysid::CalculateVelocityFeedbackGains(
sysid::presets::kDefault, params, Kv, Ka);
EXPECT_NEAR(Kp, 2.11, 0.05);
EXPECT_NEAR(Kd, 0.00, 0.05);
}
TEST(FeedbackAnalysisTest, VelocitySystem2) {
auto Kv = 0.0693;
auto Ka = 0.1170;
sysid::LQRParameters params{1, 1.5, 7};
auto [Kp, Kd] = sysid::CalculateVelocityFeedbackGains(
sysid::presets::kDefault, params, Kv, Ka);
EXPECT_NEAR(Kp, 3.11, 0.05);
EXPECT_NEAR(Kd, 0.00, 0.05);
}
TEST(FeedbackAnalysisTest, VelocitySystemWithSmallKa) {
auto Kv = 3.060;
auto Ka = 0.0;
sysid::LQRParameters params{1, 1.5, 7};
auto [Kp, Kd] = sysid::CalculateVelocityFeedbackGains(
sysid::presets::kDefault, params, Kv, Ka);
EXPECT_NEAR(Kp, 0.00, 0.05);
EXPECT_NEAR(Kd, 0.00, 0.05);
}
TEST(FeedbackAnalysisTest, VelocityConversion) {
auto Kv = 0.0693;
auto Ka = 0.1170;
sysid::LQRParameters params{1, 1.5, 7};
auto [Kp, Kd] = sysid::CalculateVelocityFeedbackGains(
sysid::presets::kDefault, params, Kv, Ka, 3.0 * 1023);
// This should have the same Kp as the test above, but scaled by a factor of 3
// * 1023.
EXPECT_NEAR(Kp, 3.11 / (3 * 1023), 0.005);
EXPECT_NEAR(Kd, 0.00, 0.05);
}
TEST(FeedbackAnalysisTest, VelocityCTRE) {
auto Kv = 1.97;
auto Ka = 0.179;
sysid::LQRParameters params{1, 1.5, 7};
auto [Kp, Kd] = sysid::CalculateVelocityFeedbackGains(sysid::presets::kCTREv5,
params, Kv, Ka);
EXPECT_NEAR(Kp, 259.21276731541178, 0.00005);
EXPECT_NEAR(Kd, 0.00, 0.05);
}
TEST(FeedbackAnalysisTest, VelocityCTREConversion) {
auto Kv = 1.97;
auto Ka = 0.179;
sysid::LQRParameters params{1, 1.5, 7};
auto [Kp, Kd] = sysid::CalculateVelocityFeedbackGains(sysid::presets::kCTREv5,
params, Kv, Ka, 3.0);
// This should have the same Kp as the test above, but scaled by a factor
// of 3.
EXPECT_NEAR(Kp, 259.21276731541178 / 3, 0.00005);
EXPECT_NEAR(Kd, 0.00, 0.05);
}
TEST(FeedbackAnalysisTest, VelocityREV) {
auto Kv = 1.97;
auto Ka = 0.179;
sysid::LQRParameters params{1, 1.5, 7};
auto [Kp, Kd] = sysid::CalculateVelocityFeedbackGains(
sysid::presets::kREVNEOBuiltIn, params, Kv, Ka);
EXPECT_NEAR(Kp, 0.00241, 0.005);
EXPECT_NEAR(Kd, 0.00, 0.05);
}
TEST(FeedbackAnalysisTest, VelocityREVConversion) {
auto Kv = 1.97;
auto Ka = 0.179;
sysid::LQRParameters params{1, 1.5, 7};
auto [Kp, Kd] = sysid::CalculateVelocityFeedbackGains(
sysid::presets::kREVNEOBuiltIn, params, Kv, Ka, 3.0);
// This should have the same Kp as the test above, but scaled by a factor
// of 3.
EXPECT_NEAR(Kp, 0.00241 / 3, 0.005);
EXPECT_NEAR(Kd, 0.00, 0.05);
}
TEST(FeedbackAnalysisTest, Position) {
auto Kv = 3.060;
auto Ka = 0.327;
sysid::LQRParameters params{1, 1.5, 7};
auto [Kp, Kd] = sysid::CalculatePositionFeedbackGains(
sysid::presets::kDefault, params, Kv, Ka);
EXPECT_NEAR(Kp, 6.41, 0.05);
EXPECT_NEAR(Kd, 2.48, 0.05);
}
TEST(FeedbackAnalysisTest, PositionWithSmallKa) {
auto Kv = 3.060;
auto Ka = 1e-10;
sysid::LQRParameters params{1, 1.5, 7};
auto [Kp, Kd] = sysid::CalculatePositionFeedbackGains(
sysid::presets::kDefault, params, Kv, Ka);
EXPECT_NEAR(Kp, 19.97, 0.05);
EXPECT_NEAR(Kd, 0.00, 0.05);
}
TEST(FeedbackAnalysisTest, PositionWithLatencyCompensation) {
auto Kv = 3.060;
auto Ka = 0.327;
sysid::LQRParameters params{1, 1.5, 7};
sysid::FeedbackControllerPreset preset{sysid::presets::kDefault};
preset.measurementDelay = 10_ms;
auto [Kp, Kd] = sysid::CalculatePositionFeedbackGains(preset, params, Kv, Ka);
EXPECT_NEAR(Kp, 5.92, 0.05);
EXPECT_NEAR(Kd, 2.12, 0.05);
}
TEST(FeedbackAnalysisTest, PositionREV) {
auto Kv = 3.060;
auto Ka = 0.327;
sysid::LQRParameters params{1, 1.5, 7};
auto [Kp, Kd] = sysid::CalculatePositionFeedbackGains(
sysid::presets::kREVNEOBuiltIn, params, Kv, Ka);
EXPECT_NEAR(Kp, 0.30202, 0.05);
EXPECT_NEAR(Kd, 48.518, 0.05);
}

View File

@@ -0,0 +1,292 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <stdint.h>
#include <bitset>
#include <cmath>
#include <span>
#include <gtest/gtest.h>
#include <units/time.h>
#include <units/voltage.h>
#include "sysid/analysis/AnalysisManager.h"
#include "sysid/analysis/AnalysisType.h"
#include "sysid/analysis/ArmSim.h"
#include "sysid/analysis/ElevatorSim.h"
#include "sysid/analysis/FeedforwardAnalysis.h"
#include "sysid/analysis/SimpleMotorSim.h"
namespace {
enum Movements : uint32_t {
kSlowForward,
kSlowBackward,
kFastForward,
kFastBackward
};
inline constexpr int kMovementCombinations = 16;
/**
* Return simulated test data for a given simulation model.
*
* @tparam Model The model type.
* @param model The simulation model.
* @param movements Which movements to do.
*/
template <typename Model>
sysid::Storage CollectData(Model& model, std::bitset<4> movements) {
constexpr auto kUstep = 0.25_V / 1_s;
constexpr units::volt_t kUmax = 7_V;
constexpr units::second_t T = 5_ms;
constexpr units::second_t kTestDuration = 5_s;
sysid::Storage storage;
auto& [slowForward, slowBackward, fastForward, fastBackward] = storage;
auto voltage = 0_V;
// Slow forward
if (movements.test(Movements::kSlowForward)) {
model.Reset();
voltage = 0_V;
for (int i = 0; i < (kTestDuration / T).value(); ++i) {
slowForward.emplace_back(sysid::PreparedData{
i * T, voltage.value(), model.GetPosition(), model.GetVelocity(), T,
model.GetAcceleration(voltage), std::cos(model.GetPosition()),
std::sin(model.GetPosition())});
model.Update(voltage, T);
voltage += kUstep * T;
}
}
// Slow backward
if (movements.test(Movements::kSlowBackward)) {
model.Reset();
voltage = 0_V;
for (int i = 0; i < (kTestDuration / T).value(); ++i) {
slowBackward.emplace_back(sysid::PreparedData{
i * T, voltage.value(), model.GetPosition(), model.GetVelocity(), T,
model.GetAcceleration(voltage), std::cos(model.GetPosition()),
std::sin(model.GetPosition())});
model.Update(voltage, T);
voltage -= kUstep * T;
}
}
// Fast forward
if (movements.test(Movements::kFastForward)) {
model.Reset();
voltage = 0_V;
for (int i = 0; i < (kTestDuration / T).value(); ++i) {
fastForward.emplace_back(sysid::PreparedData{
i * T, voltage.value(), model.GetPosition(), model.GetVelocity(), T,
model.GetAcceleration(voltage), std::cos(model.GetPosition()),
std::sin(model.GetPosition())});
model.Update(voltage, T);
voltage = kUmax;
}
}
// Fast backward
if (movements.test(Movements::kFastBackward)) {
model.Reset();
voltage = 0_V;
for (int i = 0; i < (kTestDuration / T).value(); ++i) {
fastBackward.emplace_back(sysid::PreparedData{
i * T, voltage.value(), model.GetPosition(), model.GetVelocity(), T,
model.GetAcceleration(voltage), std::cos(model.GetPosition()),
std::sin(model.GetPosition())});
model.Update(voltage, T);
voltage = -kUmax;
}
}
return storage;
}
/**
* Asserts success if the gains contain NaNs or are too far from their expected
* values.
*
* @param expectedGains The expected feedforward gains.
* @param actualGains The calculated feedforward gains.
* @param tolerances The tolerances for the coefficient comparisons.
*/
testing::AssertionResult FitIsBad(std::span<const double> expectedGains,
std::span<const double> actualGains,
std::span<const double> tolerances) {
// Check for NaN
for (const auto& coeff : actualGains) {
if (std::isnan(coeff)) {
return testing::AssertionSuccess();
}
}
for (size_t i = 0; i < expectedGains.size(); ++i) {
if (std::abs(expectedGains[i] - actualGains[i]) >= tolerances[i]) {
return testing::AssertionSuccess();
}
}
auto result = testing::AssertionFailure();
result << "\n";
for (size_t i = 0; i < expectedGains.size(); ++i) {
if (i == 0) {
result << "Ks";
} else if (i == 1) {
result << "Kv";
} else if (i == 2) {
result << "Ka";
} else if (i == 3) {
result << "Kg";
} else if (i == 4) {
result << "offset";
}
result << ":\n";
result << " expected " << expectedGains[i] << ",\n";
result << " actual " << actualGains[i] << ",\n";
result << " diff " << std::abs(expectedGains[i] - actualGains[i]) << "\n";
}
return result;
}
/**
* Asserts that two arrays are equal.
*
* @param expected The expected array.
* @param actual The actual array.
* @param tolerances The tolerances for the element comparisons.
*/
void ExpectArrayNear(std::span<const double> expected,
std::span<const double> actual,
std::span<const double> tolerances) {
// Check size
const size_t size = expected.size();
EXPECT_EQ(size, actual.size());
EXPECT_EQ(size, tolerances.size());
// Check elements
for (size_t i = 0; i < size; ++i) {
EXPECT_NEAR(expected[i], actual[i], tolerances[i]) << "where i = " << i;
}
}
/**
* @tparam Model The model type.
* @param model The simulation model.
* @param type The analysis type.
* @param expectedGains The expected feedforward gains.
* @param tolerances The tolerances for the coefficient comparisons.
*/
template <typename Model>
void RunTests(Model& model, const sysid::AnalysisType& type,
std::span<const double> expectedGains,
std::span<const double> tolerances) {
// Iterate through all combinations of movements
for (int movements = 0; movements < kMovementCombinations; ++movements) {
try {
auto ff =
sysid::CalculateFeedforwardGains(CollectData(model, movements), type);
ExpectArrayNear(expectedGains, ff.coeffs, tolerances);
} catch (sysid::InsufficientSamplesError&) {
// If calculation threw an exception, confirm at least one of the gains
// doesn't match
auto ff = sysid::CalculateFeedforwardGains(CollectData(model, movements),
type, false);
EXPECT_TRUE(FitIsBad(expectedGains, ff.coeffs, tolerances));
}
}
}
} // namespace
TEST(FeedforwardAnalysisTest, Arm) {
{
constexpr double Ks = 1.01;
constexpr double Kv = 3.060;
constexpr double Ka = 0.327;
constexpr double Kg = 0.211;
for (const auto& offset : {-2.0, -1.0, 0.0, 1.0, 2.0}) {
sysid::ArmSim model{Ks, Kv, Ka, Kg, offset};
RunTests(model, sysid::analysis::kArm, {{Ks, Kv, Ka, Kg, offset}},
{{8e-3, 8e-3, 8e-3, 8e-3, 3e-2}});
}
}
{
constexpr double Ks = 0.547;
constexpr double Kv = 0.0693;
constexpr double Ka = 0.1170;
constexpr double Kg = 0.122;
for (const auto& offset : {-2.0, -1.0, 0.0, 1.0, 2.0}) {
sysid::ArmSim model{Ks, Kv, Ka, Kg, offset};
RunTests(model, sysid::analysis::kArm, {{Ks, Kv, Ka, Kg, offset}},
{{8e-3, 8e-3, 8e-3, 8e-3, 5e-2}});
}
}
}
TEST(FeedforwardAnalysisTest, Elevator) {
{
constexpr double Ks = 1.01;
constexpr double Kv = 3.060;
constexpr double Ka = 0.327;
constexpr double Kg = -0.211;
sysid::ElevatorSim model{Ks, Kv, Ka, Kg};
RunTests(model, sysid::analysis::kElevator, {{Ks, Kv, Ka, Kg}},
{{8e-3, 8e-3, 8e-3, 8e-3}});
}
{
constexpr double Ks = 0.547;
constexpr double Kv = 0.0693;
constexpr double Ka = 0.1170;
constexpr double Kg = -0.122;
sysid::ElevatorSim model{Ks, Kv, Ka, Kg};
RunTests(model, sysid::analysis::kElevator, {{Ks, Kv, Ka, Kg}},
{{8e-3, 8e-3, 8e-3, 8e-3}});
}
}
TEST(FeedforwardAnalysisTest, Simple) {
{
constexpr double Ks = 1.01;
constexpr double Kv = 3.060;
constexpr double Ka = 0.327;
sysid::SimpleMotorSim model{Ks, Kv, Ka};
RunTests(model, sysid::analysis::kSimple, {{Ks, Kv, Ka}},
{{8e-3, 8e-3, 8e-3}});
}
{
constexpr double Ks = 0.547;
constexpr double Kv = 0.0693;
constexpr double Ka = 0.1170;
sysid::SimpleMotorSim model{Ks, Kv, Ka};
RunTests(model, sysid::analysis::kSimple, {{Ks, Kv, Ka}},
{{8e-3, 8e-3, 8e-3}});
}
}

View File

@@ -0,0 +1,233 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <array>
#include <cmath>
#include <vector>
#include <gtest/gtest.h>
#include "sysid/analysis/AnalysisManager.h"
#include "sysid/analysis/FeedforwardAnalysis.h"
#include "sysid/analysis/FilteringUtils.h"
#include "sysid/analysis/Storage.h"
TEST(FilterTest, MedianFilter) {
std::vector<sysid::PreparedData> testData{
sysid::PreparedData{0_s, 0, 0, 0}, sysid::PreparedData{0_s, 0, 0, 1},
sysid::PreparedData{0_s, 0, 0, 10}, sysid::PreparedData{0_s, 0, 0, 5},
sysid::PreparedData{0_s, 0, 0, 3}, sysid::PreparedData{0_s, 0, 0, 0},
sysid::PreparedData{0_s, 0, 0, 1000}, sysid::PreparedData{0_s, 0, 0, 7},
sysid::PreparedData{0_s, 0, 0, 6}, sysid::PreparedData{0_s, 0, 0, 5}};
std::vector<sysid::PreparedData> expectedData{
sysid::PreparedData{0_s, 0, 0, 0}, sysid::PreparedData{0_s, 0, 0, 1},
sysid::PreparedData{0_s, 0, 0, 5}, sysid::PreparedData{0_s, 0, 0, 5},
sysid::PreparedData{0_s, 0, 0, 3}, sysid::PreparedData{0_s, 0, 0, 3},
sysid::PreparedData{0_s, 0, 0, 7}, sysid::PreparedData{0_s, 0, 0, 7},
sysid::PreparedData{0_s, 0, 0, 6}, sysid::PreparedData{0_s, 0, 0, 5}};
sysid::ApplyMedianFilter(&testData, 3);
EXPECT_EQ(expectedData, testData);
}
TEST(FilterTest, NoiseFloor) {
std::vector<sysid::PreparedData> testData = {
{0_s, 1, 2, 3, 5_ms, 0, 0}, {1_s, 1, 2, 3, 5_ms, 1, 0},
{2_s, 1, 2, 3, 5_ms, 2, 0}, {3_s, 1, 2, 3, 5_ms, 5, 0},
{4_s, 1, 2, 3, 5_ms, 0.35, 0}, {5_s, 1, 2, 3, 5_ms, 0.15, 0},
{6_s, 1, 2, 3, 5_ms, 0, 0}, {7_s, 1, 2, 3, 5_ms, 0.02, 0},
{8_s, 1, 2, 3, 5_ms, 0.01, 0}, {9_s, 1, 2, 3, 5_ms, 0, 0}};
double noiseFloor =
GetNoiseFloor(testData, 2, [](auto&& pt) { return pt.acceleration; });
EXPECT_NEAR(0.953, noiseFloor, 0.001);
}
void FillStepVoltageData(std::vector<sysid::PreparedData>& data) {
auto previousDatum = data.front();
for (size_t i = 1; i < data.size(); ++i) {
auto& datum = data.at(i);
datum.timestamp = previousDatum.timestamp + previousDatum.dt;
datum.position = 0.5 * previousDatum.acceleration *
units::math::pow<2>(previousDatum.dt).value() +
previousDatum.velocity * previousDatum.dt.value() +
previousDatum.position;
datum.velocity = previousDatum.velocity +
previousDatum.acceleration * previousDatum.dt.value();
previousDatum = datum;
}
}
TEST(FilterTest, StepTrim) {
{
std::vector<sysid::PreparedData> forwardTestData = {
{0_s, 1, 0, 0, 1_s, 0}, {0_s, 1, 0, 0, 1_s, 0.25},
{0_s, 1, 0, 0, 1_s, 10}, {0_s, 1, 0, 0, 1_s, 0.45},
{0_s, 1, 0, 0, 1_s, 0}, {0_s, 1, 0, 0, 1_s, 0.15},
{0_s, 1, 0, 0, 1_s, 0}, {0_s, 1, 0, 0, 1_s, 0.02},
{0_s, 1, 0, 0, 1_s, 0}, {0_s, 1, 0, 0, 0_s, 0},
};
FillStepVoltageData(forwardTestData);
auto maxTime = 9_s;
auto minTime = maxTime;
sysid::AnalysisManager::Settings settings;
auto [tempMinTime, positionDelay, velocityDelay] =
sysid::TrimStepVoltageData(&forwardTestData, &settings, minTime,
maxTime);
minTime = tempMinTime;
EXPECT_EQ(3, settings.stepTestDuration.value());
EXPECT_EQ(2, minTime.value());
}
{
std::vector<sysid::PreparedData> backwardsTestData = {
{0_s, -1, 0, 0, 1_s, 0}, {0_s, -1, 0, 0, 1_s, -0.46},
{0_s, -1, 0, 0, 1_s, -8}, {0_s, -1, 0, 0, 1_s, -0.32},
{0_s, -1, 0, 0, 1_s, -0.12}, {0_s, -1, 0, 0, 1_s, -0.08},
{0_s, -1, 0, 0, 1_s, -0.06}, {0_s, -1, 0, 0, 1_s, -0.02},
{0_s, -1, 0, 0, 1_s, 0}, {0_s, -1, 0, 0, 0_s, 0},
};
FillStepVoltageData(backwardsTestData);
auto maxTime = 9_s;
auto minTime = maxTime;
sysid::AnalysisManager::Settings settings;
auto [tempMinTime, positionDelay, velocityDelay] =
sysid::TrimStepVoltageData(&backwardsTestData, &settings, minTime,
maxTime);
minTime = tempMinTime;
EXPECT_EQ(3, settings.stepTestDuration.value());
EXPECT_EQ(2, minTime.value());
}
{
// Forward test but with an erroneous negative acceleration at the end
std::vector<sysid::PreparedData> noisyTestData = {
{0_s, 1, 0, 0, 1_s, 0}, {0_s, 1, 0, 0, 1_s, 0.41},
{0_s, 1, 0, 0, 1_s, 11.5}, {0_s, 1, 0, 0, 1_s, 1.2},
{0_s, 1, 0, 0, 1_s, 0.34}, {0_s, 1, 0, 0, 1_s, 0.25},
{0_s, 1, 0, 0, 1_s, 0.11}, {0_s, 1, 0, 0, 1_s, -0.08},
{0_s, 1, 0, 0, 1_s, -12}, {0_s, 1, 0, 0, 0_s, 0},
};
FillStepVoltageData(noisyTestData);
auto maxTime = 9_s;
auto minTime = maxTime;
sysid::AnalysisManager::Settings settings;
auto [tempMinTime, positionDelay, velocityDelay] =
sysid::TrimStepVoltageData(&noisyTestData, &settings, minTime, maxTime);
minTime = tempMinTime;
// Expect trimming to reject the erroneous peak negative accel,
// correctly picking up the max positive accel instead.
EXPECT_EQ(4, settings.stepTestDuration.value());
EXPECT_EQ(2, minTime.value());
}
}
template <int Derivative, int Samples, typename F, typename DfDx>
void AssertCentralResults(F&& f, DfDx&& dfdx, units::second_t h, double min,
double max) {
static_assert(Samples % 2 != 0, "Number of samples must be odd.");
auto filter = sysid::CentralFiniteDifference<Derivative, Samples>(h);
for (int i = min / h.value(); i < max / h.value(); ++i) {
// Let filter initialize
if (i < static_cast<int>(min / h.value()) + Samples) {
filter.Calculate(f(i * h.value()));
continue;
}
// For central finite difference, the derivative computed at this point is
// half the window size in the past.
// The order of accuracy is O(h^(N - d)) where N is number of stencil
// points and d is order of derivative
EXPECT_NEAR(dfdx((i - static_cast<int>((Samples - 1) / 2)) * h.value()),
filter.Calculate(f(i * h.value())),
std::pow(h.value(), Samples - Derivative));
}
}
/**
* Test central finite difference.
*/
TEST(LinearFilterOutputTest, CentralFiniteDifference) {
constexpr auto h = 5_ms;
AssertCentralResults<1, 3>(
[](double x) {
// f(x) = x²
return x * x;
},
[](double x) {
// df/dx = 2x
return 2.0 * x;
},
h, -20.0, 20.0);
AssertCentralResults<1, 3>(
[](double x) {
// f(x) = std::sin(x)
return std::sin(x);
},
[](double x) {
// df/dx = std::cos(x)
return std::cos(x);
},
h, -20.0, 20.0);
AssertCentralResults<1, 3>(
[](double x) {
// f(x) = ln(x)
return std::log(x);
},
[](double x) {
// df/dx = 1 / x
return 1.0 / x;
},
h, 1.0, 20.0);
AssertCentralResults<2, 5>(
[](double x) {
// f(x) = x^2
return x * x;
},
[](double x) {
// d²f/dx² = 2
return 2.0;
},
h, -20.0, 20.0);
AssertCentralResults<2, 5>(
[](double x) {
// f(x) = std::sin(x)
return std::sin(x);
},
[](double x) {
// d²f/dx² = -std::sin(x)
return -std::sin(x);
},
h, -20.0, 20.0);
AssertCentralResults<2, 5>(
[](double x) {
// f(x) = ln(x)
return std::log(x);
},
[](double x) {
// d²f/dx² = -1 / x²
return -1.0 / (x * x);
},
h, 1.0, 20.0);
}

View File

@@ -0,0 +1,42 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <gtest/gtest.h>
#include "sysid/analysis/OLS.h"
TEST(OLSTest, TwoVariablesTwoPoints) {
// (1, 3) and (2, 5). Should produce y = 2x + 1.
Eigen::MatrixXd X{{1.0, 1.0}, {1.0, 2.0}};
Eigen::VectorXd y{{3.0}, {5.0}};
auto [coeffs, rSquared, rmse] = sysid::OLS(X, y);
EXPECT_EQ(coeffs.size(), 2u);
EXPECT_NEAR(coeffs[0], 1.0, 1e-12);
EXPECT_NEAR(coeffs[1], 2.0, 1e-12);
EXPECT_DOUBLE_EQ(rSquared, 1.0);
}
TEST(OLSTest, TwoVariablesFivePoints) {
// (2, 4), (3, 5), (5, 7), (7, 10), (9, 15)
// Should produce 1.518x + 0.305.
Eigen::MatrixXd X{{1, 2}, {1, 3}, {1, 5}, {1, 7}, {1, 9}};
Eigen::VectorXd y{{4}, {5}, {7}, {10}, {15}};
auto [coeffs, rSquared, rmse] = sysid::OLS(X, y);
EXPECT_EQ(coeffs.size(), 2u);
EXPECT_NEAR(coeffs[0], 0.30487804878048774, 1e-12);
EXPECT_NEAR(coeffs[1], 1.5182926829268293, 1e-12);
EXPECT_DOUBLE_EQ(rSquared, 0.91906029466386019);
}
#ifndef NDEBUG
TEST(OLSTest, MalformedData) {
Eigen::MatrixXd X{{1, 2}, {1, 3}, {1, 4}};
Eigen::VectorXd y{{4}, {5}};
EXPECT_DEATH(sysid::OLS(X, y), "");
}
#endif

View File

@@ -0,0 +1,12 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <gtest/gtest.h>
#include "sysid/analysis/TrackwidthAnalysis.h"
TEST(TrackwidthAnalysisTest, Calculate) {
double result = sysid::CalculateTrackwidth(-0.5386, 0.5386, 90_deg);
EXPECT_NEAR(result, 0.6858, 1E-4);
}