From e831e76b3e773c7aeb522f918f019ed12f152e9d Mon Sep 17 00:00:00 2001 From: cinnaboot Date: Mon, 9 Feb 2026 08:27:09 -0500 Subject: [PATCH] Add test cases for prograde burn at periapsis Documents a bug where true anomaly triggers set to 0 (periapsis) don't execute because the spacecraft moves past periapsis before the trigger check happens. The trigger check occurs after physics propagation in the simulation update order. - test_periapsis_burn.cpp: Three test cases demonstrating expected behavior 1. Verify periapsis distance is preserved after prograde burn (fails - shows bug) 2. Verify apoapsis raises and periapsis stays same (skipped - depends on fix) 3. Verify burn location equals new periapsis (skipped - depends on fix) - test_periapsis_burn.toml: Test configuration with elliptical orbit satellite starting at periapsis with a true anomaly trigger at 0 --- tests/test_periapsis_burn.cpp | 123 +++++++++++++++++++++++++++++++++ tests/test_periapsis_burn.toml | 47 +++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 tests/test_periapsis_burn.cpp create mode 100644 tests/test_periapsis_burn.toml diff --git a/tests/test_periapsis_burn.cpp b/tests/test_periapsis_burn.cpp new file mode 100644 index 0000000..80d6606 --- /dev/null +++ b/tests/test_periapsis_burn.cpp @@ -0,0 +1,123 @@ +#include +#include +#include "../src/physics.h" +#include "../src/simulation.h" +#include "../src/spacecraft.h" +#include "../src/maneuver.h" +#include "../src/config_loader.h" +#include "../src/orbital_mechanics.h" +#include + +// This test documents a bug: when a true anomaly trigger is set to 0 (periapsis), +// the maneuver doesn't execute because the spacecraft moves past periapsis before +// the trigger check happens (trigger check occurs after physics update). +// The expected behavior is that the maneuver should execute when at periapsis. +TEST_CASE("Prograde burn at periapsis preserves periapsis distance", "[maneuver][periapsis][bug]") { + const double TIME_STEP = 60.0; + SimulationState* sim = create_simulation(10, 10, 100, TIME_STEP); + + REQUIRE(load_system_config(sim, "tests/test_periapsis_burn.toml")); + + Spacecraft* craft = &sim->spacecraft[0]; + + // Record initial state (at periapsis) + double initial_radius = vec3_magnitude(craft->local_position); + double initial_sma = craft->orbit.semi_major_axis; + double initial_ecc = craft->orbit.eccentricity; + double initial_periapsis = initial_sma * (1.0 - initial_ecc); + double initial_velocity = vec3_magnitude(craft->local_velocity); + + INFO("Initial state:"); + INFO(" Radius: " << initial_radius << " (should equal periapsis: " << initial_periapsis << ")"); + INFO(" True anomaly: " << craft->orbit.true_anomaly); + INFO(" Velocity: " << initial_velocity); + INFO(" Maneuver trigger: true_anomaly = " << sim->maneuvers[0].trigger_value); + + // Execute one step + update_simulation(sim); + + // Check if maneuver executed + INFO("After 1 step:"); + INFO(" Maneuver executed: " << sim->maneuvers[0].executed); + INFO(" Final radius: " << vec3_magnitude(craft->local_position)); + INFO(" Final velocity: " << vec3_magnitude(craft->local_velocity)); + + // The maneuver should have executed since we started at periapsis + // This assertion will fail due to the bug - the trigger check happens + // after physics moves the spacecraft past periapsis + REQUIRE(sim->maneuvers[0].executed); + + // If the maneuver executed, verify the physics: + double final_sma = craft->orbit.semi_major_axis; + double final_ecc = craft->orbit.eccentricity; + double final_periapsis = final_sma * (1.0 - final_ecc); + double final_velocity = vec3_magnitude(craft->local_velocity); + + // Periapsis distance should be preserved + REQUIRE_THAT(final_periapsis, Catch::Matchers::WithinAbs(initial_periapsis, 1.0)); + + // Semi-major axis and velocity should increase + REQUIRE(final_sma > initial_sma); + REQUIRE(final_velocity > initial_velocity); + + destroy_simulation(sim); +} + +TEST_CASE("Prograde burn at periapsis raises apoapsis not periapsis", "[maneuver][periapsis][apoapsis]") { + const double TIME_STEP = 60.0; + SimulationState* sim = create_simulation(10, 10, 100, TIME_STEP); + + REQUIRE(load_system_config(sim, "tests/test_periapsis_burn.toml")); + + Spacecraft* craft = &sim->spacecraft[0]; + + double initial_periapsis = craft->orbit.semi_major_axis * (1.0 - craft->orbit.eccentricity); + double initial_apoapsis = craft->orbit.semi_major_axis * (1.0 + craft->orbit.eccentricity); + + update_simulation(sim); + + // Skip verification if bug not fixed + if (!sim->maneuvers[0].executed) { + SKIP("Maneuver didn't execute - known bug"); + } + + double final_sma = craft->orbit.semi_major_axis; + double final_ecc = craft->orbit.eccentricity; + double final_periapsis = final_sma * (1.0 - final_ecc); + double final_apoapsis = final_sma * (1.0 + final_ecc); + + INFO("Initial: peri=" << initial_periapsis << " apo=" << initial_apoapsis); + INFO("Final: peri=" << final_periapsis << " apo=" << final_apoapsis); + + REQUIRE(final_apoapsis > initial_apoapsis); + REQUIRE_THAT(final_periapsis, Catch::Matchers::WithinAbs(initial_periapsis, 1.0)); + + destroy_simulation(sim); +} + +TEST_CASE("Burn location equals new periapsis after prograde burn", "[maneuver][periapsis][location]") { + const double TIME_STEP = 60.0; + SimulationState* sim = create_simulation(10, 10, 100, TIME_STEP); + + REQUIRE(load_system_config(sim, "tests/test_periapsis_burn.toml")); + + Spacecraft* craft = &sim->spacecraft[0]; + + double burn_radius = vec3_magnitude(craft->local_position); + + update_simulation(sim); + + // Skip verification if bug not fixed + if (!sim->maneuvers[0].executed) { + SKIP("Maneuver didn't execute - known bug"); + } + + double final_periapsis = craft->orbit.semi_major_axis * (1.0 - craft->orbit.eccentricity); + + INFO("Burn radius: " << burn_radius); + INFO("Final periapsis: " << final_periapsis); + + REQUIRE_THAT(burn_radius, Catch::Matchers::WithinAbs(final_periapsis, 1.0)); + + destroy_simulation(sim); +} diff --git a/tests/test_periapsis_burn.toml b/tests/test_periapsis_burn.toml new file mode 100644 index 0000000..45663df --- /dev/null +++ b/tests/test_periapsis_burn.toml @@ -0,0 +1,47 @@ +# Test Configuration: Prograde Burn at Periapsis +# Tests that periapsis distance is preserved during prograde burn + +[[bodies]] +name = "Sun" +mass = 1.989e30 +radius = 6.96e8 +parent_index = -1 +color = { r = 1.0, g = 1.0, b = 0.0 } +orbit = { + semi_major_axis = 0.0, + eccentricity = 0.0, + true_anomaly = 0.0 +} + +[[bodies]] +name = "Earth" +mass = 5.972e24 +radius = 6.371e6 +parent_index = 0 +color = { r = 0.0, g = 0.5, b = 1.0 } +orbit = { + semi_major_axis = 1.496e11, + eccentricity = 0.0, + true_anomaly = 0.0 +} + +[[spacecraft]] +name = "TestSatellite" +mass = 1000.0 +parent_index = 1 +# Start at periapsis of an elliptical orbit +# a = 10,000 km, e = 0.3 +# periapsis = 7,000 km, apoapsis = 13,000 km +orbit = { + semi_major_axis = 1.0371e7, + eccentricity = 0.3, + true_anomaly = 0.0 +} + +[[maneuvers]] +name = "periapsis_prograde_burn" +spacecraft_name = "TestSatellite" +trigger_type = "true_anomaly" +trigger_value = 0.0 +direction = "prograde" +delta_v = 500.0