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.
 
 
 
 
 

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

Mat3 (physics.h)

struct Mat3 {
    double m00, m01, m02;
    double m10, m11, m12;
    double m20, m21, m22;
};

Row-major 3x3 matrix for 3D rotation operations.

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 is fully implemented using rotation matrices. The rotation sequence is z-x-z Euler angles: R_z(Ω) · R_x(i) · R_z(ω). All 3D parameters are applied in orbital_elements_to_cartesian().

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;

    // Orbital elements for 3D angle calculation
    double inclination;
    double longitude_of_ascending_node;
    double argument_of_periapsis;
    bool has_orbital_elements;
};

3D Support: When has_orbital_elements is true, the tracker transforms 3D positions back to the orbital plane using the stored orbital elements before calculating the angle. This enables accurate period measurement for inclined orbits.

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
  • Matrix operations for 3D orbital orientation:
    • mat3_identity() - returns identity matrix
    • mat3_multiply(Mat3 a, Mat3 b) - matrix-matrix multiplication
    • mat3_multiply_vec3(Mat3 m, Vec3 v) - matrix-vector multiplication
    • mat3_rotation_x(double angle) - rotation about X axis
    • mat3_rotation_z(double angle) - rotation about Z axis
    • mat3_rotation_orbital(double omega, double i, double Omega) - combined z-x-z orbital rotation

Implementation:

  • Row-major 3x3 matrix format (different from raylib's column-major 4x4)
  • Rotation matrices follow standard right-hand rule convention
  • Combined orbital rotation: R_z(Ω) · R_x(i) · R_z(ω) for z-x-z Euler angles

Orbital Mechanics (orbital_mechanics.cpp/h)

Keplerian orbital elements to Cartesian coordinate conversion. Supports all orbit types with full 3D orientation using rotation matrices.

Key functions:

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

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: applies z-x-z Euler rotations (R_z(Ω) · R_x(i) · R_z(ω)) to position and velocity vectors

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 (UIState struct):

  • body_list_scroll - Scroll position for objects list
  • body_list_active - Currently selected item index in body list
  • selected_craft_index - Selected spacecraft index (-1 = no selection)

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

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