vibe coding an orbital mechanics simulation to try out claude code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

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

  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):

[[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 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

#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):

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

  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

  1. Modify CelestialBody struct (simulation.h)

    • Add OrbitalElements orbit field
    • Rename positionglobal_position
    • Rename velocityglobal_velocity
    • Keep local_position, local_velocity, soi_radius, parent_index, color
  2. Modify Spacecraft struct (spacecraft.h)

    • Add OrbitalElements orbit field
    • Rename positionglobal_position
    • Rename velocityglobal_velocity
    • Keep local_position, local_velocity, parent_index, mass, name
  3. Update all references to position/velocity throughout codebase

    • simulation.cpp: Update all body->positionbody->global_position
    • simulation.cpp: Update all body->velocitybody->global_velocity
    • simulation.cpp: Update all craft->positioncraft->global_position
    • simulation.cpp: Update all craft->velocitycraft->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

  1. 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
  2. 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
  3. 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

  1. 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)
  2. Remove old calc_orbital_velocity() function (simulation.cpp)

    • No longer needed, replaced by orbital mechanics module
  3. 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

  1. 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):

    [[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):

    [[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

  1. 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
  2. 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

  1. Update test files that reference position/velocity

    • Search for ->position, ->velocity references in test code
    • Update to ->global_position, ->global_velocity
  2. 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
  3. Run full test suite

    • make test to verify all tests pass
    • Fix any issues found

Phase 8: Update Documentation

  1. 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
  2. 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:

    • positionglobal_position
    • velocityglobal_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