#include "config_validator.h" #include #include #include 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; }