From 136100219671ce87d9258d67dd99839ef3d88e10 Mon Sep 17 00:00:00 2001 From: cinnaboot Date: Thu, 30 Apr 2026 09:36:51 -0400 Subject: [PATCH] Add test refactoring status tracking and first refactored test - Add status section to continue.md: completed, can-refactor, blocked, skip - Add test_barkers_equation.cpp as first refactored test --- continue.md | 31 +++++++++ tests/test_barkers_equation.cpp | 112 ++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 tests/test_barkers_equation.cpp diff --git a/continue.md b/continue.md index ffce89b..c13e9f3 100644 --- a/continue.md +++ b/continue.md @@ -70,3 +70,34 @@ - **Always ask for review** before moving to the next file. - **Only commit when asked.** +--- + +## Test Refactoring Status + +### Completed +- `test_barkers_equation` ✅ — Barker's equation unit tests + parabolic propagation + +### Can Refactor Now (sim_engine.py supports all features needed) +- `test_cartesian_to_elements_basic` — element conversion round-trip +- `test_cartesian_to_elements_advanced` — advanced conversion cases +- `test_parabolic_orbit` — parabolic propagation via Barker's +- `test_extreme_eccentricity` — high-eccentricity orbits +- `test_extreme_orientation_mixed` — extreme inclinations/eccentricities +- `test_extreme_timescales` — various timescales +- `test_analytical_propagation` — propagation through apsides +- `test_moon_orbits` — multi-body propagation +- `test_periapsis_burn` — prograde burns +- `test_hybrid_burns` — impulse burns +- `test_omega_debug` — burn + element reconstruction + +### Blocked on Missing Features +- `test_soi_transition` — needs SOI transitions +- `test_root_body_transitions` — needs SOI transitions +- `test_maneuver_planning` — needs maneuver trigger system +- `test_hybrid_energy_conservation` — needs energy functions (KE, PE, total) +- `test_hyperbolic_orbit` — needs hyperbolic propagation +- `test_rendezvous` — needs Hohmann transfer calculations + +### Skip (Hardcoded / No TOML) +- `test_integration` — hardcoded vector tests, no TOML config + diff --git a/tests/test_barkers_equation.cpp b/tests/test_barkers_equation.cpp new file mode 100644 index 0000000..c2fc01b --- /dev/null +++ b/tests/test_barkers_equation.cpp @@ -0,0 +1,112 @@ +#include +#include +#include "../src/orbital_mechanics.h" +#include +#include + +using Catch::Matchers::WithinAbs; + +SCENARIO("Barker's equation solves parabolic mean anomaly", + "[barker][analytical][parabolic]") { + const double PARENT_MASS = 1.989e30; + const double TIME_STEP = 3600.0; + const int NUM_STEPS = 24; + + auto check_roundtrip = [&](double M_original, double tol) { + double nu = solve_barker_equation(M_original); + double D = tan(nu / 2.0); + double M_recovered = D + (D * D * D) / 3.0; + REQUIRE_THAT(M_recovered, WithinAbs(M_original, tol)); + }; + + SECTION("zero mean anomaly yields zero true anomaly") { + double M = 0.0; + double nu = solve_barker_equation(M); + REQUIRE_THAT(nu, WithinAbs(0.0, 1e-15)); + } + + SECTION("positive mean anomaly values") { + std::vector> tests = { + std::make_pair(0.1, 1e-14), std::make_pair(1.0, 1e-14), + std::make_pair(5.0, 1e-14), std::make_pair(20.0, 1e-13) + }; + for (const auto& p : tests) { + double nu = solve_barker_equation(p.first); + REQUIRE(nu > 0.0); + REQUIRE(nu < M_PI); + check_roundtrip(p.first, p.second); + } + } + + SECTION("negative mean anomaly values") { + std::vector> tests = { + std::make_pair(-0.1, 1e-14), std::make_pair(-1.0, 1e-14), + std::make_pair(-5.0, 1e-14) + }; + for (const auto& p : tests) { + double nu = solve_barker_equation(p.first); + REQUIRE(nu < 0.0); + REQUIRE(nu > -M_PI); + check_roundtrip(p.first, p.second); + } + } + + SECTION("round-trip across full range") { + std::vector test_values = {-10.0, -5.0, -1.0, -0.5, -0.1, + 0.0, 0.1, 0.5, 1.0, 5.0, 10.0}; + for (double M_original : test_values) { + check_roundtrip(M_original, 1e-13); + } + } + + SECTION("true anomaly stays within (-pi, pi) for M in [-50, 50]") { + for (double M = -50.0; M <= 50.0; M += 1.0) { + double nu = solve_barker_equation(M); + REQUIRE(nu > -M_PI * 0.99); + REQUIRE(nu < M_PI * 0.99); + } + } + + SECTION("parabolic orbit propagation preserves energy") { + OrbitalElements initial = {}; + initial.semi_latus_rectum = 2.992e11; + initial.eccentricity = 1.0; + initial.true_anomaly = 0.0; + initial.inclination = 0.0; + initial.longitude_of_ascending_node = 0.0; + initial.argument_of_periapsis = 0.0; + + Vec3 pos, vel; + orbital_elements_to_cartesian(initial, PARENT_MASS, &pos, &vel); + + const double initial_distance = vec3_magnitude(pos); + const double initial_velocity = vec3_magnitude(vel); + const double escape_velocity = sqrt(2.0 * G * PARENT_MASS / initial_distance); + + INFO("Initial distance: " << initial_distance / 1.496e11 << " AU"); + INFO("Initial velocity: " << initial_velocity / 1000.0 << " km/s"); + INFO("Escape velocity: " << escape_velocity / 1000.0 << " km/s"); + REQUIRE_THAT(initial_velocity, WithinAbs(escape_velocity, 1.0)); + + OrbitalElements current = initial; + for (int step = 0; step < NUM_STEPS; step++) { + current = propagate_orbital_elements(current, TIME_STEP, PARENT_MASS); + } + + Vec3 pos_final, vel_final; + orbital_elements_to_cartesian(current, PARENT_MASS, &pos_final, &vel_final); + + const double final_distance = vec3_magnitude(pos_final); + const double final_velocity = vec3_magnitude(vel_final); + const double final_escape_velocity = sqrt(2.0 * G * PARENT_MASS / final_distance); + + INFO("Final true anomaly: " << current.true_anomaly << " rad"); + INFO("Final distance: " << final_distance / 1.496e11 << " AU"); + INFO("Final velocity: " << final_velocity / 1000.0 << " km/s"); + INFO("Final escape velocity: " << final_escape_velocity / 1000.0 << " km/s"); + + REQUIRE(final_distance > initial_distance); + REQUIRE(final_velocity < initial_velocity); + REQUIRE_THAT(final_velocity, WithinAbs(final_escape_velocity, 1.0)); + } +}