17 KiB
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
- Unified configuration: Same orbital element format for both bodies and spacecraft
- Human-readable: Use altitude, eccentricity, true_anomaly instead of manual velocity calculations
- No manual calculations: Velocity automatically computed from orbital elements
- Clear reference frames: Explicit
global_position/global_velocityvslocal_position/local_velocity - Support all orbit types: Circular, elliptical, parabolic, hyperbolic
- Future-proof: Orbital elements support 3D inclined orbits (deferred implementation)
Current State
Issues
-
Inconsistent configuration approaches:
- Bodies: Use
eccentricity+semi_major_axis, velocity auto-calculated byinitialize_bodies() - Spacecraft: Use explicit
position+velocity, no initialization function
- Bodies: Use
-
Reference frame confusion:
position/velocityfield names don't indicate frame- Bodies: position is global, spacecraft: position is local
- Easy to make mistakes when writing physics code
-
No orbital elements in code:
- Orbital parameters scattered in
CelestialBodystruct - No
OrbitalElementsstruct to pass to initialization functions - Cannot specify inclination, RAAN, argument of periapsis
- Orbital parameters scattered in
Current Config Formats
Bodies (current):
[[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):
[[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:
[[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 usealtitudeconvenience)eccentricity: Default 0.0 (circular)true_anomaly: Default 0.0 (at periapsis)inclination: Default 0.0 (planar)longitude_of_ascending_node: Default 0.0argument_of_periapsis: Default 0.0
Orbital Elements Struct
New file: src/orbital_mechanics.h
#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:
- Distance from focus:
r = a(1-e²)/(1+e*cos(ν)) - Position in orbital plane:
(r*cos(ν), r*sin(ν), 0) - Velocity magnitude from vis-viva:
v² = GM(2/r - 1/a) - Velocity direction tangent to orbit in orbital plane
- Apply 3D Euler rotations for inclination/RAAN/argument of periapsis (deferred)
Updated Data Structures
CelestialBody (simulation.h):
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):
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
-
✅ Create
src/orbital_mechanics.h- Define
OrbitalElementsstruct - Declare
orbital_elements_to_cartesian()function
- Define
-
✅ 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)
-
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
-
✅ Modify
CelestialBodystruct (simulation.h)- Add
OrbitalElements orbitfield - Rename
position→global_position - Rename
velocity→global_velocity - Keep
local_position,local_velocity,soi_radius,parent_index,color
- Add
-
✅ Modify
Spacecraftstruct (spacecraft.h)- Add
OrbitalElements orbitfield - Rename
position→global_position - Rename
velocity→global_velocity - Keep
local_position,local_velocity,parent_index,mass,name
- Add
-
✅ Update all references to position/velocity throughout codebase
simulation.cpp: Update allbody->position→body->global_positionsimulation.cpp: Update allbody->velocity→body->global_velocitysimulation.cpp: Update allcraft->position→craft->global_positionsimulation.cpp: Update allcraft->velocity→craft->global_velocity- Rename old
OrbitalElementstoOrbitalMetrics(for output/analysis) renderer.cpp: Update referencesui_renderer.cpp: Update references for displayconfig_loader.cpp: Update parsing logicmaneuver.cpp: No changes needed
Phase 3: Update Config Parser ✅ COMPLETE
-
✅ Modify config_loader.cpp - body parsing
- Remove old
positionfield requirement fromparse_toml_body() - Add
orbittable parsing toparse_toml_body() - Parse all orbital element fields with defaults
- Support
altitudeconvenience field - Parse into
body->orbitstruct - Remove old
eccentricityandsemi_major_axistop-level fields
- Remove old
-
✅ Modify config_loader.cpp - spacecraft parsing
- Replace
position+velocityfields withorbittable parsing - Use same orbital element parsing logic as bodies
- Parse into
craft->orbitstruct
- Replace
-
✅ Add validation for orbital elements
semi_major_axismust not be zeroeccentricitymust be >= 0- For elliptical orbits (e < 1):
semi_major_axis > 0 - For hyperbolic orbits (e > 1):
semi_major_axisnegative or handle separately
Phase 4: Update Initialization ✅ COMPLETE
-
✅ Replace
initialize_bodies()withinitialize_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)
- Call
- Else (root body):
- Set all positions/velocities to zero
- Set
soi_radius = 1e15
- If
-
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)
- Call
-
-
✅ Remove old
calc_orbital_velocity()function (simulation.cpp)- No longer needed, replaced by orbital mechanics module
-
✅ 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
-
Update all test configs to use new format
Configs to update:
tests/configs/solar_system.toml- All planets and moonstests/configs/earth_circular.toml- Simple testtests/configs/mars_circular.toml- Simple testtests/configs/spacecraft_test.toml- Spacecraft exampletests/configs/interplanetary_transfer.toml- Transfer orbittests/configs/hyperbolic_comet.toml- Hyperbolic escapetests/configs/parabolic_comet.toml- Parabolic escapetests/configs/soi_transition.toml- SOI crossingtests/configs/simple_root_transition.toml- SOI testtests/configs/manual_root_transition.toml- SOI testtests/configs/mutual_soi_close.toml- Multi-bodytests/configs/maneuver_sequence.toml- Maneuver planning
Example transformation:
Old (earth_circular.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.496e11New (earth_circular.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
-
Update renderer.cpp function signatures if needed
render_orbit()- Currently takes position/velocity params, OKrender_body()- Usesbody->position, update tobody->global_positionrender_simulation()orbit rendering - Usesbody->position, update tobody->global_positionrender_simulation()spacecraft rendering - Usescraft->position, update tocraft->global_positionupdate_camera()- Usesbody->positionandcraft->position, update to global variants
-
Update ui_renderer.cpp
- Update info panel displays to use
global_position/global_velocity - Update body list display if it shows position data
- Update info panel displays to use
Phase 7: Update Tests
-
Update test files that reference position/velocity
- Search for
->position,->velocityreferences in test code - Update to
->global_position,->global_velocity
- Search for
-
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
-
Run full test suite
make testto verify all tests pass- Fix any issues found
Phase 8: Update Documentation
-
Update docs/technical_reference.md
- Document new
OrbitalElementsstruct - Update
CelestialBodyandSpacecraftstruct documentation - Document new config format with
orbittable - Remove old config format documentation
- Document
orbital_elements_to_cartesian()function - Update config validation rules
- Document new
-
Update Makefile if needed
- Add
orbital_mechanics.oto object files - Add
orbital_mechanics.cppto build rules
- Add
Files to Modify
New Files
src/orbital_mechanics.hsrc/orbital_mechanics.cpptests/test_orbital_mechanics.cpp
Modified Files
src/simulation.h- UpdateCelestialBodystructsrc/spacecraft.h- UpdateSpacecraftstructsrc/simulation.cpp- Update initialization, rename position/velocitysrc/config_loader.cpp- Parse orbit tables instead of position/velocitysrc/renderer.cpp- Update references to position/velocitysrc/ui_renderer.cpp- Update display referencessrc/Makefile- Add orbital_mechanics.o
Config Files (all updated)
tests/configs/solar_system.tomltests/configs/earth_circular.tomltests/configs/mars_circular.tomltests/configs/spacecraft_test.tomltests/configs/interplanetary_transfer.tomltests/configs/hyperbolic_comet.tomltests/configs/parabolic_comet.tomltests/configs/soi_transition.tomltests/configs/simple_root_transition.tomltests/configs/manual_root_transition.tomltests/configs/mutual_soi_close.tomltests/configs/maneuver_sequence.toml
Documentation Files
docs/technical_reference.md
Testing Strategy
-
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
-
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
-
Spacecraft tests:
- Load spacecraft_test.toml config
- Verify ISS orbit is correct (400km altitude)
- Verify spacecraft renders with orbit line
-
SOI transition tests:
- Load soi_transition.toml config
- Verify object transitions between parents correctly
-
Full test suite:
- Run
make testto verify all existing tests pass - Fix any failures
- Run
Breaking Changes
This is a major breaking change that affects all configs and code:
-
Config format completely changed:
- All test configs must be updated
- Old configs will fail to parse
-
API changes:
position→global_positionvelocity→global_velocity- All code referencing these fields must be updated
-
Removed functions:
calc_orbital_velocity()- replaced by orbital mechanics module
-
Renamed functions:
initialize_bodies()→initialize_orbital_objects()
Future Work (Deferred)
-
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
-
Convenience Parameters:
- Add
excess_velocityfield for hyperbolic escape trajectories - Calculate semi_major_axis from v_infinity:
a = -GM / v_inf² - Add validation for hyperbolic parameters
- Add
-
Config Format Versioning:
- Add
[simulation]section withformat_versionfield - Help identify old vs new config formats (if we ever need backward compat)
- Add
Success Criteria
- All test configs updated to new format
- Config loader successfully parses
orbittables orbital_elements_to_cartesian()correctly converts all orbit typesinitialize_orbital_objects()sets up bodies and spacecraft correctly- All references to
position/velocityrenamed toglobal_* - Renderer displays orbits correctly
- Full test suite passes (
make test) - Solar system simulation runs correctly
- Spacecraft orbits render with cyan lines
- Documentation updated
Notes
- All orbital elements use SI units (meters, radians)
true_anomaly = 0is at periapsis (closest approach)inclination = 0means 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 altitudeis a convenience parameter:semi_major_axis = parent_radius + altitude