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.
 
 
 
 
 

11 KiB

Plan: Add Molniya Orbit Test Case

Overview

Add test cases for highly inclined orbits, specifically focusing on Molniya orbits and generic inclined orbit behavior.

Background - Molniya Orbit Properties

From Wikipedia research, Molniya orbits are designed for high-latitude coverage:

  • Orbital Period: ~718 minutes (~12 hours, half a sidereal day)
  • Eccentricity: ~0.74
  • Inclination: 63.4° (critical value that prevents perigee precession)
  • Argument of Perigee: 270° (apogee at northernmost point)
  • Perigee altitude: ~600 km
  • Apogee altitude: ~39,700 km
  • Semi-major axis: ~26,600 km

Current Codebase Status

Earth as Root Body

SUPPORTED

  • Validator checks: parent_index < body_index or -1
  • Earth can be root if it's first body (index 0)
  • No special validation prevents this

3D Orientation Support

DEFINED BUT NOT APPLIED

  • inclination, longitude_of_ascending_node, argument_of_periapsis exist in OrbitalElements struct
  • orbital_elements_to_cartesian() in src/orbital_mechanics.cpp only produces 2D orbits (x, y, z=0)
  • Documented as "deferred implementation" in technical_reference.md line 114
  • Config parser supports loading these parameters (src/config_loader.cpp)
  • Test approach: Tests will expect 3D behavior and fail with [!mayfail] tag

Implementation Plan

Step 1: Create Test Configuration File

File: tests/test_molniya.toml

# Test Configuration: Molniya Orbit
# Earth as root body with highly elliptical, highly inclined satellite orbit

[[bodies]]
name = "Earth"
mass = 5.972e24
radius = 6.371e6
parent_index = -1
color = { r = 0.0, g = 0.5, b = 1.0 }
orbit = {
    semi_major_axis = 0.0,
    eccentricity = 0.0,
    true_anomaly = 0.0
}

[[spacecraft]]
name = "Molniya_Satellite"
mass = 1000.0
parent_index = 0
orbit = {
    semi_major_axis = 26540000.0,
    eccentricity = 0.74,
    true_anomaly = 0.0,
    inclination = 1.107,           # 63.4° in radians
    longitude_of_ascending_node = 0.0,
    argument_of_periapsis = 4.71    # 270° in radians
}

Key points:

  • Earth is root body (index 0, parent_index = -1)
  • Spacecraft uses Molniya orbital parameters
  • 3D orientation parameters included even though not applied yet

Step 2: Create Test File

File: tests/test_inclined_orbits.cpp

Test Cases:

  1. Molniya Position Verification (tagged [!mayfail])

    • Test satellite position at true_anomaly = 0 (perigee)
    • Test satellite position at true_anomaly = π/2
    • Test satellite position at true_anomaly = π (apogee)
    • Test satellite position at true_anomaly = 3π/2
    • Verify radii match polar equation: r = a(1-e²)/(1+e·cos(ν))
    • Check z-coordinate ≠ 0 for inclined orbits (will fail until 3D implemented)
    • Tolerance: ±10 km (10,000 meters)
  2. Molniya Orbital Period (tagged [!mayfail])

    • Verify period matches theoretical calculation from Kepler's 3rd law
    • Theoretical period: T = 2π√(a³/μ)
    • Use OrbitTracker with minimum time to prevent false completion
    • Expected: ~11.96 hours
    • Tolerance: ±600 seconds (10 minutes)
  3. Generic Inclined Orbit (tagged [!mayfail])

    • Test moderate inclination (45°) with e=0.5, a=10,000 km
    • Verify z-coordinate is non-zero (will fail until 3D implemented)
    • Verify position magnitude matches orbital radius
    • Simpler case to test 3D orientation without Molniya complexity
  4. Inclination Parameter Preservation

    • Verify config loader correctly reads and preserves inclination value
    • This should PASS (just checking config loading, not physics)
    • Ensures 3D parameters are being stored correctly

