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.
 
 
 
 
 

20 KiB

Orbital Mechanics Simulation - Technical Reference

Overview

3D orbital mechanics simulation using 2-body gravitational model with sphere of influence (SOI) transitions. Built with C-style C++ and raylib.

Technical Constraints

  • C-style C++ only: structs and functions, no classes or templates
  • RK4 (Runge-Kutta 4th order) integration for physics
  • Simple rotations (quaternions deferred)

Coordinate Frame Strategy

The simulation uses a hybrid coordinate frame system optimized for numerical precision in nested orbital systems.

Global Frame:

  • Origin at root body (index 0, parent_index = -1)
  • All positions/velocities are in meters relative to this origin
  • Used for SOI calculations, rendering, and interplanetary trajectories

Local Frame:

  • Position/velocity relative to gravitational parent
  • Used for most bodies in stable nested orbits (moons around planets)
  • RK4 integration operates in local frame to preserve floating-point precision

Coordinate Frame Selection:

  • Bodies using local frame: Children with stable orbital relationships (e.g., moons orbiting planets)
  • Bodies using global frame: Direct children of root body (planets) and bodies on interplanetary trajectories
  • Dual coordinate storage maintained for seamless frame transitions during SOI crossings

Benefits:

  • Eliminates large offsets in floating-point calculations (moon at 3.8×10⁸ m instead of 1.5×10¹¹ m)
  • Isolates moon orbits from planetary perturbations
  • Maintains full floating-point precision for small orbital changes
  • Improved Earth-Moon orbital stability (20% drift → stable)

Key Functions:

  • initialize_local_coordinates() - initializes local frame positions/velocities from global coordinates
  • compute_global_coordinates() - computes global positions/velocities from local frames
  • compute_spacecraft_globals() - calculates global coordinates for all spacecraft

Implementation Details:

  • Dual coordinate storage: both local and global coordinates maintained
  • Parent bodies treated as origin in child's reference frame during integration
  • RK4 integration operates on local coordinates
  • Global coordinates computed after each physics step for rendering and SOI checks

Core Data Structures

Vec3 (physics.h)

struct Vec3 {
    double x, y, z;
};

CelestialBody (simulation.h)

struct CelestialBody {
    char name[64];
    double mass;              // kg
    double radius;            // meters
    int parent_index;         // index of gravitational parent (-1 for root body like Sun)
    float color[3];           // RGB color for rendering

    // Orbital elements from config (Keplerian elements)
    OrbitalElements orbit;

    // Global frame (from origin)
    Vec3 global_position;    // meters from origin
    Vec3 global_velocity;    // m/s

    // Local frame (relative to parent)
    Vec3 local_position;      // meters from parent
    Vec3 local_velocity;      // m/s relative to parent

    double soi_radius;        // sphere of influence radius (meters)
};

SimulationState (simulation.h)

struct SimulationState {
    CelestialBody* bodies;
    int body_count;
    int max_bodies;

    Spacecraft* spacecraft;
    int craft_count;
    int max_craft;

    Maneuver* maneuvers;
    int maneuver_count;
    int max_maneuvers;

    double time;              // simulation time (seconds)
    double dt;                // time step (seconds)
    char config_name[256];
};

OrbitalElements (orbital_mechanics.h)

struct OrbitalElements {
    union {
        double semi_major_axis;       // for elliptical (e<1) and hyperbolic (e>1)
        double semi_latus_rectum;     // for parabolic (e≈1)
    };
    double eccentricity;
    double true_anomaly;
    double inclination;
    double longitude_of_ascending_node;
    double argument_of_periapsis;
};

Note: 3D orientation using inclination, longitude_of_ascending_node, and argument_of_periapsis is defined but not yet applied to orbital calculations (deferred implementation).

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;
};

Maneuver (maneuver.h)

enum BurnDirection {
    BURN_PROGRADE,
    BURN_RETROGRADE,
    BURN_NORMAL,
    BURN_ANTINORMAL,
    BURN_RADIAL_IN,
    BURN_RADIAL_OUT,
    BURN_CUSTOM
};

