vibe coding an orbital mechanics simulation to try out claude code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

274 lines
8.5 KiB

#include <catch2/catch_test_macros.hpp>
#include "../src/physics.h"
#include "../src/orbital_mechanics.h"
#include "../src/simulation.h"
#include "../src/config_loader.h"
#include <cmath>
#include <limits>
const double ELEMENT_TOLERANCE = 1.0e-6;
const double VELOCITY_TOLERANCE = 1.0e-3;
TEST_CASE("Perfect circle (e=0)", "[precision][boundary][circle]") {
const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, 3, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/test_precision_boundaries.toml"));
Spacecraft* circle = &sim->spacecraft[0];
CelestialBody* earth = &sim->bodies[0];
INFO("Testing circular orbit: e=" << circle->orbit.eccentricity);
Vec3 pos_1;
Vec3 vel_1;
orbital_elements_to_cartesian(circle->orbit, earth->mass, &pos_1, &vel_1);
double r_1 = vec3_magnitude(pos_1);
double v_1 = vec3_magnitude(vel_1);
INFO("Radius: " << r_1 << " m");
INFO("Velocity: " << v_1 << " m/s");
double expected_r = circle->orbit.semi_major_axis;
double mu = G * earth->mass;
double expected_v = sqrt(mu / expected_r);
double r_error = fabs(r_1 - expected_r);
double v_error = fabs(v_1 - expected_v);
INFO("Expected radius: " << expected_r << " m");
INFO("Expected velocity: " << expected_v << " m/s");
INFO("Radius error: " << r_error << " m");
INFO("Velocity error: " << v_error << " m/s");
REQUIRE(r_error < fabs(expected_r) * ELEMENT_TOLERANCE);
REQUIRE(v_error < VELOCITY_TOLERANCE);
double vis_viva = sqrt(mu * (2.0 / r_1 - 1.0 / circle->orbit.semi_major_axis));
double vis_viva_error = fabs(v_1 - vis_viva);
INFO("Vis-viva velocity: " << vis_viva << " m/s");
INFO("Vis-viva error: " << vis_viva_error << " m/s");
REQUIRE(vis_viva_error < VELOCITY_TOLERANCE);
destroy_simulation(sim);
}
TEST_CASE("Polar orbit (i=π/2)", "[precision][boundary][polar]") {
const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, 3, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/test_precision_boundaries.toml"));
Spacecraft* polar = &sim->spacecraft[1];
CelestialBody* earth = &sim->bodies[0];
INFO("Testing polar orbit: i=" << polar->orbit.inclination << " rad (" << polar->orbit.inclination * 180.0 / M_PI << "°)");
Vec3 pos;
Vec3 vel;
orbital_elements_to_cartesian(polar->orbit, earth->mass, &pos, &vel);
INFO("Position: (" << pos.x << ", " << pos.y << ", " << pos.z << ") m");
INFO("Velocity: (" << vel.x << ", " << vel.y << ", " << vel.z << ") m/s");
double r = vec3_magnitude(pos);
double v = vec3_magnitude(vel);
double expected_r = polar->orbit.semi_major_axis * (1.0 - polar->orbit.eccentricity * polar->orbit.eccentricity) / (1.0 + polar->orbit.eccentricity);
INFO("Expected radius: " << expected_r << " m");
INFO("Actual radius: " << r << " m");
double r_error = fabs(r - expected_r);
REQUIRE(r_error < fabs(expected_r) * ELEMENT_TOLERANCE);
double z_expected = r * sin(polar->orbit.inclination);
double z_actual = pos.z;
INFO("Expected Z: " << z_expected << " m");
INFO("Actual Z: " << z_actual << " m");
double z_error = fabs(z_actual - z_expected);
REQUIRE(z_error < fabs(expected_r) * ELEMENT_TOLERANCE);
destroy_simulation(sim);
}
TEST_CASE("Retrograde orbit (i=π)", "[precision][boundary][retrograde]") {
const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, 3, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/test_precision_boundaries.toml"));
Spacecraft* retrograde = &sim->spacecraft[2];
CelestialBody* earth = &sim->bodies[0];
INFO("Testing retrograde orbit: i=" << retrograde->orbit.inclination << " rad (" << retrograde->orbit.inclination * 180.0 / M_PI << "°)");
Vec3 pos;
Vec3 vel;
orbital_elements_to_cartesian(retrograde->orbit, earth->mass, &pos, &vel);
double r = vec3_magnitude(pos);
double v = vec3_magnitude(vel);
INFO("Radius: " << r << " m");
INFO("Velocity: " << v << " m/s");
double expected_r = retrograde->orbit.semi_major_axis * (1.0 - retrograde->orbit.eccentricity * retrograde->orbit.eccentricity) / (1.0 + retrograde->orbit.eccentricity);
double mu = G * earth->mass;
double expected_v = sqrt(mu * (2.0 / r - 1.0 / retrograde->orbit.semi_major_axis));
double r_error = fabs(r - expected_r);
double v_error = fabs(v - expected_v);
INFO("Expected radius: " << expected_r << " m");
INFO("Expected velocity: " << expected_v << " m/s");
INFO("Radius error: " << r_error << " m");
INFO("Velocity error: " << v_error << " m/s");
REQUIRE(r_error < fabs(expected_r) * ELEMENT_TOLERANCE);
REQUIRE(v_error < VELOCITY_TOLERANCE);
destroy_simulation(sim);
}
TEST_CASE("Inclination at 0°, 90°, 180°", "[precision][boundary][inclination]") {
const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, 3, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/test_precision_boundaries.toml"));
double expected_inclinations[] = {0.0, M_PI / 2.0, M_PI};
for (int i = 0; i < 3; i++) {
Spacecraft* craft = &sim->spacecraft[i];
double expected_i = expected_inclinations[i];
INFO("Spacecraft " << i << ": i=" << craft->orbit.inclination << " rad (" << craft->orbit.inclination * 180.0 / M_PI << "°)");
double i_error = fabs(craft->orbit.inclination - expected_i);
INFO(" Expected inclination: " << expected_i << " rad");
INFO(" Inclination error: " << i_error << " rad");
REQUIRE(i_error < ELEMENT_TOLERANCE);
}
destroy_simulation(sim);
}
TEST_CASE("Semi-major axis sign change", "[precision][boundary][semi_major]") {
const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, 3, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/test_precision_boundaries.toml"));
for (int i = 0; i < 3; i++) {
Spacecraft* craft = &sim->spacecraft[i];
double e = craft->orbit.eccentricity;
double a = craft->orbit.semi_major_axis;
INFO("Spacecraft " << i << ": e=" << e << ", a=" << a << " m");
if (e < 1.0) {
INFO(" Elliptical orbit: a > 0");
REQUIRE(a > 0.0);
} else if (fabs(e - 1.0) < 1.0e-6) {
INFO(" Parabolic orbit: near-circular");
} else {
INFO(" Hyperbolic orbit: a < 0");
REQUIRE(a < 0.0);
}
}
destroy_simulation(sim);
}
TEST_CASE("Angular momentum conservation", "[precision][boundary][angular_momentum]") {
const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, 3, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/test_precision_boundaries.toml"));
Spacecraft* craft = &sim->spacecraft[0];
CelestialBody* earth = &sim->bodies[0];
double true_anomalies[] = {0.0, M_PI / 4.0, M_PI / 2.0, 3.0 * M_PI / 4.0, M_PI};
Vec3 initial_h = {0.0, 0.0, 0.0};
for (int i = 0; i < 5; i++) {
double nu = true_anomalies[i];
craft->orbit.true_anomaly = nu;
Vec3 pos;
Vec3 vel;
orbital_elements_to_cartesian(craft->orbit, earth->mass, &pos, &vel);
Vec3 h = vec3_cross(pos, vel);
double h_mag = vec3_magnitude(h);
INFO("ν=" << nu << " rad: |h|=" << h_mag << " m²/s");
if (i == 0) {
initial_h = h;
} else {
double h_error = vec3_distance(h, initial_h);
double relative_error = h_error / h_mag;
INFO(" Angular momentum error: " << h_error << " m²/s (" << relative_error * 100.0 << "%)");
REQUIRE(relative_error < ELEMENT_TOLERANCE);
}
}
destroy_simulation(sim);
}
TEST_CASE("Zero/very small radius or velocity", "[precision][boundary][zero]") {
const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, 3, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/test_precision_boundaries.toml"));
Spacecraft* craft = &sim->spacecraft[0];
CelestialBody* earth = &sim->bodies[0];
Vec3 pos;
Vec3 vel;
orbital_elements_to_cartesian(craft->orbit, earth->mass, &pos, &vel);
double r = vec3_magnitude(pos);
double v = vec3_magnitude(vel);
INFO("Radius: " << r << " m");
INFO("Velocity: " << v << " m/s");
REQUIRE(r > earth->radius);
REQUIRE(v > 0.0);
Vec3 r_vec = vec3_normalize(pos);
Vec3 v_vec = vec3_normalize(vel);
double r_dot_v = vec3_dot(r_vec, v_vec);
INFO("r̂ · v̂: " << r_dot_v);
REQUIRE(r_dot_v > -1.0);
REQUIRE(r_dot_v < 1.0);
destroy_simulation(sim);
}