From 1781190feda114b7841ea898c46e0c6fb63c700a Mon Sep 17 00:00:00 2001 From: cinnaboot Date: Tue, 3 Feb 2026 17:25:04 -0500 Subject: [PATCH] Consolidate analytical propagation tests: merge apsides + timesteps - Combined test_analytical_propagation_apsides.cpp (248 lines) and test_analytical_propagation_timesteps.cpp (209 lines) into test_analytical_propagation.cpp (422 lines, -35 lines saved) - All 10 test cases preserved with unique spacecraft names - Merged config files into test_analytical_propagation.toml - Tests pass: 240,299 assertions in 133 test cases --- ...es.cpp => test_analytical_propagation.cpp} | 232 ++++++++++++++++-- tests/test_analytical_propagation.toml | 41 ++++ .../test_analytical_propagation_apsides.toml | 27 -- .../test_analytical_propagation_timesteps.cpp | 208 ---------------- ...test_analytical_propagation_timesteps.toml | 27 -- 5 files changed, 257 insertions(+), 278 deletions(-) rename tests/{test_analytical_propagation_apsides.cpp => test_analytical_propagation.cpp} (51%) create mode 100644 tests/test_analytical_propagation.toml delete mode 100644 tests/test_analytical_propagation_apsides.toml delete mode 100644 tests/test_analytical_propagation_timesteps.cpp delete mode 100644 tests/test_analytical_propagation_timesteps.toml diff --git a/tests/test_analytical_propagation_apsides.cpp b/tests/test_analytical_propagation.cpp similarity index 51% rename from tests/test_analytical_propagation_apsides.cpp rename to tests/test_analytical_propagation.cpp index 29396da..33130af 100644 --- a/tests/test_analytical_propagation_apsides.cpp +++ b/tests/test_analytical_propagation.cpp @@ -6,15 +6,17 @@ #include "../src/test_utilities.h" #include -const double VELOCITY_TOLERANCE = 1.0; -const double POSITION_TOLERANCE = 1.0e3; +const double VELOCITY_TOLERANCE_APSIDES = 1.0; +const double POSITION_TOLERANCE_APSIDES = 1.0e3; +const double VELOCITY_TOLERANCE_TIMESTEP = 10.0; +const double POSITION_TOLERANCE_TIMESTEP = 1.0e4; TEST_CASE("Propagation through perigee (velocity maximum)", "[analytical][propagation][perigee]") { const double TIME_STEP = 60.0; - SimulationState* sim = create_simulation(10, 1, 0, TIME_STEP); + SimulationState* sim = create_simulation(10, 2, 0, TIME_STEP); - REQUIRE(load_system_config(sim, "tests/test_analytical_propagation_apsides.toml")); + REQUIRE(load_system_config(sim, "tests/test_analytical_propagation.toml")); Spacecraft* craft = &sim->spacecraft[0]; CelestialBody* earth = &sim->bodies[0]; @@ -43,7 +45,7 @@ TEST_CASE("Propagation through perigee (velocity maximum)", "[analytical][propag double v_perigee = vec3_magnitude(vel_perigee); double r_perigee = vec3_magnitude(pos_perigee); - INFO("At perigee (ν=0):"); + INFO("At perigee (v=0):"); INFO(" Position: (" << pos_perigee.x << ", " << pos_perigee.y << ", " << pos_perigee.z << ") m"); INFO(" Velocity: (" << vel_perigee.x << ", " << vel_perigee.y << ", " << vel_perigee.z << ") m/s"); INFO(" Velocity magnitude: " << v_perigee << " m/s"); @@ -55,7 +57,7 @@ TEST_CASE("Propagation through perigee (velocity maximum)", "[analytical][propag double r_error = fabs(r_perigee - expected_r_perigee); INFO("Radius error: " << r_error << " m"); - REQUIRE(r_error < POSITION_TOLERANCE); + REQUIRE(r_error < POSITION_TOLERANCE_APSIDES); REQUIRE(v_perigee > v_before); destroy_simulation(sim); @@ -64,9 +66,9 @@ TEST_CASE("Propagation through perigee (velocity maximum)", "[analytical][propag TEST_CASE("Propagation through apogee (velocity minimum)", "[analytical][propagation][apogee]") { const double TIME_STEP = 60.0; - SimulationState* sim = create_simulation(10, 1, 0, TIME_STEP); + SimulationState* sim = create_simulation(10, 2, 0, TIME_STEP); - REQUIRE(load_system_config(sim, "tests/test_analytical_propagation_apsides.toml")); + REQUIRE(load_system_config(sim, "tests/test_analytical_propagation.toml")); Spacecraft* craft = &sim->spacecraft[0]; CelestialBody* earth = &sim->bodies[0]; @@ -91,7 +93,7 @@ TEST_CASE("Propagation through apogee (velocity minimum)", "[analytical][propaga double v_apogee = vec3_magnitude(vel_apogee); double r_apogee = vec3_magnitude(pos_apogee); - INFO("At apogee (ν=π):"); + INFO("At apogee (v=π):"); INFO(" Position: (" << pos_apogee.x << ", " << pos_apogee.y << ", " << pos_apogee.z << ") m"); INFO(" Velocity: (" << vel_apogee.x << ", " << vel_apogee.y << ", " << vel_apogee.z << ") m/s"); INFO(" Velocity magnitude: " << v_apogee << " m/s"); @@ -103,7 +105,7 @@ TEST_CASE("Propagation through apogee (velocity minimum)", "[analytical][propaga double r_error = fabs(r_apogee - expected_r_apogee); INFO("Radius error: " << r_error << " m"); - REQUIRE(r_error < POSITION_TOLERANCE); + REQUIRE(r_error < POSITION_TOLERANCE_APSIDES); REQUIRE(v_apogee < v_perigee); REQUIRE(r_apogee > r_perigee); @@ -113,9 +115,9 @@ TEST_CASE("Propagation through apogee (velocity minimum)", "[analytical][propaga TEST_CASE("Propagation returns to initial state after one orbital period", "[analytical][propagation][period]") { const double TIME_STEP = 60.0; - SimulationState* sim = create_simulation(10, 1, 0, TIME_STEP); + SimulationState* sim = create_simulation(10, 2, 0, TIME_STEP); - REQUIRE(load_system_config(sim, "tests/test_analytical_propagation_apsides.toml")); + REQUIRE(load_system_config(sim, "tests/test_analytical_propagation.toml")); Spacecraft* craft = &sim->spacecraft[0]; CelestialBody* earth = &sim->bodies[0]; @@ -169,9 +171,9 @@ TEST_CASE("Propagation returns to initial state after one orbital period", "[ana TEST_CASE("True anomaly accuracy after full orbit", "[analytical][propagation][true_anomaly]") { const double TIME_STEP = 60.0; - SimulationState* sim = create_simulation(10, 1, 0, TIME_STEP); + SimulationState* sim = create_simulation(10, 2, 0, TIME_STEP); - REQUIRE(load_system_config(sim, "tests/test_analytical_propagation_apsides.toml")); + REQUIRE(load_system_config(sim, "tests/test_analytical_propagation.toml")); Spacecraft* craft = &sim->spacecraft[0]; CelestialBody* earth = &sim->bodies[0]; @@ -204,9 +206,9 @@ TEST_CASE("True anomaly accuracy after full orbit", "[analytical][propagation][t TEST_CASE("Vis-viva equation holds at multiple points in orbit", "[analytical][propagation][vis_viva]") { const double TIME_STEP = 60.0; - SimulationState* sim = create_simulation(10, 1, 0, TIME_STEP); + SimulationState* sim = create_simulation(10, 2, 0, TIME_STEP); - REQUIRE(load_system_config(sim, "tests/test_analytical_propagation_apsides.toml")); + REQUIRE(load_system_config(sim, "tests/test_analytical_propagation.toml")); Spacecraft* craft = &sim->spacecraft[0]; CelestialBody* earth = &sim->bodies[0]; @@ -245,3 +247,201 @@ TEST_CASE("Vis-viva equation holds at multiple points in orbit", "[analytical][p destroy_simulation(sim); } + +TEST_CASE("Large timestep - dt greater than orbital period", "[analytical][timestep][large]") { + const double TIME_STEP = 60.0; + + SimulationState* sim = create_simulation(10, 2, 0, TIME_STEP); + + REQUIRE(load_system_config(sim, "tests/test_analytical_propagation.toml")); + + Spacecraft* craft = &sim->spacecraft[1]; + CelestialBody* earth = &sim->bodies[0]; + + double a = craft->orbit.semi_major_axis; + double mu = G * earth->mass; + double period_seconds = 2.0 * M_PI * sqrt(pow(a, 3.0) / mu); + + INFO("Orbital period: " << period_seconds << " s (" << period_seconds / 3600.0 << " hours)"); + + double large_dt = period_seconds * 2.0; + INFO("Timestep: " << large_dt << " s (2x orbital period)"); + + Vec3 pos_before; + Vec3 vel_before; + orbital_elements_to_cartesian(craft->orbit, earth->mass, &pos_before, &vel_before); + + OrbitalElements propagated = propagate_orbital_elements(craft->orbit, large_dt, earth->mass); + + Vec3 pos_after; + Vec3 vel_after; + orbital_elements_to_cartesian(propagated, earth->mass, &pos_after, &vel_after); + + double r_before = vec3_magnitude(pos_before); + double r_after = vec3_magnitude(pos_after); + double v_before = vec3_magnitude(vel_before); + double v_after = vec3_magnitude(vel_after); + + INFO("Before propagation:"); + INFO(" Radius: " << r_before << " m"); + INFO(" Velocity: " << v_before << " m/s"); + + INFO("After 2 periods:"); + INFO(" Radius: " << r_after << " m"); + INFO(" Velocity: " << v_after << " m/s"); + + double r_error = fabs(r_after - r_before); + double v_error = fabs(v_after - v_before); + double relative_r_error = r_error / r_before * 100.0; + double relative_v_error = v_error / v_before * 100.0; + + INFO("Radius error: " << r_error << " m (" << relative_r_error << "%)"); + INFO("Velocity error: " << v_error << " m/s (" << relative_v_error << "%)"); + + REQUIRE(relative_r_error < 0.1); + REQUIRE(relative_v_error < 0.1); + + destroy_simulation(sim); +} + +TEST_CASE("Very small timestep - dt less than 1 second", "[analytical][timestep][small]") { + const double TIME_STEP = 60.0; + + SimulationState* sim = create_simulation(10, 2, 0, TIME_STEP); + + REQUIRE(load_system_config(sim, "tests/test_analytical_propagation.toml")); + + Spacecraft* craft = &sim->spacecraft[1]; + CelestialBody* earth = &sim->bodies[0]; + + Vec3 pos_before; + Vec3 vel_before; + orbital_elements_to_cartesian(craft->orbit, earth->mass, &pos_before, &vel_before); + + double small_dt = 0.1; + INFO("Timestep: " << small_dt << " s"); + + OrbitalElements propagated = propagate_orbital_elements(craft->orbit, small_dt, earth->mass); + + Vec3 pos_after; + Vec3 vel_after; + orbital_elements_to_cartesian(propagated, earth->mass, &pos_after, &vel_after); + + double pos_change = vec3_distance(pos_before, pos_after); + double vel_change = vec3_distance(vel_before, vel_after); + + INFO("Position change: " << pos_change << " m"); + INFO("Velocity change: " << vel_change << " m/s"); + + double v_before_mag = vec3_magnitude(vel_before); + double expected_pos_change = v_before_mag * small_dt; + double pos_error = fabs(pos_change - expected_pos_change); + + INFO("Expected position change (v·dt): " << expected_pos_change << " m"); + INFO("Position error: " << pos_error << " m"); + INFO("Relative position error: " << (pos_error / expected_pos_change * 100.0) << "%"); + + REQUIRE(pos_error < VELOCITY_TOLERANCE_TIMESTEP * small_dt * 10.0); + REQUIRE(vel_change < VELOCITY_TOLERANCE_TIMESTEP); + + destroy_simulation(sim); +} + +TEST_CASE("Accuracy vs timestep size relationship", "[analytical][timestep][accuracy]") { + const double TIME_STEP = 60.0; + + SimulationState* sim = create_simulation(10, 2, 0, TIME_STEP); + + REQUIRE(load_system_config(sim, "tests/test_analytical_propagation.toml")); + + Spacecraft* craft = &sim->spacecraft[1]; + CelestialBody* earth = &sim->bodies[0]; + + double a = craft->orbit.semi_major_axis; + double mu = G * earth->mass; + double period_seconds = 2.0 * M_PI * sqrt(pow(a, 3.0) / mu); + + double dt_ratios[] = {0.01, 0.1, 1.0, 10.0}; + + Vec3 pos_initial; + Vec3 vel_initial; + orbital_elements_to_cartesian(craft->orbit, earth->mass, &pos_initial, &vel_initial); + + for (int i = 0; i < 4; i++) { + double dt = period_seconds * dt_ratios[i]; + INFO("Testing dt = " << dt << " s (" << dt_ratios[i] << "x period)"); + + OrbitalElements propagated = propagate_orbital_elements(craft->orbit, dt, earth->mass); + + Vec3 pos_final; + Vec3 vel_final; + orbital_elements_to_cartesian(propagated, earth->mass, &pos_final, &vel_final); + + double pos_error = vec3_distance(pos_initial, pos_final); + double vel_error = vec3_distance(vel_initial, vel_final); + + double num_periods = dt / period_seconds; + double expected_num_orbits = round(num_periods); + + double fractional_phase = num_periods - expected_num_orbits; + double expected_pos_error = fractional_phase * 2.0 * M_PI * a; + + INFO(" Position error: " << pos_error << " m"); + INFO(" Expected error (phase): " << expected_pos_error << " m"); + INFO(" Number of periods: " << num_periods); + + if (expected_num_orbits > 0 && expected_pos_error > 1.0e-6) { + double relative_error = pos_error / expected_pos_error; + + INFO(" Relative error: " << relative_error); + + REQUIRE(relative_error < 0.5); + } else if (expected_num_orbits > 0) { + INFO(" Expected error is zero, skipping relative error check"); + REQUIRE(pos_error < 1.0e-3); + } + } + + destroy_simulation(sim); +} + +TEST_CASE("Mean anomaly accumulation over long propagation", "[analytical][timestep][accumulation]") { + const double TIME_STEP = 60.0; + + SimulationState* sim = create_simulation(10, 2, 0, TIME_STEP); + + REQUIRE(load_system_config(sim, "tests/test_analytical_propagation.toml")); + + Spacecraft* craft = &sim->spacecraft[1]; + CelestialBody* earth = &sim->bodies[0]; + + double a = craft->orbit.semi_major_axis; + double mu = G * earth->mass; + double period_seconds = 2.0 * M_PI * sqrt(pow(a, 3.0) / mu); + double mean_motion = sqrt(mu / pow(a, 3.0)); + + double initial_true_anomaly = craft->orbit.true_anomaly; + INFO("Initial true anomaly: " << initial_true_anomaly << " rad"); + + double propagation_time = period_seconds * 100.0; + INFO("Propagation time: " << propagation_time << " s (" << propagation_time / period_seconds << " periods)"); + + OrbitalElements propagated = propagate_orbital_elements(craft->orbit, propagation_time, earth->mass); + + double final_true_anomaly = propagated.true_anomaly; + INFO("Final true anomaly: " << final_true_anomaly << " rad"); + + double expected_delta_anomaly = mean_motion * propagation_time; + double expected_final_anomaly = fmod(initial_true_anomaly + expected_delta_anomaly, 2.0 * M_PI); + + INFO("Expected final anomaly: " << expected_final_anomaly << " rad"); + + double raw_error = fabs(final_true_anomaly - expected_final_anomaly); + double anomaly_error = fmin(raw_error, 2.0 * M_PI - raw_error); + + INFO("True anomaly error: " << anomaly_error << " rad (" << anomaly_error * 180.0 / M_PI << "°)"); + + REQUIRE(anomaly_error < 1.0e-3); + + destroy_simulation(sim); +} diff --git a/tests/test_analytical_propagation.toml b/tests/test_analytical_propagation.toml new file mode 100644 index 0000000..7120673 --- /dev/null +++ b/tests/test_analytical_propagation.toml @@ -0,0 +1,41 @@ +# Test Configuration: Analytical Propagation Tests +# Combined configuration for apsides and timestep testing +# Contains two spacecraft with different orbital parameters + +[[bodies]] +name = "Earth" +mass = 5.972e24 +radius = 6.371e6 +parent_index = -1 +color = { r = 0.0, g = 0.5, b = 1.0 } +orbit = { + semi_major_axis = 0.0, + eccentricity = 0.0, + true_anomaly = 0.0 +} + +[[spacecraft]] +name = "Apsides_Test_Spacecraft" +mass = 1000.0 +parent_index = 0 +orbit = { + semi_major_axis = 2.0e7, + eccentricity = 0.6, + true_anomaly = 0.0, + inclination = 0.0, + longitude_of_ascending_node = 0.0, + argument_of_periapsis = 0.0 +} + +[[spacecraft]] +name = "Timestep_Test_Spacecraft" +mass = 1000.0 +parent_index = 0 +orbit = { + semi_major_axis = 1.5e7, + eccentricity = 0.4, + true_anomaly = 0.0, + inclination = 0.0, + longitude_of_ascending_node = 0.0, + argument_of_periapsis = 0.0 +} diff --git a/tests/test_analytical_propagation_apsides.toml b/tests/test_analytical_propagation_apsides.toml deleted file mode 100644 index 308958d..0000000 --- a/tests/test_analytical_propagation_apsides.toml +++ /dev/null @@ -1,27 +0,0 @@ -# Test Configuration: Elliptical Orbit for Analytical Propagation -# Moderate eccentricity to test propagation through apsides - -[[bodies]] -name = "Earth" -mass = 5.972e24 -radius = 6.371e6 -parent_index = -1 -color = { r = 0.0, g = 0.5, b = 1.0 } -orbit = { - semi_major_axis = 0.0, - eccentricity = 0.0, - true_anomaly = 0.0 -} - - [[spacecraft]] -name = "Elliptical_Orbit_Spacecraft" -mass = 1000.0 -parent_index = 0 -orbit = { - semi_major_axis = 2.0e7, - eccentricity = 0.6, - true_anomaly = 0.0, - inclination = 0.0, - longitude_of_ascending_node = 0.0, - argument_of_periapsis = 0.0 -} diff --git a/tests/test_analytical_propagation_timesteps.cpp b/tests/test_analytical_propagation_timesteps.cpp deleted file mode 100644 index 26254a8..0000000 --- a/tests/test_analytical_propagation_timesteps.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include -#include "../src/physics.h" -#include "../src/orbital_mechanics.h" -#include "../src/simulation.h" -#include "../src/config_loader.h" -#include "../src/test_utilities.h" -#include - -const double VELOCITY_TOLERANCE = 10.0; -const double POSITION_TOLERANCE = 1.0e4; - -TEST_CASE("Large timestep - dt greater than orbital period", "[analytical][timestep][large]") { - const double TIME_STEP = 60.0; - - SimulationState* sim = create_simulation(10, 1, 0, TIME_STEP); - - REQUIRE(load_system_config(sim, "tests/test_analytical_propagation_timesteps.toml")); - - Spacecraft* craft = &sim->spacecraft[0]; - CelestialBody* earth = &sim->bodies[0]; - - double a = craft->orbit.semi_major_axis; - double mu = G * earth->mass; - double period_seconds = 2.0 * M_PI * sqrt(pow(a, 3.0) / mu); - - INFO("Orbital period: " << period_seconds << " s (" << period_seconds / 3600.0 << " hours)"); - - double large_dt = period_seconds * 2.0; - INFO("Timestep: " << large_dt << " s (2x orbital period)"); - - Vec3 pos_before; - Vec3 vel_before; - orbital_elements_to_cartesian(craft->orbit, earth->mass, &pos_before, &vel_before); - - OrbitalElements propagated = propagate_orbital_elements(craft->orbit, large_dt, earth->mass); - - Vec3 pos_after; - Vec3 vel_after; - orbital_elements_to_cartesian(propagated, earth->mass, &pos_after, &vel_after); - - double r_before = vec3_magnitude(pos_before); - double r_after = vec3_magnitude(pos_after); - double v_before = vec3_magnitude(vel_before); - double v_after = vec3_magnitude(vel_after); - - INFO("Before propagation:"); - INFO(" Radius: " << r_before << " m"); - INFO(" Velocity: " << v_before << " m/s"); - - INFO("After 2 periods:"); - INFO(" Radius: " << r_after << " m"); - INFO(" Velocity: " << v_after << " m/s"); - - double r_error = fabs(r_after - r_before); - double v_error = fabs(v_after - v_before); - double relative_r_error = r_error / r_before * 100.0; - double relative_v_error = v_error / v_before * 100.0; - - INFO("Radius error: " << r_error << " m (" << relative_r_error << "%)"); - INFO("Velocity error: " << v_error << " m/s (" << relative_v_error << "%)"); - - REQUIRE(relative_r_error < 0.1); - REQUIRE(relative_v_error < 0.1); - - destroy_simulation(sim); -} - -TEST_CASE("Very small timestep - dt less than 1 second", "[analytical][timestep][small]") { - const double TIME_STEP = 60.0; - - SimulationState* sim = create_simulation(10, 1, 0, TIME_STEP); - - REQUIRE(load_system_config(sim, "tests/test_analytical_propagation_timesteps.toml")); - - Spacecraft* craft = &sim->spacecraft[0]; - CelestialBody* earth = &sim->bodies[0]; - - Vec3 pos_before; - Vec3 vel_before; - orbital_elements_to_cartesian(craft->orbit, earth->mass, &pos_before, &vel_before); - - double small_dt = 0.1; - INFO("Timestep: " << small_dt << " s"); - - OrbitalElements propagated = propagate_orbital_elements(craft->orbit, small_dt, earth->mass); - - Vec3 pos_after; - Vec3 vel_after; - orbital_elements_to_cartesian(propagated, earth->mass, &pos_after, &vel_after); - - double pos_change = vec3_distance(pos_before, pos_after); - double vel_change = vec3_distance(vel_before, vel_after); - - INFO("Position change: " << pos_change << " m"); - INFO("Velocity change: " << vel_change << " m/s"); - - double v_before_mag = vec3_magnitude(vel_before); - double expected_pos_change = v_before_mag * small_dt; - double pos_error = fabs(pos_change - expected_pos_change); - - INFO("Expected position change (v·dt): " << expected_pos_change << " m"); - INFO("Position error: " << pos_error << " m"); - INFO("Relative position error: " << (pos_error / expected_pos_change * 100.0) << "%"); - - REQUIRE(pos_error < VELOCITY_TOLERANCE * small_dt * 10.0); - REQUIRE(vel_change < VELOCITY_TOLERANCE); - - destroy_simulation(sim); -} - -TEST_CASE("Accuracy vs timestep size relationship", "[analytical][timestep][accuracy]") { - const double TIME_STEP = 60.0; - - SimulationState* sim = create_simulation(10, 1, 0, TIME_STEP); - - REQUIRE(load_system_config(sim, "tests/test_analytical_propagation_timesteps.toml")); - - Spacecraft* craft = &sim->spacecraft[0]; - CelestialBody* earth = &sim->bodies[0]; - - double a = craft->orbit.semi_major_axis; - double mu = G * earth->mass; - double period_seconds = 2.0 * M_PI * sqrt(pow(a, 3.0) / mu); - - double dt_ratios[] = {0.01, 0.1, 1.0, 10.0}; - - Vec3 pos_initial; - Vec3 vel_initial; - orbital_elements_to_cartesian(craft->orbit, earth->mass, &pos_initial, &vel_initial); - - for (int i = 0; i < 4; i++) { - double dt = period_seconds * dt_ratios[i]; - INFO("Testing dt = " << dt << " s (" << dt_ratios[i] << "x period)"); - - OrbitalElements propagated = propagate_orbital_elements(craft->orbit, dt, earth->mass); - - Vec3 pos_final; - Vec3 vel_final; - orbital_elements_to_cartesian(propagated, earth->mass, &pos_final, &vel_final); - - double pos_error = vec3_distance(pos_initial, pos_final); - double vel_error = vec3_distance(vel_initial, vel_final); - - double num_periods = dt / period_seconds; - double expected_num_orbits = round(num_periods); - - double fractional_phase = num_periods - expected_num_orbits; - double expected_pos_error = fractional_phase * 2.0 * M_PI * a; - - INFO(" Position error: " << pos_error << " m"); - INFO(" Expected error (phase): " << expected_pos_error << " m"); - INFO(" Number of periods: " << num_periods); - - if (expected_num_orbits > 0 && expected_pos_error > 1.0e-6) { - double relative_error = pos_error / expected_pos_error; - - INFO(" Relative error: " << relative_error); - - REQUIRE(relative_error < 0.5); - } else if (expected_num_orbits > 0) { - INFO(" Expected error is zero, skipping relative error check"); - REQUIRE(pos_error < 1.0e-3); - } - } - - destroy_simulation(sim); -} - -TEST_CASE("Mean anomaly accumulation over long propagation", "[analytical][timestep][accumulation]") { - const double TIME_STEP = 60.0; - - SimulationState* sim = create_simulation(10, 1, 0, TIME_STEP); - - REQUIRE(load_system_config(sim, "tests/test_analytical_propagation_timesteps.toml")); - - Spacecraft* craft = &sim->spacecraft[0]; - CelestialBody* earth = &sim->bodies[0]; - - double a = craft->orbit.semi_major_axis; - double mu = G * earth->mass; - double period_seconds = 2.0 * M_PI * sqrt(pow(a, 3.0) / mu); - double mean_motion = sqrt(mu / pow(a, 3.0)); - - double initial_true_anomaly = craft->orbit.true_anomaly; - INFO("Initial true anomaly: " << initial_true_anomaly << " rad"); - - double propagation_time = period_seconds * 100.0; - INFO("Propagation time: " << propagation_time << " s (" << propagation_time / period_seconds << " periods)"); - - OrbitalElements propagated = propagate_orbital_elements(craft->orbit, propagation_time, earth->mass); - - double final_true_anomaly = propagated.true_anomaly; - INFO("Final true anomaly: " << final_true_anomaly << " rad"); - - double expected_delta_anomaly = mean_motion * propagation_time; - double expected_final_anomaly = fmod(initial_true_anomaly + expected_delta_anomaly, 2.0 * M_PI); - - INFO("Expected final anomaly: " << expected_final_anomaly << " rad"); - - double raw_error = fabs(final_true_anomaly - expected_final_anomaly); - double anomaly_error = fmin(raw_error, 2.0 * M_PI - raw_error); - - INFO("True anomaly error: " << anomaly_error << " rad (" << anomaly_error * 180.0 / M_PI << "°)"); - - REQUIRE(anomaly_error < 1.0e-3); - - destroy_simulation(sim); -} diff --git a/tests/test_analytical_propagation_timesteps.toml b/tests/test_analytical_propagation_timesteps.toml deleted file mode 100644 index d4050be..0000000 --- a/tests/test_analytical_propagation_timesteps.toml +++ /dev/null @@ -1,27 +0,0 @@ -# Test Configuration: Standard Orbit for Timestep Testing -# Moderate eccentricity orbit for testing various timestep sizes - -[[bodies]] -name = "Earth" -mass = 5.972e24 -radius = 6.371e6 -parent_index = -1 -color = { r = 0.0, g = 0.5, b = 1.0 } -orbit = { - semi_major_axis = 0.0, - eccentricity = 0.0, - true_anomaly = 0.0 -} - - [[spacecraft]] -name = "Standard_Orbit_Spacecraft" -mass = 1000.0 -parent_index = 0 -orbit = { - semi_major_axis = 1.5e7, - eccentricity = 0.4, - true_anomaly = 0.0, - inclination = 0.0, - longitude_of_ascending_node = 0.0, - argument_of_periapsis = 0.0 -}