Browse Source

Phase 3: Update config parser to use orbital elements

- Replace position/velocity with orbit table parsing
- Parse all orbital element fields with defaults
- Support altitude convenience field for semi_major_axis
- Add validation for orbital elements
- Remove manual position/velocity calculation from loader
- Build succeeds (expected unused function warning)
main
cinnaboot 6 months ago
parent
commit
ec1d115676
  1. 8
      docs/unified_orbital_elements_plan.md
  2. 200
      src/config_loader.cpp

8
docs/unified_orbital_elements_plan.md

@ -224,9 +224,9 @@ struct Spacecraft {
- `config_loader.cpp`: Update parsing logic
- `maneuver.cpp`: No changes needed
### Phase 3: Update Config Parser
### Phase 3: Update Config Parser ✅ COMPLETE
7. **Modify config_loader.cpp - body parsing**
7. **Modify config_loader.cpp - body parsing**
- Remove old `position` field requirement from `parse_toml_body()`
- Add `orbit` table parsing to `parse_toml_body()`
- Parse all orbital element fields with defaults
@ -234,12 +234,12 @@ struct Spacecraft {
- Parse into `body->orbit` struct
- Remove old `eccentricity` and `semi_major_axis` top-level fields
8. **Modify config_loader.cpp - spacecraft parsing**
8. **Modify config_loader.cpp - spacecraft parsing**
- Replace `position` + `velocity` fields with `orbit` table parsing
- Use same orbital element parsing logic as bodies
- Parse into `craft->orbit` struct
9. **Add validation for orbital elements**
9. **Add validation for orbital elements**
- `semi_major_axis` must not be zero
- `eccentricity` must be >= 0
- For elliptical orbits (e < 1): `semi_major_axis > 0`

200
src/config_loader.cpp

@ -59,36 +59,86 @@ static bool parse_toml_body(toml_datum_t body_table, CelestialBody* body) {
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");
toml_datum_t eccentricity = toml_get(body_table, "eccentricity");
toml_datum_t semi_major = toml_get(body_table, "semi_major_axis");
if (mass.type != TOML_FP64 || radius.type != TOML_FP64 ||
parent_idx.type != TOML_INT64 ||
eccentricity.type != TOML_FP64 ||
semi_major.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);
body->orbit.eccentricity = eccentricity.u.fp64;
body->orbit.semi_major_axis = semi_major.u.fp64;
// Extract position vector
toml_datum_t position = toml_get(body_table, "position");
if (position.type != TOML_TABLE || !extract_vec3_from_table(position, &body->global_position)) {
// 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;
}
// Initialize orbital elements with defaults
body->orbit.semi_major_axis = 0.0;
body->orbit.eccentricity = 0.0;
body->orbit.inclination = 0.0;
body->orbit.longitude_of_ascending_node = 0.0;
body->orbit.argument_of_periapsis = 0.0;
body->orbit.true_anomaly = 0.0;
// Parse semi_major_axis (required) or altitude (convenience)
toml_datum_t semi_major = toml_get(orbit_table, "semi_major_axis");
toml_datum_t altitude = toml_get(orbit_table, "altitude");
if (semi_major.type == TOML_FP64) {
body->orbit.semi_major_axis = semi_major.u.fp64;
} else if (altitude.type == TOML_FP64) {
// altitude will be added to parent radius in initialization
body->orbit.semi_major_axis = altitude.u.fp64;
} else {
printf("Error: Body '%s' must have either 'semi_major_axis' or 'altitude' in orbit table\n", body->name);
return false;
}
// Parse eccentricity (optional, default 0.0)
toml_datum_t eccentricity = toml_get(orbit_table, "eccentricity");
if (eccentricity.type == TOML_FP64) {
body->orbit.eccentricity = eccentricity.u.fp64;
}
// Parse true_anomaly (optional, default 0.0)
toml_datum_t true_anomaly = toml_get(orbit_table, "true_anomaly");
if (true_anomaly.type == TOML_FP64) {
body->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) {
body->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) {
body->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) {
body->orbit.argument_of_periapsis = aop.u.fp64;
}
// 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 (will be calculated later)
// 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;
@ -161,6 +211,35 @@ bool load_system_config(SimulationState* sim, const char* filepath) {
}
}
// Validate orbital elements
for (int i = 0; i < body_count; i++) {
CelestialBody* body = &sim->bodies[i];
// Validate semi_major_axis
if (fabs(body->orbit.semi_major_axis) < 1e-10) {
printf("Error: Body '%s' has invalid semi_major_axis: %.2e (must not be zero)\n",
body->name, body->orbit.semi_major_axis);
toml_free(result);
return false;
}
// Validate eccentricity
if (body->orbit.eccentricity < 0.0) {
printf("Error: Body '%s' has invalid eccentricity: %.3f (must be >= 0)\n",
body->name, body->orbit.eccentricity);
toml_free(result);
return false;
}
// For elliptical orbits (e < 1), semi_major_axis must be positive
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);
toml_free(result);
return false;
}
}
sim->body_count = body_count;
strncpy(sim->config_name, filepath, sizeof(sim->config_name) - 1);
@ -217,10 +296,6 @@ static bool load_spacecraft_from_toml(SimulationState* sim, toml_result_t result
return false;
}
CelestialBody* parent = &sim->bodies[craft.parent_index];
craft.global_position = vec3_add(parent->global_position, craft.local_position);
craft.global_velocity = vec3_add(parent->global_velocity, craft.local_velocity);
int idx = add_spacecraft(sim, &craft);
if (idx < 0) {
printf("Error: Failed to add spacecraft to simulation\n");
@ -228,6 +303,32 @@ static bool load_spacecraft_from_toml(SimulationState* sim, toml_result_t result
}
}
// Validate spacecraft orbital elements
for (int i = 0; i < sim->craft_count; i++) {
Spacecraft* craft = &sim->spacecraft[i];
// Validate semi_major_axis
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;
}
// Validate eccentricity
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;
}
// For elliptical orbits (e < 1), semi_major_axis must be positive
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;
}
}
printf("Loaded %d spacecraft from %s\n", craft_count, sim->config_name);
return true;
}
@ -250,21 +351,70 @@ static bool parse_toml_spacecraft(toml_datum_t craft_table, Spacecraft* craft) {
craft->mass = mass.u.fp64;
craft->parent_index = (int)(parent_idx.type == TOML_INT64 ? parent_idx.u.int64 : (int)parent_idx.u.fp64);
toml_datum_t position = toml_get(craft_table, "position");
if (position.type != TOML_TABLE || !extract_vec3_from_table(position, &craft->local_position)) {
// 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;
}
craft->global_position = craft->local_position;
toml_datum_t velocity = toml_get(craft_table, "velocity");
if (velocity.type == TOML_TABLE) {
if (!extract_vec3_from_table(velocity, &craft->local_velocity)) {
return false;
}
// Initialize orbital elements with defaults
craft->orbit.semi_major_axis = 0.0;
craft->orbit.eccentricity = 0.0;
craft->orbit.inclination = 0.0;
craft->orbit.longitude_of_ascending_node = 0.0;
craft->orbit.argument_of_periapsis = 0.0;
craft->orbit.true_anomaly = 0.0;
// Parse semi_major_axis (required) or altitude (convenience)
toml_datum_t semi_major = toml_get(orbit_table, "semi_major_axis");
toml_datum_t altitude = toml_get(orbit_table, "altitude");
if (semi_major.type == TOML_FP64) {
craft->orbit.semi_major_axis = semi_major.u.fp64;
} else if (altitude.type == TOML_FP64) {
// altitude will be added to parent radius in initialization
craft->orbit.semi_major_axis = altitude.u.fp64;
} else {
craft->local_velocity = {0.0, 0.0, 0.0};
printf("Error: Spacecraft '%s' must have either 'semi_major_axis' or 'altitude' in orbit table\n", craft->name);
return false;
}
// Parse eccentricity (optional, default 0.0)
toml_datum_t eccentricity = toml_get(orbit_table, "eccentricity");
if (eccentricity.type == TOML_FP64) {
craft->orbit.eccentricity = eccentricity.u.fp64;
}
// Parse true_anomaly (optional, default 0.0)
toml_datum_t true_anomaly = toml_get(orbit_table, "true_anomaly");
if (true_anomaly.type == TOML_FP64) {
craft->orbit.true_anomaly = true_anomaly.u.fp64;
}
craft->global_velocity = craft->local_velocity;
// Parse inclination (optional, default 0.0)
toml_datum_t inclination = toml_get(orbit_table, "inclination");
if (inclination.type == TOML_FP64) {
craft->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) {
craft->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) {
craft->orbit.argument_of_periapsis = aop.u.fp64;
}
// 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;
}

Loading…
Cancel
Save