# Unified Orbital Elements Configuration - Implementation Plan ## Overview Replace the inconsistent configuration format between bodies and spacecraft with a unified Keplerian orbital elements system. All objects (both `CelestialBody` and `Spacecraft`) will use the same `OrbitalElements` struct to define initial conditions, and explicit reference frame naming (`global_position`/`global_velocity`) will prevent frame confusion. ## Goals 1. **Unified configuration**: Same orbital element format for both bodies and spacecraft 2. **Human-readable**: Use altitude, eccentricity, true_anomaly instead of manual velocity calculations 3. **No manual calculations**: Velocity automatically computed from orbital elements 4. **Clear reference frames**: Explicit `global_position`/`global_velocity` vs `local_position`/`local_velocity` 5. **Support all orbit types**: Circular, elliptical, parabolic, hyperbolic 6. **Future-proof**: Orbital elements support 3D inclined orbits (deferred implementation) ## Current State ### Issues 1. **Inconsistent configuration approaches**: - **Bodies**: Use `eccentricity` + `semi_major_axis`, velocity auto-calculated by `initialize_bodies()` - **Spacecraft**: Use explicit `position` + `velocity`, no initialization function 2. **Reference frame confusion**: - `position` / `velocity` field names don't indicate frame - Bodies: position is global, spacecraft: position is local - Easy to make mistakes when writing physics code 3. **No orbital elements in code**: - Orbital parameters scattered in `CelestialBody` struct - No `OrbitalElements` struct to pass to initialization functions - Cannot specify inclination, RAAN, argument of periapsis ### Current Config Formats **Bodies (current):** ```toml [[bodies]] name = "Earth" mass = 5.972e24 radius = 6.371e6 position = { x = 1.496e11, y = 0.0, z = 0.0 } # global parent_index = 0 color = { r = 0.0, g = 0.5, b = 1.0 } eccentricity = 0.0 semi_major_axis = 1.496e11 ``` **Spacecraft (current):** ```toml [[spacecraft]] name = "ISS" mass = 420000 position = { x = 0.0, y = 6.779e6, z = 0.0 } # local to parent velocity = { x = 7660.0, y = 0.0, z = 0.0 } # local to parent parent_index = 1 ``` ## Proposed Solution ### New Unified Config Format Both bodies and spacecraft use the same `orbit` table with Keplerian orbital elements: ```toml [[bodies]] name = "Earth" mass = 5.972e24 radius = 6.371e6 parent_index = 0 color = { r = 0.0, g = 0.5, b = 1.0 } orbit = { semi_major_axis = 1.496e11 eccentricity = 0.0 true_anomaly = 0.0 } [[spacecraft]] name = "ISS" mass = 420000 parent_index = 1 orbit = { altitude = 408000 # Convenience: semi_major_axis = radius + altitude eccentricity = 0.001 true_anomaly = 0.0 } ``` **All orbital element fields optional with defaults:** - `semi_major_axis`: Required (or use `altitude` convenience) - `eccentricity`: Default 0.0 (circular) - `true_anomaly`: Default 0.0 (at periapsis) - `inclination`: Default 0.0 (planar) - `longitude_of_ascending_node`: Default 0.0 - `argument_of_periapsis`: Default 0.0 ### Orbital Elements Struct **New file: `src/orbital_mechanics.h`** ```cpp #ifndef ORBITAL_MECHANICS_H #define ORBITAL_MECHANICS_H #include "physics.h" // Keplerian orbital elements struct OrbitalElements { double semi_major_axis; // a (meters) double eccentricity; // e double inclination; // i (radians) double longitude_of_ascending_node; // Ω (radians) double argument_of_periapsis; // ω (radians) double true_anomaly; // ν (radians) }; // Convert orbital elements to local Cartesian position and velocity void orbital_elements_to_cartesian(OrbitalElements elements, double parent_mass, Vec3* out_position, Vec3* out_velocity); #endif ``` **New file: `src/orbital_mechanics.cpp`** - Implement standard orbital mechanics equations - Handle all orbit types: circular (e=0), elliptical (01) - Support planar orbits (inclination=0) now, 3D orientation deferred - Convert elements → local position and velocity vectors **Key equations:** 1. Distance from focus: `r = a(1-e²)/(1+e*cos(ν))` 2. Position in orbital plane: `(r*cos(ν), r*sin(ν), 0)` 3. Velocity magnitude from vis-viva: `v² = GM(2/r - 1/a)` 4. Velocity direction tangent to orbit in orbital plane 5. Apply 3D Euler rotations for inclination/RAAN/argument of periapsis (deferred) ### Updated Data Structures **CelestialBody (simulation.h):** ```cpp struct CelestialBody { char name[64]; double mass; double radius; int parent_index; float color[3]; // Orbital elements from config OrbitalElements orbit; // Global frame (from origin) Vec3 global_position; Vec3 global_velocity; // Local frame (relative to parent) Vec3 local_position; Vec3 local_velocity; double soi_radius; }; ``` **Spacecraft (spacecraft.h):** ```cpp struct Spacecraft { char name[64]; double mass; int parent_index; // Orbital elements from config OrbitalElements orbit; // Global frame (from origin) Vec3 global_position; Vec3 global_velocity; // Local frame (relative to parent) Vec3 local_position; Vec3 local_velocity; }; ``` ## Implementation Steps ### Phase 1: Create Orbital Mechanics Module ✅ COMPLETE 1. ✅ **Create `src/orbital_mechanics.h`** - Define `OrbitalElements` struct - Declare `orbital_elements_to_cartesian()` function 2. ✅ **Create `src/orbital_mechanics.cpp`** - Implement conversion from orbital elements to Cartesian state vectors - Handle all orbit types (circular, elliptical, parabolic, hyperbolic) - Support planar orbits (inclination=0) - Include in Makefile (automatic via wildcard pattern) 3. ~~**Test orbital mechanics module**~~ (deferred to Phase 7) - Unit tests for circular orbit conversion - Unit tests for elliptical orbit conversion - Unit tests for parabolic/hyperbolic conversion - Verify velocity magnitudes match vis-viva equation ### Phase 2: Update Data Structures ✅ COMPLETE 4. ✅ **Modify `CelestialBody` struct (simulation.h)** - Add `OrbitalElements orbit` field - Rename `position` → `global_position` - Rename `velocity` → `global_velocity` - Keep `local_position`, `local_velocity`, `soi_radius`, `parent_index`, `color` 5. ✅ **Modify `Spacecraft` struct (spacecraft.h)** - Add `OrbitalElements orbit` field - Rename `position` → `global_position` - Rename `velocity` → `global_velocity` - Keep `local_position`, `local_velocity`, `parent_index`, `mass`, `name` 6. ✅ **Update all references to position/velocity throughout codebase** - `simulation.cpp`: Update all `body->position` → `body->global_position` - `simulation.cpp`: Update all `body->velocity` → `body->global_velocity` - `simulation.cpp`: Update all `craft->position` → `craft->global_position` - `simulation.cpp`: Update all `craft->velocity` → `craft->global_velocity` - Rename old `OrbitalElements` to `OrbitalMetrics` (for output/analysis) - `renderer.cpp`: Update references - `ui_renderer.cpp`: Update references for display - `config_loader.cpp`: Update parsing logic - `maneuver.cpp`: No changes needed ### Phase 3: Update Config Parser ✅ COMPLETE 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 - Support `altitude` convenience field - Parse into `body->orbit` struct - Remove old `eccentricity` and `semi_major_axis` top-level fields 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** - `semi_major_axis` must not be zero - `eccentricity` must be >= 0 - For elliptical orbits (e < 1): `semi_major_axis > 0` - For hyperbolic orbits (e > 1): `semi_major_axis` negative or handle separately ### Phase 4: Update Initialization 10. **Replace `initialize_bodies()` with `initialize_orbital_objects()` (simulation.cpp)** - For each body: - If `parent_index >= 0`: - Call `orbital_elements_to_cartesian(body->orbit, parent->mass, &local_pos, &local_vel)` - Set `body->local_position = local_pos`, `body->local_velocity = local_vel` - Compute global: `body->global_position = vec3_add(parent->global_position, local_pos)` - Compute global: `body->global_velocity = vec3_add(parent->global_velocity, local_vel)` - Calculate SOI: `body->soi_radius = calculate_soi_radius(body, parent)` - Else (root body): - Set all positions/velocities to zero - Set `soi_radius = 1e15` - For each spacecraft: - Call `orbital_elements_to_cartesian(craft->orbit, parent->mass, &local_pos, &local_vel)` - Set `craft->local_position = local_pos`, `craft->local_velocity = local_vel` - Compute global: `craft->global_position = vec3_add(parent->global_position, local_pos)` - Compute global: `craft->global_velocity = vec3_add(parent->global_velocity, local_vel)` 11. **Remove old `calc_orbital_velocity()` function** (simulation.cpp) - No longer needed, replaced by orbital mechanics module 12. **Update all calls to `initialize_bodies()` → `initialize_orbital_objects()`** - `config_loader.cpp`: Update call after config load - Tests: Update if they call initialization directly ### Phase 5: Update Test Configs 13. **Update all test configs to use new format** **Configs to update:** - `tests/configs/solar_system.toml` - All planets and moons - `tests/configs/earth_circular.toml` - Simple test - `tests/configs/mars_circular.toml` - Simple test - `tests/configs/spacecraft_test.toml` - Spacecraft example - `tests/configs/interplanetary_transfer.toml` - Transfer orbit - `tests/configs/hyperbolic_comet.toml` - Hyperbolic escape - `tests/configs/parabolic_comet.toml` - Parabolic escape - `tests/configs/soi_transition.toml` - SOI crossing - `tests/configs/simple_root_transition.toml` - SOI test - `tests/configs/manual_root_transition.toml` - SOI test - `tests/configs/mutual_soi_close.toml` - Multi-body - `tests/configs/maneuver_sequence.toml` - Maneuver planning **Example transformation:** **Old (earth_circular.toml):** ```toml [[bodies]] name = "Earth" mass = 5.972e24 radius = 6.371e6 position = { x = 1.496e11, y = 0.0, z = 0.0 } parent_index = 0 color = { r = 0.0, g = 0.5, b = 1.0 } eccentricity = 0.0 semi_major_axis = 1.496e11 ``` **New (earth_circular.toml):** ```toml [[bodies]] name = "Earth" mass = 5.972e24 radius = 6.371e6 parent_index = 0 color = { r = 0.0, g = 0.5, b = 1.0 } orbit = { semi_major_axis = 1.496e11 eccentricity = 0.0 true_anomaly = 0.0 } ``` ### Phase 6: Update Renderer 14. **Update renderer.cpp function signatures if needed** - `render_orbit()` - Currently takes position/velocity params, OK - `render_body()` - Uses `body->position`, update to `body->global_position` - `render_simulation()` orbit rendering - Uses `body->position`, update to `body->global_position` - `render_simulation()` spacecraft rendering - Uses `craft->position`, update to `craft->global_position` - `update_camera()` - Uses `body->position` and `craft->position`, update to global variants 15. **Update ui_renderer.cpp** - Update info panel displays to use `global_position`/`global_velocity` - Update body list display if it shows position data ### Phase 7: Update Tests 16. **Update test files that reference position/velocity** - Search for `->position`, `->velocity` references in test code - Update to `->global_position`, `->global_velocity` 17. **Add tests for orbital mechanics module** - `test_orbital_mechanics.cpp`: New test file - Test circular orbit conversion (verify positions/velocities) - Test elliptical orbit conversion - Test parabolic orbit conversion - Test hyperbolic orbit conversion - Verify vis-viva equation holds for converted state 18. **Run full test suite** - `make test` to verify all tests pass - Fix any issues found ### Phase 8: Update Documentation 19. **Update docs/technical_reference.md** - Document new `OrbitalElements` struct - Update `CelestialBody` and `Spacecraft` struct documentation - Document new config format with `orbit` table - Remove old config format documentation - Document `orbital_elements_to_cartesian()` function - Update config validation rules 20. **Update Makefile if needed** - Add `orbital_mechanics.o` to object files - Add `orbital_mechanics.cpp` to build rules ## Files to Modify ### New Files - `src/orbital_mechanics.h` - `src/orbital_mechanics.cpp` - `tests/test_orbital_mechanics.cpp` ### Modified Files - `src/simulation.h` - Update `CelestialBody` struct - `src/spacecraft.h` - Update `Spacecraft` struct - `src/simulation.cpp` - Update initialization, rename position/velocity - `src/config_loader.cpp` - Parse orbit tables instead of position/velocity - `src/renderer.cpp` - Update references to position/velocity - `src/ui_renderer.cpp` - Update display references - `src/Makefile` - Add orbital_mechanics.o ### Config Files (all updated) - `tests/configs/solar_system.toml` - `tests/configs/earth_circular.toml` - `tests/configs/mars_circular.toml` - `tests/configs/spacecraft_test.toml` - `tests/configs/interplanetary_transfer.toml` - `tests/configs/hyperbolic_comet.toml` - `tests/configs/parabolic_comet.toml` - `tests/configs/soi_transition.toml` - `tests/configs/simple_root_transition.toml` - `tests/configs/manual_root_transition.toml` - `tests/configs/mutual_soi_close.toml` - `tests/configs/maneuver_sequence.toml` ### Documentation Files - `docs/technical_reference.md` ## Testing Strategy 1. **Unit tests for orbital mechanics:** - Test circular orbit conversion (e=0) - Test elliptical orbit conversion (01) - Verify velocity magnitudes match vis-viva equation 2. **Integration tests:** - Load solar_system.toml config - Verify all bodies initialized correctly - Verify Earth's orbital period is ~365 days - Run simulation for 1 year, verify stability 3. **Spacecraft tests:** - Load spacecraft_test.toml config - Verify ISS orbit is correct (400km altitude) - Verify spacecraft renders with orbit line 4. **SOI transition tests:** - Load soi_transition.toml config - Verify object transitions between parents correctly 5. **Full test suite:** - Run `make test` to verify all existing tests pass - Fix any failures ## Breaking Changes This is a **major breaking change** that affects all configs and code: 1. **Config format completely changed:** - All test configs must be updated - Old configs will fail to parse 2. **API changes:** - `position` → `global_position` - `velocity` → `global_velocity` - All code referencing these fields must be updated 3. **Removed functions:** - `calc_orbital_velocity()` - replaced by orbital mechanics module 4. **Renamed functions:** - `initialize_bodies()` → `initialize_orbital_objects()` ## Future Work (Deferred) 1. **3D Orbit Orientation:** - Implement full 3D orientation with inclination, RAAN, argument of periapsis - Add Euler rotation matrices to `orbital_elements_to_cartesian()` - Update config documentation for 3D orbit specification - Add tests for inclined orbits 2. **Convenience Parameters:** - Add `excess_velocity` field for hyperbolic escape trajectories - Calculate semi_major_axis from v_infinity: `a = -GM / v_inf²` - Add validation for hyperbolic parameters 3. **Config Format Versioning:** - Add `[simulation]` section with `format_version` field - Help identify old vs new config formats (if we ever need backward compat) ## Success Criteria 1. [ ] All test configs updated to new format 2. [ ] Config loader successfully parses `orbit` tables 3. [ ] `orbital_elements_to_cartesian()` correctly converts all orbit types 4. [ ] `initialize_orbital_objects()` sets up bodies and spacecraft correctly 5. [ ] All references to `position`/`velocity` renamed to `global_*` 6. [ ] Renderer displays orbits correctly 7. [ ] Full test suite passes (`make test`) 8. [ ] Solar system simulation runs correctly 9. [ ] Spacecraft orbits render with cyan lines 10. [ ] Documentation updated ## Notes - All orbital elements use SI units (meters, radians) - `true_anomaly = 0` is at periapsis (closest approach) - `inclination = 0` means orbit is in xy-plane - Planar orbits (current default) have `inclination = longitude_of_ascending_node = argument_of_periapsis = 0` - Parent index remains in outer struct, not in `OrbitalElements` - `altitude` is a convenience parameter: `semi_major_axis = parent_radius + altitude`