Constants:

  • POSITION_TOLERANCE_METERS = 10000.0 (±10 km per user request)
  • PERIOD_TOLERANCE_SECONDS = 600.0 (±10 minutes)

Step 3: Update Build System

Add test file to existing test build (Catch2 automatically includes all test_*.cpp files)

Step 4: Expected Behavior

Before 3D Implementation

  • All tests with [!mayfail] tag will run but failure is acceptable
  • Position tests: Pass radius check, fail on z-coordinate check
  • Period test: May pass (period independent of inclination for same orbital energy)
  • Generic inclined test: Fail on z-coordinate check
  • Config preservation test: PASS (just checks config loading)

After 3D Implementation

  • All position tests should PASS
  • Period test should PASS
  • Generic inclined test should PASS
  • Config preservation test should PASS
  • Remove [!mayfail] tags from passing tests

Implementation Findings

Bug Discovered in Spacecraft Initialization

Location: src/config_loader.cpp lines 257-259

Code:

if (!is_parabolic && craft->parent_index >= 0 && craft->parent_index < sim->body_count) {
    CelestialBody* parent = &sim->bodies[craft->parent_index];
    craft->orbit.semi_major_axis += parent->radius;  // BUG: Always adds parent radius
}

Problem:

  • For spacecraft, code unconditionally adds parent radius to semi_major_axis
  • For bodies, this logic does NOT exist (bodies use semi_major_axis as-is)
  • This creates an inconsistency between body and spacecraft orbital definitions
  • The logic was intended for altitude parameter convenience but is applied even when semi_major_axis is specified directly

Impact on Molniya Tests:

  • Config specifies: semi_major_axis = 26,540,000 m
  • Actual used: 26,540,000 + 6,371,000 = 32,911,000 m
  • Position errors: 1.7M m at perigee, 11.1M m at apogee
  • Orbital period: ~16.5 hours instead of expected ~12 hours
  • All radius-based tests fail with errors far exceeding ±10 km tolerance

Root Cause: The config loader has these paths (simplified):

// For bodies:
if (semi_major_axis.type == TOML_FP64) {
    body->orbit.semi_major_axis = semi_major.u.fp64;  // Used directly
} else if (altitude.type == TOML_FP64) {
    body->orbit.semi_major_axis = altitude.u.fp64;  // Stored as altitude
}

// For spacecraft:
if (semi_major_axis.type == TOML_FP64) {
    craft->orbit.semi_major_axis = semi_major.u.fp64;  // Stored as SMA
} else if (altitude.type == TOML_FP64) {
    craft->orbit.semi_major_axis = altitude.u.fp64;  // Stored as altitude
}

// AFTER loading all spacecraft (line 253-260):
for (int i = 0; i < sim->craft_count; i++) {
    Spacecraft* craft = &sim->spacecraft[i];
    bool is_parabolic = (fabs(craft->orbit.eccentricity - 1.0) < 0.005);
    if (!is_parabolic && craft->parent_index >= 0) {
        CelestialBody* parent = &sim->bodies[craft->parent_index];
        craft->orbit.semi_major_axis += parent->radius;  // ALWAYS adds!
    }
}

Required Fix: The logic should only add parent radius when altitude was specified:

  • Option A: Track which parameter was used during parsing
  • Option B: Check if semi_major_axis < parent radius (altitude would be small)
  • Option C: Only apply this transformation for altitude-based initialization

Additional Issues Found

OrbitTracker Not Completing

  • Symptom: Molniya period test never completes orbit (tracker->orbit_completed = false)
  • Possible causes:
    1. Minimum time threshold too restrictive (currently 0.01 days = 14.4 minutes)
    2. Quadrant transition logic failing for highly elliptical orbits
    3. Angle comparison tolerance too tight for large orbital variations
  • Status: Not investigated yet, secondary priority to config bug

Test File Name Mismatch

  • Original plan: tests/test_molniya.toml
  • Actual created: tests/test_inclined_orbits.toml (matches test file name)
  • Reason: User preference to have config filename match test filename
  • Status: RESOLVED (updated config in final implementation)

