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.
 
 
 
 
 

425 lines
14 KiB

#include "config_loader.h"
#include <cstdio>
#include <cmath>
#include <cstring>
#include "simulation.h"
#include "maneuver.h"
#include "config_validator.h"
#include "orbital_mechanics.h"
static bool parse_toml_spacecraft(toml_datum_t craft_table, Spacecraft* craft);
static bool load_spacecraft_from_toml(SimulationState* sim, toml_result_t result);
static bool parse_toml_maneuver(toml_datum_t maneuver_table, Maneuver* maneuver, SimulationState* sim);
static bool load_maneuvers_from_toml(SimulationState* sim, toml_result_t result);
static bool extract_color_from_table(toml_datum_t table, float* color) {
toml_datum_t r = toml_get(table, "r");
toml_datum_t g = toml_get(table, "g");
toml_datum_t b = toml_get(table, "b");
// Accept both INT64 and FP64 for color components
if ((r.type != TOML_FP64 && r.type != TOML_INT64) ||
(g.type != TOML_FP64 && g.type != TOML_INT64) ||
(b.type != TOML_FP64 && b.type != TOML_INT64)) {
return false;
}
color[0] = (float)(r.type == TOML_FP64 ? r.u.fp64 : (double)r.u.int64);
color[1] = (float)(g.type == TOML_FP64 ? g.u.fp64 : (double)g.u.int64);
color[2] = (float)(b.type == TOML_FP64 ? b.u.fp64 : (double)b.u.int64);
return true;
}
static bool parse_toml_orbit(toml_datum_t orbit_table, OrbitalElements* orbit, const char* object_name) {
// Initialize orbital elements with defaults
orbit->semi_major_axis = 0.0;
orbit->eccentricity = 0.0;
orbit->inclination = 0.0;
orbit->longitude_of_ascending_node = 0.0;
orbit->argument_of_periapsis = 0.0;
orbit->true_anomaly = 0.0;
// Parse semi_major_axis (for elliptical/hyperbolic) or semi_latus_rectum (for parabolic)
toml_datum_t semi_major = toml_get(orbit_table, "semi_major_axis");
toml_datum_t semi_latus = toml_get(orbit_table, "semi_latus_rectum");
// Parse eccentricity first to determine which parameter is required
toml_datum_t eccentricity = toml_get(orbit_table, "eccentricity");
if (eccentricity.type != TOML_FP64) {
printf("Error: Object '%s' missing required 'eccentricity' in orbit table\n", object_name);
return false;
}
orbit->eccentricity = eccentricity.u.fp64;
bool is_parabolic = (fabs(orbit->eccentricity - 1.0) < PARABOLIC_TOLERANCE);
if (is_parabolic) {
// Parabolic orbit - requires semi_latus_rectum
if (semi_latus.type != TOML_FP64) {
printf("Error: Parabolic orbit for object '%s' requires 'semi_latus_rectum' (not 'semi_major_axis')\n", object_name);
return false;
}
orbit->semi_latus_rectum = semi_latus.u.fp64;
if (semi_major.type == TOML_FP64) {
printf("Warning: Object '%s' has parabolic eccentricity, 'semi_latus_rectum' used instead of 'semi_major_axis'\n", object_name);
}
} else {
// Elliptical or hyperbolic - requires semi_major_axis
if (semi_major.type == TOML_FP64) {
orbit->semi_major_axis = semi_major.u.fp64;
} else {
printf("Error: Object '%s' must have 'semi_major_axis' in orbit table (non-parabolic orbits)\n", object_name);
return false;
}
if (semi_latus.type == TOML_FP64) {
printf("Warning: Object '%s' has non-parabolic eccentricity, 'semi_latus_rectum' ignored\n", object_name);
}
}
// Parse true_anomaly (optional, default 0.0)
toml_datum_t true_anomaly = toml_get(orbit_table, "true_anomaly");
if (true_anomaly.type == TOML_FP64) {
orbit->true_anomaly = true_anomaly.u.fp64;
}
// Parse inclination (optional, default 0.0)
toml_datum_t inclination = toml_get(orbit_table, "inclination");
if (inclination.type == TOML_FP64) {
orbit->inclination = inclination.u.fp64;
}
// Parse longitude_of_ascending_node (optional, default 0.0)
toml_datum_t raan = toml_get(orbit_table, "longitude_of_ascending_node");
if (raan.type == TOML_FP64) {
orbit->longitude_of_ascending_node = raan.u.fp64;
}
// Parse argument_of_periapsis (optional, default 0.0)
toml_datum_t aop = toml_get(orbit_table, "argument_of_periapsis");
if (aop.type == TOML_FP64) {
orbit->argument_of_periapsis = aop.u.fp64;
}
return true;
}
static bool parse_toml_body(toml_datum_t body_table, CelestialBody* body) {
// Extract string fields
toml_datum_t name = toml_get(body_table, "name");
if (name.type != TOML_STRING) {
return false;
}
strncpy(body->name, name.u.s, 63);
body->name[63] = '\0';
// Extract numeric fields
toml_datum_t mass = toml_get(body_table, "mass");
toml_datum_t radius = toml_get(body_table, "radius");
toml_datum_t parent_idx = toml_get(body_table, "parent_index");
if (mass.type != TOML_FP64 || radius.type != TOML_FP64 ||
parent_idx.type != TOML_INT64) {
return false;
}
body->mass = mass.u.fp64;
body->radius = radius.u.fp64;
body->parent_index = (int)(parent_idx.type == TOML_INT64 ? parent_idx.u.int64 : (int)parent_idx.u.fp64);
// Parse orbit table
toml_datum_t orbit_table = toml_get(body_table, "orbit");
if (orbit_table.type != TOML_TABLE) {
printf("Error: Body '%s' missing required 'orbit' table\n", body->name);
return false;
}
if (!parse_toml_orbit(orbit_table, &body->orbit, body->name)) {
return false;
}
// Extract color
toml_datum_t color = toml_get(body_table, "color");
if (color.type != TOML_TABLE || !extract_color_from_table(color, body->color)) {
return false;
}
// Initialize velocity and position (will be calculated later from orbital elements)
body->global_position = {0.0, 0.0, 0.0};
body->global_velocity = {0.0, 0.0, 0.0};
body->local_position = {0.0, 0.0, 0.0};
body->local_velocity = {0.0, 0.0, 0.0};
body->soi_radius = 0.0;
return true;
}
bool load_system_config(SimulationState* sim, const char* filepath) {
toml_result_t result = toml_parse_file_ex(filepath);
if (!result.ok) {
printf("Error: Could not parse TOML config file: %s\n", filepath);
printf("TOML Error: %s\n", result.errmsg);
return false;
}
// Get bodies array
toml_datum_t bodies = toml_get(result.toptab, "bodies");
if (bodies.type != TOML_ARRAY) {
printf("Error: Expected 'bodies' array in config file: %s\n", filepath);
toml_free(result);
return false;
}
// Count bodies first
int body_count = bodies.u.arr.size;
if (body_count == 0) {
printf("Error: No bodies found in config file: %s\n", filepath);
toml_free(result);
return false;
}
if (body_count > sim->max_bodies) {
printf("Error: Too many bodies (%d) for simulation (max: %d)\n",
body_count, sim->max_bodies);
toml_free(result);
return false;
}
for (int i = 0; i < body_count; i++) {
toml_datum_t body_table = bodies.u.arr.elem[i];
if (!parse_toml_body(body_table, &sim->bodies[i])) {
printf("Error: Failed to parse body at index %d\n", i);
toml_free(result);
return false;
}
}
sim->body_count = body_count;
strncpy(sim->config_name, filepath, sizeof(sim->config_name) - 1);
sim->config_name[sizeof(sim->config_name) - 1] = '\0';
// Load spacecraft from the same config file (before freeing TOML data)
if (!load_spacecraft_from_toml(sim, result)) {
toml_free(result);
return false;
}
// Load maneuvers from the same config file (before freeing TOML data)
if (!load_maneuvers_from_toml(sim, result)) {
toml_free(result);
return false;
}
toml_free(result);
initialize_orbital_objects(sim);
if (!run_all_config_validations(sim)) {
printf("Error: Config validation failed\n");
return false;
}
printf("Loaded %d bodies from %s\n", body_count, filepath);
return true;
}
static bool load_spacecraft_from_toml(SimulationState* sim, toml_result_t result) {
toml_datum_t spacecraft = toml_get(result.toptab, "spacecraft");
if (spacecraft.type != TOML_ARRAY) {
return true;
}
int craft_count = spacecraft.u.arr.size;
if (craft_count == 0) {
return true;
}
if (craft_count > sim->max_craft) {
printf("Error: Too many spacecraft (%d) for simulation (max: %d)\n",
craft_count, sim->max_craft);
return false;
}
for (int i = 0; i < craft_count; i++) {
Spacecraft craft;
toml_datum_t craft_table = spacecraft.u.arr.elem[i];
if (!parse_toml_spacecraft(craft_table, &craft)) {
printf("Error: Failed to parse spacecraft at index %d\n", i);
return false;
}
int idx = add_spacecraft(sim, &craft);
if (idx < 0) {
printf("Error: Failed to add spacecraft to simulation\n");
return false;
}
}
printf("Loaded %d spacecraft from %s\n", craft_count, sim->config_name);
return true;
}
static bool parse_toml_spacecraft(toml_datum_t craft_table, Spacecraft* craft) {
toml_datum_t name = toml_get(craft_table, "name");
if (name.type != TOML_STRING) {
return false;
}
strncpy(craft->name, name.u.s, 63);
craft->name[63] = '\0';
toml_datum_t mass = toml_get(craft_table, "mass");
toml_datum_t parent_idx = toml_get(craft_table, "parent_index");
if (mass.type != TOML_FP64 || parent_idx.type != TOML_INT64) {
return false;
}
craft->mass = mass.u.fp64;
craft->parent_index = (int)(parent_idx.type == TOML_INT64 ? parent_idx.u.int64 : (int)parent_idx.u.fp64);
// Parse orbit table
toml_datum_t orbit_table = toml_get(craft_table, "orbit");
if (orbit_table.type != TOML_TABLE) {
printf("Error: Spacecraft '%s' missing required 'orbit' table\n", craft->name);
return false;
}
if (!parse_toml_orbit(orbit_table, &craft->orbit, craft->name)) {
return false;
}
// Initialize position and velocity (will be calculated later from orbital elements)
craft->global_position = {0.0, 0.0, 0.0};
craft->global_velocity = {0.0, 0.0, 0.0};
craft->local_position = {0.0, 0.0, 0.0};
craft->local_velocity = {0.0, 0.0, 0.0};
return true;
}
static bool parse_toml_maneuver(toml_datum_t maneuver_table, Maneuver* maneuver, SimulationState* sim) {
toml_datum_t name = toml_get(maneuver_table, "name");
if (name.type != TOML_STRING) {
return false;
}
strncpy(maneuver->name, name.u.s, 63);
maneuver->name[63] = '\0';
toml_datum_t spacecraft_name = toml_get(maneuver_table, "spacecraft_name");
if (spacecraft_name.type != TOML_STRING) {
return false;
}
int craft_idx = -1;
for (int i = 0; i < sim->craft_count; i++) {
if (strcmp(sim->spacecraft[i].name, spacecraft_name.u.s) == 0) {
craft_idx = i;
break;
}
}
if (craft_idx < 0) {
printf("Error: Maneuver '%s' references non-existent spacecraft '%s'\n",
maneuver->name, spacecraft_name.u.s);
return false;
}
maneuver->craft_index = craft_idx;
toml_datum_t trigger_type_str = toml_get(maneuver_table, "trigger_type");
if (trigger_type_str.type != TOML_STRING) {
return false;
}
if (strcmp(trigger_type_str.u.s, "time") == 0) {
maneuver->trigger_type = TRIGGER_TIME;
} else if (strcmp(trigger_type_str.u.s, "true_anomaly") == 0) {
maneuver->trigger_type = TRIGGER_TRUE_ANOMALY;
} else {
printf("Error: Unknown trigger_type '%s' for maneuver '%s'\n",
trigger_type_str.u.s, maneuver->name);
return false;
}
toml_datum_t trigger_value = toml_get(maneuver_table, "trigger_value");
if (trigger_value.type != TOML_FP64 && trigger_value.type != TOML_INT64) {
return false;
}
maneuver->trigger_value = trigger_value.type == TOML_FP64 ? trigger_value.u.fp64 : (double)trigger_value.u.int64;
toml_datum_t direction_str = toml_get(maneuver_table, "direction");
if (direction_str.type != TOML_STRING) {
return false;
}
if (strcmp(direction_str.u.s, "prograde") == 0) {
maneuver->direction = BURN_PROGRADE;
} else if (strcmp(direction_str.u.s, "retrograde") == 0) {
maneuver->direction = BURN_RETROGRADE;
} else if (strcmp(direction_str.u.s, "normal") == 0) {
maneuver->direction = BURN_NORMAL;
} else if (strcmp(direction_str.u.s, "antinormal") == 0) {
maneuver->direction = BURN_ANTINORMAL;
} else if (strcmp(direction_str.u.s, "radial_in") == 0) {
maneuver->direction = BURN_RADIAL_IN;
} else if (strcmp(direction_str.u.s, "radial_out") == 0) {
maneuver->direction = BURN_RADIAL_OUT;
} else {
printf("Error: Unknown direction '%s' for maneuver '%s'\n",
direction_str.u.s, maneuver->name);
return false;
}
toml_datum_t delta_v = toml_get(maneuver_table, "delta_v");
if (delta_v.type != TOML_FP64 && delta_v.type != TOML_INT64) {
return false;
}
maneuver->delta_v = delta_v.type == TOML_FP64 ? delta_v.u.fp64 : (double)delta_v.u.int64;
maneuver->executed = false;
maneuver->executed_time = 0.0;
maneuver->scheduled_dt = 0.0;
return true;
}
static bool load_maneuvers_from_toml(SimulationState* sim, toml_result_t result) {
toml_datum_t maneuvers = toml_get(result.toptab, "maneuvers");
if (maneuvers.type != TOML_ARRAY) {
return true;
}
int maneuver_count = maneuvers.u.arr.size;
if (maneuver_count == 0) {
return true;
}
if (maneuver_count > sim->max_maneuvers) {
printf("Error: Too many maneuvers (%d) for simulation (max: %d)\n",
maneuver_count, sim->max_maneuvers);
return false;
}
for (int i = 0; i < maneuver_count; i++) {
Maneuver maneuver;
toml_datum_t maneuver_table = maneuvers.u.arr.elem[i];
if (!parse_toml_maneuver(maneuver_table, &maneuver, sim)) {
printf("Error: Failed to parse maneuver at index %d\n", i);
return false;
}
for (int j = 0; j < sim->maneuver_count; j++) {
if (strcmp(sim->maneuvers[j].name, maneuver.name) == 0) {
printf("Error: Duplicate maneuver name '%s' found at index %d\n",
maneuver.name, i);
return false;
}
}
sim->maneuvers[sim->maneuver_count] = maneuver;
sim->maneuver_count++;
}
printf("Loaded %d maneuvers from %s\n", maneuver_count, sim->config_name);
return true;
}