Files
wrmath/test/madgwick_test.cc
Kyle Isom c47c91f418 Extend wrmath to match Rust library in astro-rs
Brings the C++ library in line with the Rust wrmath crate in astro-rs,
which extends this library with additional geometry and estimation
primitives. All changes generated with AI assistance (Claude Fable 5).

New headers:
- wrmath/geom/matrix.h: Matrix<T,M,N> with rank, det, inv, transpose,
  and matrix-vector/matrix-matrix multiplication
- wrmath/geom/coord2d.h: Polar<T> 2D polar coordinates with navigation
  convention (clockwise-positive heading)
- wrmath/geom/coord3d.h: Spherical<T> 3D spherical coordinates with
  yaw/pitch, slerp, great-circle path interpolation, and quaternion
  direction
- wrmath/estim/imu.h: IMU<T> for 6-DoF and 9-DoF (MARG) sensor fusion

Extensions to existing headers:
- math.h: Epsilon3/6/Max constants; AbsTolerance (NaN/inf-safe),
  AbsError, RotateRadians, Circumference
- vector.h: zero(), withEpsilon(), asArray(), fromArray/Eps(), map(),
  isNaN(), angle2() (signed), euclidist(), projectLower/Tail<M>(),
  x()/y()/z() accessors; fixed isParallel() to use unit-vector equality
  (matches Rust fix for macOS/arm64 acos domain issue)
- quaternion.h: lerp(), slerp() methods; jacobian() returning
  Matrix<T,3,4>; direction()
- filter/madgwick.h: beta gain field, setDeltaT(), setGain(),
  direction(), updateFrame2(), updateAngularOrientation2()
- orientation.h/.cc: RBearing3d/f, ABearing3d/f, CompassHeading3d/f

Infrastructure:
- C++ standard bumped to C++17 (required for std::optional)
- CMake include path fixed so source-relative includes work
- Umbrella headers (geom.h, filter.h) updated

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 22:34:49 -07:00

42 lines
1.1 KiB
C++

#include <cmath>
#include <sstream>
#include <gtest/gtest.h>
#include "wrmath/geom/vector.h"
#include "wrmath/geom/quaternion.h"
#include "wrmath/math.h"
#include "wrmath/filter/madgwick.h"
using namespace std;
using namespace wr;
TEST(MadgwickFilter, SimpleAngularOrientation)
{
filter::Madgwickd mf;
geom::Vector3d gyro {0.174533, 0.0, 0.0}; // 10° X rotation.
geom::Quaterniond frame20Deg {0.984808, 0.173648, 0, 0}; // 20° final orientation.
double delta = 0.00917; // assume 109 updates per second, as per the paper.
double twentyDegrees = math::DegreesToRadiansD(20.0);
// The paper specifies a minimum of 109 IMU readings to stabilize; for
// two seconds, that means 218 updates.
for (int i = 0; i < 218; i++) {
mf.updateAngularOrientation(gyro, delta);
}
EXPECT_EQ(mf.orientation(), frame20Deg);
auto euler = mf.euler();
EXPECT_NEAR(euler[0], twentyDegrees, 0.01);
EXPECT_NEAR(euler[1], 0.0, 0.01);
EXPECT_NEAR(euler[2], 0.0, 0.01);
}
int
main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}