Browse Source

refactor: true_anomaly_roundtrip - SCENARIO/SECTION pattern, tight tolerances

test-refactor
cinnaboot 3 months ago
parent
commit
9c455ccea5
  1. 116
      tests/test_true_anomaly_roundtrip.cpp

116
tests/test_true_anomaly_roundtrip.cpp

@ -4,13 +4,19 @@
#include "../src/orbital_mechanics.h" #include "../src/orbital_mechanics.h"
#include <cmath> #include <cmath>
TEST_CASE("True anomaly round-trip conversion at periapsis", "[orbital_elements][true_anomaly]") { SCENARIO("True anomaly round-trip conversion preserves values",
double parent_mass = 5.972e24; "[orbital_elements][true_anomaly]") {
const double parent_mass = 5.972e24;
const double a = 7000e3;
const double e = 0.3;
SECTION("at periapsis (nu = 0)") {
const double expected_nu = 0.0;
OrbitalElements elements = {0}; OrbitalElements elements = {0};
elements.semi_major_axis = 7000e3; elements.semi_major_axis = a;
elements.eccentricity = 0.3; elements.eccentricity = e;
elements.true_anomaly = 0.0; elements.true_anomaly = expected_nu;
elements.inclination = 0.0; elements.inclination = 0.0;
elements.longitude_of_ascending_node = 0.0; elements.longitude_of_ascending_node = 0.0;
elements.argument_of_periapsis = 0.0; elements.argument_of_periapsis = 0.0;
@ -19,20 +25,20 @@ TEST_CASE("True anomaly round-trip conversion at periapsis", "[orbital_elements]
orbital_elements_to_cartesian(elements, parent_mass, &pos, &vel); orbital_elements_to_cartesian(elements, parent_mass, &pos, &vel);
OrbitalElements reconstructed = cartesian_to_orbital_elements(pos, vel, parent_mass); OrbitalElements reconstructed = cartesian_to_orbital_elements(pos, vel, parent_mass);
INFO("Original true_anomaly: " << elements.true_anomaly); INFO("Expected nu: " << expected_nu);
INFO("Reconstructed true_anomaly: " << reconstructed.true_anomaly); INFO("Reconstructed: " << reconstructed.true_anomaly);
REQUIRE_THAT(reconstructed.true_anomaly, REQUIRE_THAT(reconstructed.true_anomaly,
Catch::Matchers::WithinAbs(elements.true_anomaly, 0.01)); Catch::Matchers::WithinAbs(expected_nu, 1e-12));
} }
TEST_CASE("True anomaly round-trip conversion at apoapsis", "[orbital_elements][true_anomaly]") { SECTION("at apoapsis (nu = pi)") {
double parent_mass = 5.972e24; const double expected_nu = M_PI;
OrbitalElements elements = {0}; OrbitalElements elements = {0};
elements.semi_major_axis = 7000e3; elements.semi_major_axis = a;
elements.eccentricity = 0.3; elements.eccentricity = e;
elements.true_anomaly = M_PI; elements.true_anomaly = expected_nu;
elements.inclination = 0.0; elements.inclination = 0.0;
elements.longitude_of_ascending_node = 0.0; elements.longitude_of_ascending_node = 0.0;
elements.argument_of_periapsis = 0.0; elements.argument_of_periapsis = 0.0;
@ -41,20 +47,20 @@ TEST_CASE("True anomaly round-trip conversion at apoapsis", "[orbital_elements][
orbital_elements_to_cartesian(elements, parent_mass, &pos, &vel); orbital_elements_to_cartesian(elements, parent_mass, &pos, &vel);
OrbitalElements reconstructed = cartesian_to_orbital_elements(pos, vel, parent_mass); OrbitalElements reconstructed = cartesian_to_orbital_elements(pos, vel, parent_mass);
INFO("Original true_anomaly: " << elements.true_anomaly); INFO("Expected nu: " << expected_nu);
INFO("Reconstructed true_anomaly: " << reconstructed.true_anomaly); INFO("Reconstructed: " << reconstructed.true_anomaly);
REQUIRE_THAT(reconstructed.true_anomaly, REQUIRE_THAT(reconstructed.true_anomaly,
Catch::Matchers::WithinAbs(elements.true_anomaly, 0.01)); Catch::Matchers::WithinAbs(expected_nu, 1e-12));
} }
TEST_CASE("True anomaly round-trip conversion at 90 degrees", "[orbital_elements][true_anomaly]") { SECTION("at 90 degrees (nu = pi/2)") {
double parent_mass = 5.972e24; const double expected_nu = M_PI / 2.0;
OrbitalElements elements = {0}; OrbitalElements elements = {0};
elements.semi_major_axis = 7000e3; elements.semi_major_axis = a;
elements.eccentricity = 0.3; elements.eccentricity = e;
elements.true_anomaly = M_PI / 2.0; elements.true_anomaly = expected_nu;
elements.inclination = 0.0; elements.inclination = 0.0;
elements.longitude_of_ascending_node = 0.0; elements.longitude_of_ascending_node = 0.0;
elements.argument_of_periapsis = 0.0; elements.argument_of_periapsis = 0.0;
@ -63,20 +69,20 @@ TEST_CASE("True anomaly round-trip conversion at 90 degrees", "[orbital_elements
orbital_elements_to_cartesian(elements, parent_mass, &pos, &vel); orbital_elements_to_cartesian(elements, parent_mass, &pos, &vel);
OrbitalElements reconstructed = cartesian_to_orbital_elements(pos, vel, parent_mass); OrbitalElements reconstructed = cartesian_to_orbital_elements(pos, vel, parent_mass);
INFO("Original true_anomaly: " << elements.true_anomaly); INFO("Expected nu: " << expected_nu);
INFO("Reconstructed true_anomaly: " << reconstructed.true_anomaly); INFO("Reconstructed: " << reconstructed.true_anomaly);
REQUIRE_THAT(reconstructed.true_anomaly, REQUIRE_THAT(reconstructed.true_anomaly,
Catch::Matchers::WithinAbs(elements.true_anomaly, 0.01)); Catch::Matchers::WithinAbs(expected_nu, 1e-12));
} }
TEST_CASE("True anomaly round-trip conversion at 270 degrees", "[orbital_elements][true_anomaly]") { SECTION("at 270 degrees (nu = 3*pi/2)") {
double parent_mass = 5.972e24; const double expected_nu = 3.0 * M_PI / 2.0;
OrbitalElements elements = {0}; OrbitalElements elements = {0};
elements.semi_major_axis = 7000e3; elements.semi_major_axis = a;
elements.eccentricity = 0.3; elements.eccentricity = e;
elements.true_anomaly = 3.0 * M_PI / 2.0; elements.true_anomaly = expected_nu;
elements.inclination = 0.0; elements.inclination = 0.0;
elements.longitude_of_ascending_node = 0.0; elements.longitude_of_ascending_node = 0.0;
elements.argument_of_periapsis = 0.0; elements.argument_of_periapsis = 0.0;
@ -85,49 +91,53 @@ TEST_CASE("True anomaly round-trip conversion at 270 degrees", "[orbital_element
orbital_elements_to_cartesian(elements, parent_mass, &pos, &vel); orbital_elements_to_cartesian(elements, parent_mass, &pos, &vel);
OrbitalElements reconstructed = cartesian_to_orbital_elements(pos, vel, parent_mass); OrbitalElements reconstructed = cartesian_to_orbital_elements(pos, vel, parent_mass);
INFO("Original true_anomaly: " << elements.true_anomaly); INFO("Expected nu: " << expected_nu);
INFO("Reconstructed true_anomaly: " << reconstructed.true_anomaly); INFO("Reconstructed: " << reconstructed.true_anomaly);
REQUIRE_THAT(reconstructed.true_anomaly, REQUIRE_THAT(reconstructed.true_anomaly,
Catch::Matchers::WithinAbs(elements.true_anomaly, 0.01)); Catch::Matchers::WithinAbs(expected_nu, 1e-12));
}
} }
TEST_CASE("Radius at periapsis matches expected value", "[orbital_elements][sanity]") { SCENARIO("Radius at periapsis and apoapsis matches analytical formula",
double parent_mass = 5.972e24; "[orbital_elements][sanity]") {
const double parent_mass = 5.972e24;
const double a = 7000e3;
const double e = 0.3;
const double expected_r_peri = a * (1.0 - e); // 4900000.0
const double expected_r_apo = a * (1.0 + e); // 9100000.0
SECTION("periapsis radius = a*(1-e)") {
OrbitalElements peri = {0}; OrbitalElements peri = {0};
peri.semi_major_axis = 7000e3; peri.semi_major_axis = a;
peri.eccentricity = 0.3; peri.eccentricity = e;
peri.true_anomaly = 0.0; peri.true_anomaly = 0.0;
Vec3 pos, vel; Vec3 pos, vel;
orbital_elements_to_cartesian(peri, parent_mass, &pos, &vel); orbital_elements_to_cartesian(peri, parent_mass, &pos, &vel);
double r_peri = vec3_magnitude(pos); const double r_peri = vec3_magnitude(pos);
double expected_peri = peri.semi_major_axis * (1.0 - peri.eccentricity);
INFO("At true_anomaly=0:"); INFO("Expected r: " << expected_r_peri);
INFO(" Calculated radius: " << r_peri); INFO("Calculated r: " << r_peri);
INFO(" Expected: " << expected_peri);
REQUIRE_THAT(r_peri, Catch::Matchers::WithinAbs(expected_peri, 1.0)); REQUIRE_THAT(r_peri,
Catch::Matchers::WithinAbs(expected_r_peri, 1e-6));
} }
TEST_CASE("Radius at apoapsis matches expected value", "[orbital_elements][sanity]") { SECTION("apoapsis radius = a*(1+e)") {
double parent_mass = 5.972e24;
OrbitalElements apo = {0}; OrbitalElements apo = {0};
apo.semi_major_axis = 7000e3; apo.semi_major_axis = a;
apo.eccentricity = 0.3; apo.eccentricity = e;
apo.true_anomaly = M_PI; apo.true_anomaly = M_PI;
Vec3 pos, vel; Vec3 pos, vel;
orbital_elements_to_cartesian(apo, parent_mass, &pos, &vel); orbital_elements_to_cartesian(apo, parent_mass, &pos, &vel);
double r_apo = vec3_magnitude(pos); const double r_apo = vec3_magnitude(pos);
double expected_apo = apo.semi_major_axis * (1.0 + apo.eccentricity);
INFO("At true_anomaly=pi:"); INFO("Expected r: " << expected_r_apo);
INFO(" Calculated radius: " << r_apo); INFO("Calculated r: " << r_apo);
INFO(" Expected: " << expected_apo);
REQUIRE_THAT(r_apo, Catch::Matchers::WithinAbs(expected_apo, 1.0)); REQUIRE_THAT(r_apo,
Catch::Matchers::WithinAbs(expected_r_apo, 1e-6));
}
} }

Loading…
Cancel
Save