Test Verification Formula

Orbital Radius at True Anomaly

r = a(1-e²) / (1 + e·cos(ν))

Where:

  • a = semi-major_axis
  • e = eccentricity
  • ν = true_anomaly

Orbital Period (Kepler's 3rd Law)

T = 2π√(a³/μ)

Where:

  • μ = G·M (standard gravitational parameter)
  • M = parent body mass

Questions Resolved

  1. Tolerance: ±10 km (10,000 meters) - chosen as moderate tolerance for RK4 integration
  2. Test Scope: Both Molniya + generic inclined orbit tests for completeness
  3. 3D Status: Tests expect 3D and fail with [!mayfail] tag, documenting expected behavior

Next Steps After This Plan

  1. Implement 3D orientation in src/orbital_mechanics.cpp

    • Apply rotation matrices for inclination, RAAN, and argument of periapsis
    • Update orbital_elements_to_cartesian() to include 3D transformations
  2. Remove [!mayfail] tags from tests that now pass

  3. Add additional 3D-specific tests:

    • Verify correct orientation of orbital plane
    • Test ascending/descending node crossings
    • Verify argument of periapsis positioning

Files to Create

  • tests/test_molniya.toml (new test config)
  • tests/test_inclined_orbits.cpp (new test file)

Files to Potentially Modify (future 3D implementation)

  • src/orbital_mechanics.cpp - add 3D rotation logic
  • src/orbital_mechanics.h - potentially expose rotation helper functions

Bug Fix Implementation

Decision Made

Remove altitude parameter entirely rather than fix it, based on:

  • Minimal usage: Only 3 test configs, 0 production configs
  • High complexity cost: Requires tracking parameter usage, post-processing loop
  • Confusing semantics: Bodies say they'll add radius but don't
  • Clean alternative: Explicit semi_major_axis is unambiguous

Changes Implemented

1. Updated Test Configs

Replaced altitude with explicit semi_major_axis in:

  • tests/test_maneuver_planning.toml
  • tests/test_maneuvers.toml
  • tests/test_orbit_rendering.toml

Change pattern:

# OLD:
orbit = { altitude = 400000.0, ... }

# NEW:
orbit = { semi_major_axis = 6.771e6, ... }
# LEO orbit: 400 km altitude (Earth radius 6.371e6 m + 400e3 m)

2. Removed Altitude from Body Parsing

File: src/config_loader.cpp (lines 70-73)

  • Removed altitude variable declaration
  • Removed altitude handling block (lines 99-101)
  • Updated error messages to only mention semi_major_axis

3. Removed Altitude from Spacecraft Parsing

File: src/config_loader.cpp (lines 296-299)

  • Removed altitude variable declaration
  • Removed altitude handling block (lines 325-327)
  • Updated error messages to only mention semi_major_axis

4. Removed Buggy Post-Processing Loop

File: src/config_loader.cpp (lines 249-257)

  • Deleted entire loop that unconditionally added parent radius to spacecraft semi_major_axis
  • This was the root cause of the bug

5. Updated Technical Reference

File: docs/technical_reference.md (line 317)

  • Removed documentation of altitude convenience feature
  • Simplified to only mention semi_major_axis as required parameter

Test Results After Fix

Molniya Tests:

  • Position verification: PASS (radius error: 0 m, was 1.7M m)
  • Z-coordinate checks: FAIL (expected, 3D deferred)
  • Orbital period: INCOMPLETE (separate OrbitTracker issue, not related to bug)

All Tests:

  • 36 tests passed
  • 3 tests failed as expected (Molniya 3D tests)
  • 0 regressions

Remaining Issues

  1. OrbitTracker not completing orbits for Molniya period test

    • May need to increase MAX_SIMULATION_HOURS or fix tracking logic
    • Separate from altitude parameter bug
  2. 3D orientation not implemented (documented as deferred)

    • Molniya tests will fail z-coordinate checks until implemented