enum TriggerType {
    TRIGGER_TIME,
    TRIGGER_TRUE_ANOMALY
};

struct Maneuver {
    char name[64];
    int craft_index;
    BurnDirection direction;
    double delta_v;
    TriggerType trigger_type;
    double trigger_value;
    bool executed;
    double executed_time;
};

OrbitalMetrics (test_utilities.h)

struct OrbitalMetrics {
    double kinetic_energy;
    double potential_energy;
    double total_energy;
    double orbital_radius;
    double velocity_magnitude;
    double angular_position;
};

OrbitTracker (test_utilities.h)

struct OrbitTracker {
    double initial_angle;
    double previous_angle;
    int quadrant_transitions;
    bool orbit_completed;
    double time_at_completion;
    int body_index;
    double min_time_days;
};

Module Overview

Physics (physics.cpp/h)

Vector math and gravity calculations. RK4 (Runge-Kutta 4th order) integration with rk4_step(). Physics module is independent of simulation structures and accepts direct parameters for improved modularity.

Key functions:

  • rk4_step(Vec3* position, Vec3* velocity, double dt, double body_mass, double parent_mass) - RK4 integration using position/velocity pointers
  • evaluate_acceleration(Vec3 relative_pos, double body_mass, double parent_mass) - computes gravitational acceleration from parent

Orbital Mechanics (orbital_mechanics.cpp/h)

Keplerian orbital elements to Cartesian coordinate conversion. Supports all orbit types with planar orbits (3D orientation deferred).

Key functions:

  • orbital_elements_to_cartesian(OrbitalElements elements, double parent_mass, Vec3* out_position, Vec3* out_velocity) - converts Keplerian elements to local position/velocity

Implementation:

  • Circular orbits (e=0): Position on circle, velocity from vis-viva equation
  • Elliptical orbits (0<e<1): Position from polar equation, velocity from vis-viva
  • Parabolic orbits (e≈1): Uses semi_latus_rectum (p), position r = p/(1+cos(ν)), velocity v = √(2μ/r)
  • Hyperbolic orbits (e>1): Same as elliptical with negative semi_major_axis
  • All elements use SI units (meters, radians)
  • true_anomaly = 0 is at periapsis (closest approach)
  • 3D orientation (inclination, RAAN, argument of periapsis) defined but not yet applied (deferred)

Simulation (simulation.cpp/h)

Simulation state management and updates. SOI detection using Hill sphere: r_soi = a * (m/M)^(2/5).

Key functions:

  • find_dominant_body() - determines which body has gravitational dominance
  • update_soi() - calculates sphere of influence radius using Hill sphere
  • update_simulation() - runs one physics step: finds dominant parent, calculates gravity, applies RK4 integration
  • initialize_local_coordinates() - converts global to local coordinates on load
  • compute_global_coordinates() - converts local to global coordinates after update
  • Dynamic parent switching when bodies cross SOI boundaries (with hysteresis)

Update Order:

  • Root bodies updated first (in their own frame = global)
  • Global coordinates computed for roots
  • Child bodies updated in parent's local frame
  • Global coordinates computed for all children

Additional functions:

  • create_simulation() / destroy_simulation() - memory management for simulation state
  • add_body_to_simulation() / add_spacecraft() - dynamic addition of bodies and spacecraft
  • update_bodies_physics() / update_spacecraft_physics() - separated physics updates
  • execute_pending_maneuvers() - processes scheduled burn maneuvers
  • compute_spacecraft_globals() - calculates global coordinates for all spacecraft
  • initialize_bodies() - combined initialization for velocities, SOI radii, and local coordinates

Maneuver (maneuver.cpp/h)

Burn execution system for spacecraft orbital maneuvers. Supports multiple burn directions and trigger types.

Enums:

  • BurnDirection: PROGRADE, RETROGRADE, NORMAL, ANTINORMAL, RADIAL_IN, RADIAL_OUT, CUSTOM
  • TriggerType: TIME, TRUE_ANOMALY

