1 changed files with 484 additions and 0 deletions
@ -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 (0<e<1), parabolic (e=1), hyperbolic (e>1) |
||||
- 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 (0<e<1) |
||||
- Test parabolic orbit conversion (e=1) |
||||
- Test hyperbolic orbit conversion (e>1) |
||||
- 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` |
||||
Loading…
Reference in new issue