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.
329 lines
11 KiB
329 lines
11 KiB
#include "config_validator.h" |
|
#include <cstdio> |
|
#include <cmath> |
|
#include <cstring> |
|
#include "orbital_mechanics.h" |
|
|
|
bool validate_parent_index_ordering(SimulationState* sim) { |
|
for (int i = 0; i < sim->body_count; i++) { |
|
if (sim->bodies[i].parent_index != -1 && sim->bodies[i].parent_index >= i) { |
|
printf("Error: Body '%s' (index %d) has invalid parent_index %d - must be < %d or -1\n", |
|
sim->bodies[i].name, i, sim->bodies[i].parent_index, i); |
|
return false; |
|
} |
|
} |
|
|
|
for (int i = 0; i < sim->craft_count; i++) { |
|
Spacecraft* craft = &sim->spacecraft[i]; |
|
if (craft->parent_index < 0 || craft->parent_index >= sim->body_count) { |
|
printf("Error: Spacecraft '%s' has invalid parent_index %d (valid: 0-%d)\n", |
|
craft->name, craft->parent_index, sim->body_count - 1); |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool validate_orbital_elements(SimulationState* sim) { |
|
for (int i = 0; i < sim->body_count; i++) { |
|
CelestialBody* body = &sim->bodies[i]; |
|
|
|
if (body->parent_index < 0) { |
|
continue; |
|
} |
|
|
|
bool is_parabolic = (fabs(body->orbit.eccentricity - 1.0) < PARABOLIC_TOLERANCE); |
|
|
|
if (body->orbit.eccentricity < 0.0) { |
|
printf("Error: Body '%s' has invalid eccentricity: %.2e (must be >= 0)\n", |
|
body->name, body->orbit.eccentricity); |
|
return false; |
|
} |
|
|
|
if (is_parabolic) { |
|
if (body->orbit.semi_latus_rectum <= 0.0) { |
|
printf("Error: Body '%s' has parabolic orbit but non-positive semi_latus_rectum: %.2e\n", |
|
body->name, body->orbit.semi_latus_rectum); |
|
return false; |
|
} |
|
} else { |
|
if (body->orbit.semi_major_axis == 0.0) { |
|
printf("Error: Body '%s' has invalid semi_major_axis: %.2e (must not be zero)\n", |
|
body->name, body->orbit.semi_major_axis); |
|
return false; |
|
} |
|
|
|
if (body->orbit.eccentricity < 1.0 && body->orbit.semi_major_axis <= 0.0) { |
|
printf("Error: Body '%s' has elliptical orbit but non-positive semi_major_axis: %.2e\n", |
|
body->name, body->orbit.semi_major_axis); |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
for (int i = 0; i < sim->craft_count; i++) { |
|
Spacecraft* craft = &sim->spacecraft[i]; |
|
|
|
bool is_parabolic = (fabs(craft->orbit.eccentricity - 1.0) < PARABOLIC_TOLERANCE); |
|
|
|
if (is_parabolic) { |
|
if (craft->orbit.semi_latus_rectum <= 0.0) { |
|
printf("Error: Spacecraft '%s' has parabolic orbit but non-positive semi_latus_rectum: %.2e\n", |
|
craft->name, craft->orbit.semi_latus_rectum); |
|
return false; |
|
} |
|
} else { |
|
if (fabs(craft->orbit.semi_major_axis) < 1e-10) { |
|
printf("Error: Spacecraft '%s' has invalid semi_major_axis: %.2e (must not be zero)\n", |
|
craft->name, craft->orbit.semi_major_axis); |
|
return false; |
|
} |
|
|
|
if (craft->orbit.eccentricity < 1.0 && craft->orbit.semi_major_axis <= 0.0) { |
|
printf("Error: Spacecraft '%s' has elliptical orbit but non-positive semi_major_axis: %.2e\n", |
|
craft->name, craft->orbit.semi_major_axis); |
|
return false; |
|
} |
|
} |
|
|
|
if (craft->orbit.eccentricity < 0.0) { |
|
printf("Error: Spacecraft '%s' has invalid eccentricity: %.3f (must be >= 0)\n", |
|
craft->name, craft->orbit.eccentricity); |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool validate_mass_ratios(SimulationState* sim) { |
|
for (int i = 0; i < sim->body_count; i++) { |
|
CelestialBody* body = &sim->bodies[i]; |
|
|
|
if (body->parent_index < 0 || body->parent_index >= sim->body_count) { |
|
continue; |
|
} |
|
|
|
CelestialBody* parent = &sim->bodies[body->parent_index]; |
|
double mass_ratio = parent->mass / body->mass; |
|
double radius_ratio = body->radius / parent->radius; |
|
|
|
if (parent->parent_index < 0 && radius_ratio > 0.5 && mass_ratio < MIN_MASS_RATIO) { |
|
printf("Error: Body '%s' (mass=%.2e kg, radius=%.2e m) has insufficient mass ratio with root parent '%s' (mass=%.2e kg, radius=%.2e m)\n", |
|
body->name, body->mass, body->radius, parent->name, parent->mass, parent->radius); |
|
printf(" Mass ratio: %.2f (minimum required: %.2f)\n", mass_ratio, MIN_MASS_RATIO); |
|
printf(" Radius ratio: %.2f (triggers validation for radius > 50%% of parent)\n", radius_ratio); |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool validate_soi_overlap(SimulationState* sim) { |
|
for (int i = 0; i < sim->body_count; i++) { |
|
CelestialBody* body_i = &sim->bodies[i]; |
|
|
|
if (body_i->parent_index < 0) { |
|
continue; |
|
} |
|
|
|
for (int j = i + 1; j < sim->body_count; j++) { |
|
CelestialBody* body_j = &sim->bodies[j]; |
|
|
|
if (body_j->parent_index != body_i->parent_index) { |
|
continue; |
|
} |
|
|
|
double distance = vec3_distance(body_i->global_position, body_j->global_position); |
|
double combined_soi = body_i->soi_radius + body_j->soi_radius; |
|
|
|
if (distance < combined_soi) { |
|
printf("Error: Bodies '%s' and '%s' have overlapping SOIs while sharing same parent '%s'\n", |
|
body_i->name, body_j->name, sim->bodies[body_i->parent_index].name); |
|
printf(" Separation: %.2e m\n", distance); |
|
printf(" Combined SOI: %.2e m (%.2e + %.2e)\n", |
|
combined_soi, body_i->soi_radius, body_j->soi_radius); |
|
printf(" SOI overlap: %.2e m\n", combined_soi - distance); |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool validate_nested_orbits(SimulationState* sim) { |
|
for (int i = 0; i < sim->body_count; i++) { |
|
CelestialBody* body = &sim->bodies[i]; |
|
|
|
if (body->parent_index < 0 || body->parent_index >= sim->body_count) { |
|
continue; |
|
} |
|
|
|
CelestialBody* parent = &sim->bodies[body->parent_index]; |
|
|
|
if (parent->parent_index < 0) { |
|
continue; |
|
} |
|
|
|
double radius_ratio = body->radius / parent->radius; |
|
|
|
if (radius_ratio > 0.3) { |
|
continue; |
|
} |
|
|
|
if (body->orbit.eccentricity > 0.5) { |
|
continue; |
|
} |
|
|
|
if (body->mass < 1e20) { |
|
continue; |
|
} |
|
|
|
double child_orbit_radius = body->orbit.semi_major_axis; |
|
double parent_soi = parent->soi_radius; |
|
double max_allowed = NESTED_ORBIT_FRACTION * parent_soi; |
|
|
|
if (child_orbit_radius > max_allowed) { |
|
printf("Error: Body '%s' orbit extends too far from parent '%s'\n", |
|
body->name, parent->name); |
|
printf(" Child orbit radius: %.2e m\n", child_orbit_radius); |
|
printf(" Parent SOI radius: %.2e m\n", parent_soi); |
|
printf(" Maximum allowed: %.2e m (%.2f%% of parent SOI)\n", |
|
max_allowed, NESTED_ORBIT_FRACTION * 100.0); |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool validate_true_anomaly_ranges(SimulationState* sim) { |
|
for (int i = 0; i < sim->body_count; i++) { |
|
CelestialBody* body = &sim->bodies[i]; |
|
|
|
if (body->parent_index < 0) { |
|
continue; |
|
} |
|
|
|
double e = body->orbit.eccentricity; |
|
double nu = body->orbit.true_anomaly; |
|
|
|
// Normalize nu to [0, 2π] |
|
while (nu < 0.0) nu += 2.0 * M_PI; |
|
while (nu >= 2.0 * M_PI) nu -= 2.0 * M_PI; |
|
|
|
// Check hyperbolic orbit anomaly limits |
|
if (e > 1.0) { |
|
double max_nu = acos(-1.0 / e); |
|
if (nu > max_nu && nu < (2.0 * M_PI - max_nu)) { |
|
printf("Error: Body '%s' has invalid true_anomaly for hyperbolic orbit\n", |
|
body->name); |
|
printf(" Eccentricity: %.4f\n", e); |
|
printf(" True anomaly: %.4f rad\n", nu); |
|
printf(" Valid range: [0, %.4f] ∪ [%.4f, 2π]\n", max_nu, 2.0 * M_PI - max_nu); |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
for (int i = 0; i < sim->craft_count; i++) { |
|
Spacecraft* craft = &sim->spacecraft[i]; |
|
|
|
double e = craft->orbit.eccentricity; |
|
double nu = craft->orbit.true_anomaly; |
|
|
|
// Normalize nu to [0, 2π] |
|
while (nu < 0.0) nu += 2.0 * M_PI; |
|
while (nu >= 2.0 * M_PI) nu -= 2.0 * M_PI; |
|
|
|
// Check hyperbolic orbit anomaly limits |
|
if (e > 1.0) { |
|
double max_nu = acos(-1.0 / e); |
|
if (nu > max_nu && nu < (2.0 * M_PI - max_nu)) { |
|
printf("Error: Spacecraft '%s' has invalid true_anomaly for hyperbolic orbit\n", |
|
craft->name); |
|
printf(" Eccentricity: %.4f\n", e); |
|
printf(" True anomaly: %.4f rad\n", nu); |
|
printf(" Valid range: [0, %.4f] ∪ [%.4f, 2π]\n", max_nu, 2.0 * M_PI - max_nu); |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool validate_initial_positions(SimulationState* sim) { |
|
for (int i = 0; i < sim->body_count; i++) { |
|
CelestialBody* body = &sim->bodies[i]; |
|
|
|
if (body->parent_index >= 0 && body->parent_index < sim->body_count) { |
|
CelestialBody* parent = &sim->bodies[body->parent_index]; |
|
double distance = vec3_magnitude(vec3_sub(body->global_position, parent->global_position)); |
|
double min_distance = parent->radius + body->radius; |
|
|
|
if (distance < min_distance) { |
|
printf("Error: Body '%s' (index %d) too close to parent '%s' (index %d)\n", |
|
body->name, i, parent->name, body->parent_index); |
|
printf(" Distance: %.2e m\n", distance); |
|
printf(" Minimum required: %.2e m (parent radius + body radius)\n", min_distance); |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
for (int i = 0; i < sim->craft_count; i++) { |
|
Spacecraft* craft = &sim->spacecraft[i]; |
|
|
|
if (craft->parent_index >= 0 && craft->parent_index < sim->body_count) { |
|
CelestialBody* parent = &sim->bodies[craft->parent_index]; |
|
double distance = vec3_magnitude(vec3_sub(craft->global_position, parent->global_position)); |
|
double min_distance = parent->radius; |
|
|
|
if (distance < min_distance) { |
|
printf("Error: Spacecraft '%s' too close to parent '%s'\n", |
|
craft->name, parent->name); |
|
printf(" Distance: %.2e m\n", distance); |
|
printf(" Minimum required: %.2e m (parent radius)\n", min_distance); |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool run_all_config_validations(SimulationState* sim) { |
|
if (!validate_parent_index_ordering(sim)) { |
|
return false; |
|
} |
|
|
|
if (!validate_orbital_elements(sim)) { |
|
return false; |
|
} |
|
|
|
if (!validate_true_anomaly_ranges(sim)) { |
|
return false; |
|
} |
|
|
|
if (!validate_mass_ratios(sim)) { |
|
return false; |
|
} |
|
|
|
if (!validate_soi_overlap(sim)) { |
|
return false; |
|
} |
|
|
|
if (!validate_nested_orbits(sim)) { |
|
return false; |
|
} |
|
|
|
if (!validate_initial_positions(sim)) { |
|
return false; |
|
} |
|
|
|
return true; |
|
}
|
|
|