Key functions:

  • Direction calculation: calculate_prograde_dir(), calculate_retrograde_dir(), calculate_normal_dir(), calculate_antinormal_dir(), calculate_radial_in_dir(), calculate_radial_out_dir(), get_burn_direction_vector()
  • Burn application: apply_impulsive_burn(), apply_custom_burn()
  • Execution: check_maneuver_trigger(), execute_maneuver()
  • Utility: calculate_orbital_velocity()

Implementation:

  • All burn direction vectors calculated in local frame relative to current orbital state
  • Impulsive burns apply instantaneous velocity changes
  • Trigger types allow time-based or orbital-position-based burn execution
  • Maneuvers track execution state and timestamp

Config Loader (config_loader.cpp/h)

TOML-based config parser using tomlc17 library. Auto-calculates circular orbit velocities from orbital elements and SOI radii.

Key functions:

  • load_system_config() - main entry point, loads bodies/spacecraft/maneuvers from config file
  • parse_toml_body() - parses individual body entries
  • parse_toml_spacecraft() - parses spacecraft entries
  • parse_toml_maneuver() - parses maneuver entries
  • load_spacecraft_from_toml() - loads spacecraft array from config
  • load_maneuvers_from_toml() - loads maneuvers array from config

Config format details:

  • TOML arrays: [[bodies]], [[spacecraft]], [[maneuvers]]
  • Comments start with #
  • parent_index = -1 indicates root body (star)
  • Supports nested orbits (planets with moons)
  • All numeric values accept integers or floats
  • Uses orbital elements table (Keplerian elements) instead of state vectors

Config format (TOML) - Bodies:

[[bodies]]
name = "Sun"
mass = 1.989e30
radius = 6.96e8
parent_index = -1
color = { r = 1.0, g = 1.0, b = 0.0 }
orbit = {
    semi_major_axis = 0.0,
    eccentricity = 0.0,
    true_anomaly = 0.0
}

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

Config format (TOML) - Spacecraft:

[[spacecraft]]
name = "LEO_Satellite"
mass = 1000.0
parent_index = 1
orbit = {
    semi_major_axis = 6.571e6,
    eccentricity = 0.0,
    true_anomaly = 0.0
}
  • Uses orbit table for orbital elements (same as bodies)
  • Optional 3D orbital elements (inclination, longitude_of_ascending_node, argument_of_periapsis) are defined but not yet applied (deferred)

Config format (TOML) - Maneuvers:

[[maneuvers]]
name = "orbit_raise"
spacecraft_name = "LEO_Satellite"
trigger_type = "time"
trigger_value = 3600.0
direction = "prograde"
delta_v = 500.0
  • trigger_type: "time" (seconds) or "true_anomaly" (radians)
  • direction: "prograde", "retrograde", "normal", "antinormal", "radial_in", "radial_out"
  • delta_v: velocity change magnitude in m/s

Config Validator (config_validator.cpp/h)

Validation module that ensures config files define physically realistic systems. Called automatically after loading config and initializing orbital objects.

Key functions:

  • run_all_config_validations() - master validation function that calls all validators
  • validate_parent_index_ordering() - ensures parent indices are properly ordered
  • validate_orbital_elements() - checks orbital elements for valid values
  • validate_initial_positions() - ensures bodies aren't inside their parent
  • validate_mass_ratios() - checks parent-child mass ratios for hierarchical consistency
  • validate_soi_overlap() - detects bodies with overlapping spheres of influence
  • validate_nested_orbits() - ensures moons orbit within stable boundaries

