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 - `config_loader.cpp`: Update parsing logic
- `maneuver.cpp`: No changes needed - `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()` - Remove old `position` field requirement from `parse_toml_body()`
- Add `orbit` table parsing to `parse_toml_body()` - Add `orbit` table parsing to `parse_toml_body()`
- Parse all orbital element fields with defaults - Parse all orbital element fields with defaults
@ -234,12 +234,12 @@ struct Spacecraft {
- Parse into `body->orbit` struct - Parse into `body->orbit` struct
- Remove old `eccentricity` and `semi_major_axis` top-level fields - 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 - Replace `position` + `velocity` fields with `orbit` table parsing
- Use same orbital element parsing logic as bodies - Use same orbital element parsing logic as bodies
- Parse into `craft->orbit` struct - Parse into `craft->orbit` struct
9. **Add validation for orbital elements** 9. **Add validation for orbital elements**
- `semi_major_axis` must not be zero - `semi_major_axis` must not be zero
- `eccentricity` must be >= 0 - `eccentricity` must be >= 0
- For elliptical orbits (e < 1): `semi_major_axis > 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 mass = toml_get(body_table, "mass");
toml_datum_t radius = toml_get(body_table, "radius"); toml_datum_t radius = toml_get(body_table, "radius");
toml_datum_t parent_idx = toml_get(body_table, "parent_index"); 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 || if (mass.type != TOML_FP64 || radius.type != TOML_FP64 ||
parent_idx.type != TOML_INT64 || parent_idx.type != TOML_INT64) {
eccentricity.type != TOML_FP64 ||
semi_major.type != TOML_FP64) {
return false; return false;
} }
body->mass = mass.u.fp64; body->mass = mass.u.fp64;
body->radius = radius.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->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 // Parse orbit table
toml_datum_t position = toml_get(body_table, "position"); toml_datum_t orbit_table = toml_get(body_table, "orbit");
if (position.type != TOML_TABLE || !extract_vec3_from_table(position, &body->global_position)) { if (orbit_table.type != TOML_TABLE) {
printf("Error: Body '%s' missing required 'orbit' table\n", body->name);
return false; 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 // Extract color
toml_datum_t color = toml_get(body_table, "color"); toml_datum_t color = toml_get(body_table, "color");
if (color.type != TOML_TABLE || !extract_color_from_table(color, body->color)) { if (color.type != TOML_TABLE || !extract_color_from_table(color, body->color)) {
return false; 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->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; body->soi_radius = 0.0;
return true; 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; sim->body_count = body_count;
strncpy(sim->config_name, filepath, sizeof(sim->config_name) - 1); 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; 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); int idx = add_spacecraft(sim, &craft);
if (idx < 0) { if (idx < 0) {
printf("Error: Failed to add spacecraft to simulation\n"); 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); printf("Loaded %d spacecraft from %s\n", craft_count, sim->config_name);
return true; return true;
} }
@ -250,21 +351,70 @@ static bool parse_toml_spacecraft(toml_datum_t craft_table, Spacecraft* craft) {
craft->mass = mass.u.fp64; craft->mass = mass.u.fp64;
craft->parent_index = (int)(parent_idx.type == TOML_INT64 ? parent_idx.u.int64 : (int)parent_idx.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"); // Parse orbit table
if (position.type != TOML_TABLE || !extract_vec3_from_table(position, &craft->local_position)) { 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; return false;
} }
craft->global_position = craft->local_position;
toml_datum_t velocity = toml_get(craft_table, "velocity"); // Initialize orbital elements with defaults
if (velocity.type == TOML_TABLE) { craft->orbit.semi_major_axis = 0.0;
if (!extract_vec3_from_table(velocity, &craft->local_velocity)) { craft->orbit.eccentricity = 0.0;
return false; 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 { } 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; return true;
} }

Loading…
Cancel
Save