Browse Source
- Consolidate 5 TEST_CASEs into 1 SCENARIO with 6 SECTIONs - Add tolerance constants (A_TOL, E_TOL, ANG_TOL, R_TOL, V_TOL) - Convert TOML to inline table syntax - Add precalc script for expected values - Update continue.md with tolerance reference table and strict widen policytest-refactor
4 changed files with 204 additions and 5 deletions
@ -0,0 +1,81 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
""" |
||||||
|
Precalculate expected values for test_cartesian_to_elements_basic.cpp. |
||||||
|
|
||||||
|
Usage: |
||||||
|
python3 scripts/precalc_cartesian_to_elements_basic.py |
||||||
|
|
||||||
|
Outputs C++-style comments with precalculated values for embedding in the test. |
||||||
|
""" |
||||||
|
|
||||||
|
import sys, math |
||||||
|
sys.path.insert(0, 'scripts') |
||||||
|
from sim_engine import orbital_to_cartesian, cartesian_to_orbital_elements, vmag, OrbitalElements, G |
||||||
|
|
||||||
|
# ============================================================================= |
||||||
|
# Test configuration: moderate eccentricity, zero inclination |
||||||
|
# ============================================================================= |
||||||
|
mu = G * 5.972e24 |
||||||
|
a = 1.5e7 |
||||||
|
e = 0.5 |
||||||
|
nu = 0.0 |
||||||
|
inc = 0.0 |
||||||
|
Omega = 0.0 |
||||||
|
omega = 0.0 |
||||||
|
|
||||||
|
elements = OrbitalElements(a=a, e=e, nu=nu, inc=inc, Omega=Omega, omega=omega) |
||||||
|
pos, vel = orbital_to_cartesian(elements, 5.972e24) |
||||||
|
|
||||||
|
r = vmag(pos) |
||||||
|
v = vmag(vel) |
||||||
|
|
||||||
|
# Round-trip: convert back |
||||||
|
elements_rt = cartesian_to_orbital_elements(pos, vel, 5.972e24) |
||||||
|
|
||||||
|
print("# Test: test_cartesian_to_elements_basic") |
||||||
|
print(f"#") |
||||||
|
print(f"# Original elements:") |
||||||
|
print(f"# a = {a:.6f}") |
||||||
|
print(f"# e = {e:.6f}") |
||||||
|
print(f"# nu = {nu:.6f}") |
||||||
|
print(f"# inc = {inc:.6f}") |
||||||
|
print(f"# Omega = {Omega:.6f}") |
||||||
|
print(f"# omega = {omega:.6f}") |
||||||
|
print(f"#") |
||||||
|
print(f"# State vectors from elements:") |
||||||
|
print(f"# pos = ({pos[0]:.6f}, {pos[1]:.6f}, {pos[2]:.6f}) m") |
||||||
|
print(f"# vel = ({vel[0]:.6f}, {vel[1]:.6f}, {vel[2]:.6f}) m/s") |
||||||
|
print(f"# r = {r:.6f} m") |
||||||
|
print(f"# v = {v:.6f} m/s") |
||||||
|
print(f"#") |
||||||
|
print(f"# Round-trip recovered elements:") |
||||||
|
print(f"# a = {elements_rt.a:.15f}") |
||||||
|
print(f"# e = {elements_rt.e:.15f}") |
||||||
|
print(f"# nu = {elements_rt.nu:.15f}") |
||||||
|
print(f"# inc = {elements_rt.inc:.15f}") |
||||||
|
print(f"# Omega = {elements_rt.Omega:.15f}") |
||||||
|
print(f"# omega = {elements_rt.omega:.15f}") |
||||||
|
print(f"#") |
||||||
|
print(f"# Errors:") |
||||||
|
print(f"# da = {abs(elements_rt.a - a):.2e}") |
||||||
|
print(f"# de = {abs(elements_rt.e - e):.2e}") |
||||||
|
print(f"# dnu = {abs(elements_rt.nu - nu):.2e}") |
||||||
|
print(f"# dinc = {abs(elements_rt.inc - inc):.2e}") |
||||||
|
print(f"# dOmega = {abs(elements_rt.Omega - Omega):.2e}") |
||||||
|
print(f"# domega = {abs(elements_rt.omega - omega):.2e}") |
||||||
|
print(f"# dr = {abs(r - r):.2e} (trivial)") |
||||||
|
print(f"# dv = {abs(v - v):.2e} (trivial)") |
||||||
|
|
||||||
|
# Re-convert recovered elements back to state vectors |
||||||
|
pos2, vel2 = orbital_to_cartesian(elements_rt, 5.972e24) |
||||||
|
r2 = vmag(pos2) |
||||||
|
v2 = vmag(vel2) |
||||||
|
|
||||||
|
print(f"#") |
||||||
|
print(f"# Reconstructed from recovered elements:") |
||||||
|
print(f"# pos = ({pos2[0]:.6f}, {pos2[1]:.6f}, {pos2[2]:.6f}) m") |
||||||
|
print(f"# vel = ({vel2[0]:.6f}, {vel2[1]:.6f}, {vel2[2]:.6f}) m/s") |
||||||
|
print(f"# r = {r2:.6f} m") |
||||||
|
print(f"# v = {v2:.6f} m/s") |
||||||
|
print(f"# dr = {abs(r2 - r):.2e} m") |
||||||
|
print(f"# dv = {abs(v2 - v):.2e} m/s") |
||||||
@ -0,0 +1,89 @@ |
|||||||
|
#include <catch2/catch_test_macros.hpp> |
||||||
|
#include <catch2/matchers/catch_matchers_floating_point.hpp> |
||||||
|
#include "../src/physics.h" |
||||||
|
#include "../src/orbital_mechanics.h" |
||||||
|
#include "../src/simulation.h" |
||||||
|
#include "../src/config_loader.h" |
||||||
|
#include <cmath> |
||||||
|
|
||||||
|
using Catch::Matchers::WithinAbs; |
||||||
|
|
||||||
|
SCENARIO("Cartesian ↔ orbital elements round-trip conversion", |
||||||
|
"[cartesian][elements][roundtrip]") { |
||||||
|
const double TIME_STEP = 60.0; |
||||||
|
const double parent_mass = 5.972e24; |
||||||
|
|
||||||
|
const double A_TOL = 1e-6; |
||||||
|
const double E_TOL = 1e-12; |
||||||
|
const double ANG_TOL = 1e-12; |
||||||
|
const double R_TOL = 1e-6; |
||||||
|
const double V_TOL = 1e-6; |
||||||
|
|
||||||
|
SimulationState* sim = create_simulation(10, 1, 0, TIME_STEP); |
||||||
|
REQUIRE(load_system_config(sim, "tests/test_cartesian_to_elements_basic.toml")); |
||||||
|
Spacecraft* craft = &sim->spacecraft[0]; |
||||||
|
|
||||||
|
const OrbitalElements& orig = craft->orbit; |
||||||
|
|
||||||
|
// Convert elements → state vectors
|
||||||
|
Vec3 pos, vel; |
||||||
|
orbital_elements_to_cartesian(orig, parent_mass, &pos, &vel); |
||||||
|
|
||||||
|
const double expected_r = vec3_magnitude(pos); // 7500000.0
|
||||||
|
const double expected_v = vec3_magnitude(vel); // 8928.484709...
|
||||||
|
|
||||||
|
// Round-trip: state vectors → elements
|
||||||
|
const OrbitalElements recovered = cartesian_to_orbital_elements(pos, vel, parent_mass); |
||||||
|
|
||||||
|
// Re-convert recovered elements → state vectors
|
||||||
|
Vec3 pos2, vel2; |
||||||
|
orbital_elements_to_cartesian(recovered, parent_mass, &pos2, &vel2); |
||||||
|
const double recovered_r = vec3_magnitude(pos2); |
||||||
|
const double recovered_v = vec3_magnitude(vel2); |
||||||
|
|
||||||
|
SECTION("elements round-trip: semi-major axis") { |
||||||
|
const double da = fabs(recovered.semi_major_axis - orig.semi_major_axis); |
||||||
|
INFO("Original a: " << orig.semi_major_axis); |
||||||
|
INFO("Recovered a: " << recovered.semi_major_axis); |
||||||
|
INFO("Error: " << da << " m"); |
||||||
|
REQUIRE_THAT(da, WithinAbs(0.0, A_TOL)); |
||||||
|
} |
||||||
|
|
||||||
|
SECTION("elements round-trip: eccentricity") { |
||||||
|
const double de = fabs(recovered.eccentricity - orig.eccentricity); |
||||||
|
INFO("Original e: " << orig.eccentricity); |
||||||
|
INFO("Recovered e: " << recovered.eccentricity); |
||||||
|
INFO("Error: " << de); |
||||||
|
REQUIRE_THAT(de, WithinAbs(0.0, E_TOL)); |
||||||
|
} |
||||||
|
|
||||||
|
SECTION("elements round-trip: true anomaly") { |
||||||
|
const double dnu = fabs(recovered.true_anomaly - orig.true_anomaly); |
||||||
|
INFO("Original nu: " << orig.true_anomaly); |
||||||
|
INFO("Recovered nu: " << recovered.true_anomaly); |
||||||
|
INFO("Error: " << dnu); |
||||||
|
REQUIRE_THAT(dnu, WithinAbs(0.0, ANG_TOL)); |
||||||
|
} |
||||||
|
|
||||||
|
SECTION("elements round-trip: inclination") { |
||||||
|
const double dinc = fabs(recovered.inclination - orig.inclination); |
||||||
|
INFO("Original inc: " << orig.inclination); |
||||||
|
INFO("Recovered inc: " << recovered.inclination); |
||||||
|
INFO("Error: " << dinc); |
||||||
|
REQUIRE_THAT(dinc, WithinAbs(0.0, ANG_TOL)); |
||||||
|
} |
||||||
|
|
||||||
|
SECTION("radius preservation") { |
||||||
|
INFO("Original r: " << expected_r); |
||||||
|
INFO("Recovered r: " << recovered_r); |
||||||
|
REQUIRE_THAT(recovered_r, WithinAbs(expected_r, R_TOL)); |
||||||
|
} |
||||||
|
|
||||||
|
SECTION("velocity magnitude preservation") { |
||||||
|
INFO("Original v: " << expected_v); |
||||||
|
INFO("Recovered v: " << recovered_v); |
||||||
|
REQUIRE_THAT(recovered_v, WithinAbs(expected_v, V_TOL)); |
||||||
|
} |
||||||
|
|
||||||
|
destroy_simulation(sim); |
||||||
|
} |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
# Basic elliptical orbit: a=15000km, e=0.5, zero inclination |
||||||
|
|
||||||
|
[[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 = "Test_Spacecraft" |
||||||
|
mass = 1000.0 |
||||||
|
parent_index = 0 |
||||||
|
orbit = { semi_major_axis = 1.5e7, eccentricity = 0.5, true_anomaly = 0.0, inclination = 0.0, longitude_of_ascending_node = 0.0, argument_of_periapsis = 0.0 } |
||||||
Loading…
Reference in new issue