Validation rules:

  • Body count must not exceed max_bodies
  • Spacecraft count must not exceed max_craft
  • Maneuver count must not exceed max_maneuvers
  • Body parent_index must be < body index or -1 (root)
  • Spacecraft parent_index must reference valid body
  • Maneuver spacecraft_name must reference existing spacecraft
  • Maneuver names must be unique within config
  • Eccentricity must be >= 0
  • Parabolic orbits (e≈1) require semi_latus_rectum (not semi_major_axis)
  • Elliptical/hyperbolic orbits require semi_major_axis (not semi_latus_rectum)
  • Elliptical orbits (e<1) require positive semi_major_axis
  • Bodies with eccentricity < 1 and semi_major_axis <= 0 are rejected
  • Parent-child distance must exceed combined radii
  • Spacecraft must be at least parent's radius away
  • Mass ratio validation: For root children with radius > 50% of parent, mass ratio >= 1000
  • SOI overlap validation: Bodies sharing same parent must not have overlapping SOIs
  • Nested orbit validation: Moons (small radius, circular orbits) must orbit within 5x parent SOI

Renderer (renderer.cpp/h)

Raylib 3D visualization system. See docs/rendering.md for complete documentation including:

  • Camera controls and follow system
  • Object rendering (bodies, spacecraft, maneuvers)
  • Orbit path rendering (elliptical, parabolic, hyperbolic)
  • Coordinate transformation and scaling

UI Renderer (ui_renderer.cpp/h)

Raygui-based UI system for rendering interactive panels. Handles all 2D overlay elements.

Key functions:

  • render_info() - Bottom-left info panel showing simulation time, FPS, and controls
  • render_body_list_ui() - Top-left objects list panel for selecting bodies/spacecraft
  • render_body_info_ui() - Top-right panel showing selected object details
  • render_maneuver_list_ui() - Maneuver list panel for spacecraft planning

UI State:

  • body_list_scroll - Scroll position for objects list
  • body_list_active - Currently selected item index

Implementation:

  • Uses raygui header-only library
  • All UI rendering happens after 3D scene rendering
  • Manages user interaction for object selection and camera following

Test Utilities (test_utilities.cpp/h)

Test helper functions for orbital mechanics validation.

Key functions:

  • calculate_kinetic_energy() - computes kinetic energy of a body
  • calculate_potential_energy_pair() - computes gravitational potential energy between two bodies
  • calculate_system_total_energy() - sums total energy of entire system
  • calculate_orbital_metrics() - returns comprehensive orbital state metrics
  • create_orbit_tracker() - initializes orbit completion tracking
  • update_orbit_tracker() - tracks orbital progress and detects completion
  • compare_double() / compare_vec3() - floating-point comparison with tolerance

Main Program (main.cpp)

GUI-only application with interactive 3D visualization.

  • Initializes simulation with MAX_BODIES=100, TIME_STEP=60 seconds
  • Runs 100 physics steps per frame (adjustable with speed multiplier)
  • Game loop: input handling → camera update → physics update (if not paused) → rendering
  • Supports speed multiplier (2x/0.5x per keypress, min 0.125x)
  • Default config: tests/configs/solar_system.toml

Controls:

  • Arrow keys: Rotate and zoom camera
  • Space: Pause/Resume
  • +/-: Speed up/slow down simulation
  • I: Toggle info display
  • ESC: Quit

Build System

Makefile Targets

  • make - Build raylib (first time) and compile sources to orbit_sim
  • make rebuild - Clean and rebuild
  • make clean - Remove build artifacts
  • make clean-all - Clean everything including raylib
  • make run - Build and run the simulation
  • make test - Run full automated test suite
  • make test-build - Build test executable

Dependencies

  • g++ (C++14)
  • raylib (built automatically from ext/raylib/src)
  • tomlc17 (included in ext/tomlc17/src)
  • Catch2 (for testing)
  • libX11, libGL, libpthread (system libraries)

