Browse Source
- Add config_validator module with validation functions - Move validate_initial_positions from simulation to config_validator - Add validate_mass_ratios (checks parent/child mass ratio) - Add validate_soi_overlap (detects bodies with overlapping SOIs) - Add validate_nested_orbits (checks moon orbit boundaries) - Update config_loader to use run_all_config_validations - Update tests to expect mutual SOI config to fail validationmain
8 changed files with 293 additions and 201 deletions
@ -0,0 +1,268 @@ |
|||||||
|
#include "config_validator.h" |
||||||
|
#include <cstdio> |
||||||
|
#include <cmath> |
||||||
|
#include <cstring> |
||||||
|
|
||||||
|
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) < 0.005); |
||||||
|
|
||||||
|
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) < 0.005); |
||||||
|
|
||||||
|
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_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_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; |
||||||
|
} |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
#ifndef CONFIG_VALIDATOR_H |
||||||
|
#define CONFIG_VALIDATOR_H |
||||||
|
|
||||||
|
#include "simulation.h" |
||||||
|
#include "spacecraft.h" |
||||||
|
|
||||||
|
#define MIN_MASS_RATIO 1000.0 |
||||||
|
#define NESTED_ORBIT_FRACTION 5.0 |
||||||
|
|
||||||
|
bool validate_parent_index_ordering(SimulationState* sim); |
||||||
|
bool validate_orbital_elements(SimulationState* sim); |
||||||
|
bool validate_initial_positions(SimulationState* sim); |
||||||
|
bool validate_mass_ratios(SimulationState* sim); |
||||||
|
bool validate_soi_overlap(SimulationState* sim); |
||||||
|
bool validate_nested_orbits(SimulationState* sim); |
||||||
|
bool run_all_config_validations(SimulationState* sim); |
||||||
|
|
||||||
|
#endif |
||||||
Loading…
Reference in new issue