diff --git a/docs/unified_orbital_elements_plan.md b/docs/unified_orbital_elements_plan.md new file mode 100644 index 0000000..a064861 --- /dev/null +++ b/docs/unified_orbital_elements_plan.md @@ -0,0 +1,484 @@ +# 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 + +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 + +3. **Test orbital mechanics module** + - 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 + +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` + - `renderer.cpp`: Update references (mostly OK, uses `position` and `velocity` params) + - `ui_renderer.cpp`: Update references for display + - `config_loader.cpp`: Update parsing logic + - `maneuver.cpp`: Update if needed + +### Phase 3: Update Config Parser + +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`