Test Infrastructure

  • Framework: Catch2 for unit testing
    • use ./orbit_test -s '[CONFIG_NAME]' to show extra [INFO] messages on passing tests if needed
  • Test Configs: tests/configs/ contains test scenarios
    • solar_system.toml - Full solar system with moons
    • earth_circular.toml, mars_circular.toml - Simple orbital tests
    • parabolic_comet.toml - Parabolic orbit (e=1.0)
    • hyperbolic_comet.toml - Hyperbolic orbit (e>1.0)
    • soi_transition.toml - SOI crossing test (3-body system)
  • Test Files:
    • test_energy.cpp - Energy conservation validation
    • test_moon_orbits.cpp - Moon orbital stability tests
    • test_orbital_period.cpp - Orbital period verification
    • test_parabolic_orbit.cpp - Parabolic orbit tests
    • test_hyperbolic_orbit.cpp - Hyperbolic orbit tests
    • test_soi_transition.cpp - SOI transition validation

Orbit Types

Elliptical Orbits (0 ≤ e < 1)

  • Standard planetary and moon orbits
  • Eccentricity e = 0 (circular) to e < 1 (elliptical)
  • Total energy is negative (bound to parent)
  • Velocity follows vis-viva equation: v² = GM(2/r - 1/a)

Parabolic Orbits (e = 1)

  • Escape trajectories with exactly escape velocity
  • Total energy is zero (marginally unbound)
  • Escape velocity: v² = 2GM/r
  • Rendered using true anomaly range: -π0.95 to π0.95
  • Used for comets on escape trajectories

Hyperbolic Orbits (e > 1)

  • Fast escape trajectories exceeding escape velocity
  • Total energy is positive (unbound)
  • Asymptotic velocity: v∞ = √(2GM/|a|) where a < 0
  • Shows open curve with asymptotic behavior
  • Used for high-speed comets and interstellar objects

Data Flow

Initialization Sequence

  1. Configuration file → load_system_config() → populates SimulationState:
    • Loads bodies array from [[bodies]]
    • Loads spacecraft array from [[spacecraft]]
    • Loads maneuvers array from [[maneuvers]]
  2. initialize_orbital_objects() → combined initialization:
    • Converts orbital elements to local position/velocity for all bodies
    • Calculates circular orbit velocities for all bodies (using vis-viva equation)
    • Computes sphere of influence radius for each body (Hill sphere)
    • Sets local coordinates (position/velocity) for all bodies and spacecraft
    • Initializes spacecraft global coordinates
  3. run_all_config_validations() → validates loaded configuration:
    • Checks parent_index ordering
    • Validates orbital elements
    • Checks mass ratios between parent and child bodies
    • Detects overlapping SOIs between bodies sharing same parent
    • Validates nested orbit boundaries (moons within parent's SOI)
    • Ensures initial positions don't collide with parent body

Main Simulation Loop

  1. update_simulation() → for each body:
    • find_dominant_body() → determine gravitational parent based on SOI
    • evaluate_acceleration() → compute gravitational force from parent
    • rk4_step() → update position/velocity using Runge-Kutta 4th order
  2. render_simulation() → for each body:
    • scale_position() → convert to render coordinates using logarithmic scaling
    • scale_radius() → convert to render size using exponential scaling
    • render_body() → draw sphere with color

SOI Transition Mechanics

  • Bodies dynamically switch gravitational parents when crossing SOI boundaries
  • Uses 0.5x distance hysteresis to prevent oscillation between parents
  • find_dominant_body() checks all bodies and selects most dominant influence

Technical Notes

Code Style and Architecture

  • C-style C++: structs and functions only, no classes or templates
  • All headers use include guards
  • Memory management uses malloc/free
  • Layer separation: Physics, Simulation, Configuration, Rendering layers
  • Physics module is independent of simulation structures (parameter-based signatures)

Physics Considerations

  • Timestep: 60 seconds for solar system scale
  • Circular orbit velocity: v = sqrt(G * M / r)
  • Physics steps per frame: 100 (default) with speed multiplier adjustment
  • Simulation time per frame: 60s * 100 = 6000 seconds at 1x speed
  • SOI (Sphere of Influence) uses Hill sphere approximation: r_soi = a * (m/M)^(2/5)
  • SOI transitions use 0.5x distance hysteresis to prevent oscillation
  • Parabolic orbits use escape velocity: v² = 2GM/r
  • Hyperbolic orbits have positive total energy and asymptotic velocity