New modular API:
- solve_kepler_elliptical(M, e): Newton-Raphson for E - e·sin(E) = M
- solve_kepler_hyperbolic(M, e): Solver for H - e·sinh(H) = M
- eccentric_to_true_anomaly(E, e): Convert eccentric to true anomaly
- hyperbolic_to_true_anomaly(H, e): Convert hyperbolic to true anomaly
- mean_anomaly_to_true_anomaly(M, e): Unified wrapper (dispatches based on e)
Changes:
- Renamed solve_kepler_equation() → solve_kepler_elliptical() for clarity
- Extracted KEPLER_TOLERANCE and KEPLER_MAX_ITERATIONS constants
- Separated hyperbolic solver logic from combined function
- Fixed test_newton_raphson_convergence to verify Kepler's equation
(instead of incorrectly expecting E ≈ M for small e)
- Added TODO comment for future cartesian_to_orbital_elements refactoring
New validation function validate_true_anomaly_ranges() checks that
hyperbolic orbits (e > 1) have true anomalies within physically valid
range: |ν| < arccos(-1/e)
This prevents configs from specifying starting positions that would
cause negative radius values (1 + e·cos(ν) ≤ 0).
Bug: r_dot_e was incorrectly divided by mu instead of r_mag*e_mag,
causing cos_nu values of ~7.5e6 (far outside [-1,1]) which made acos() return NaN.
Fix: Calculate cos_nu correctly as r_dot_e/(r_mag*e_mag) and clamp
to [-1, 1] to handle floating-point precision errors.
This fixes all cartesian <-> orbital elements round-trip conversion tests.
Added 6 test files for Newton-Raphson solver and analytical propagation:
- test_cartesian_to_elements_basic.cpp: Tests state vector ↔ orbital elements conversion
- test_newton_raphson_convergence.cpp: Tests Newton-Raphson solver convergence behavior
- test_analytical_propagation_apsides.cpp: Tests propagation through orbital apsides
- test_analytical_propagation_timesteps.cpp: Tests propagation with various timesteps
- test_extreme_eccentricity.cpp: Tests near-parabolic and hyperbolic orbits
- test_precision_boundaries.cpp: Tests exact boundary value handling
Implemented core orbital mechanics functions:
- solve_kepler_equation(): Newton-Raphson solver with 1e-10 tolerance
- get_initial_trial_value(): Series expansion initial guess
- cartesian_to_orbital_elements(): State vectors to orbital elements conversion
- propagate_orbital_elements(): Analytical propagation using Kepler's equation
Updated test plan document with current progress and remaining tests.
Test status: 66 passed, 14 failed (out of 80 test cases)
- Failing tests are expected: implementation needs debugging
- Config validation issues fixed by adjusting orbital parameters
- Remove unconditional reset of ui_state.selected_craft_index in render_body_list_ui()
- Only reset ui_state.selected_craft_index to -1 when body is selected
- This prevents panel from disappearing in subsequent frames when spacecraft remains selected
- Ensure camera is always at least INITIAL_DISTANCE_RADIUS_MULTIPLIER (100x) away from target body's radius
- get_initial_camera_distance() returns max(average_children_distance, body_radius * 100) when children exist
- get_initial_camera_distance() returns body_radius * 100 when no children
- Prevents camera clipping when switching from Sun (large radius) to Earth (small radius) with close children
- Remove selected_craft_index from RenderState, move to UIState
- Camera now always targets bodies (directly selected or parent of selected spacecraft)
- UI handles all spacecraft selection logic
- Remove ~30 lines of duplicate/special-case code from renderer
- Update all renderer functions to single body-focused path
- Update UI rendering functions to use ui_state->selected_craft_index
- Add orbital element fields to OrbitTracker struct (inclination, RAAN, argument_of_periapsis)
- Create create_orbit_tracker_3d() for initializing with orbital elements
- Modify update_orbit_tracker() to use inverse rotation matrix for angle calculation
- Transform 3D position back to orbital plane before computing angle
- Update Molniya period test to use 3D tracker and relaxed tolerance (30 min)
- Remove remaining [!mayfail] tags
All 47 test cases now pass with 239,431 assertions
- Modify orbital_elements_to_cartesian() to apply z-x-z Euler rotations
- Apply rotation: R_z(Ω) · R_x(i) · R_z(ω) to position and velocity
- Fix generic inclined orbit test: set argument_of_periapsis to π/2
- Molniya position tests now pass with non-zero z-coordinates
- Period test still fails (orbit tracker doesn't handle 3D orbits yet)
- Remove altitude convenience parameter from body and spacecraft config parsing
- Update test configs to use explicit semi_major_axis instead of altitude
- Remove buggy post-processing loop that added parent radius to all spacecraft
- Result: Semi-major axis now used as-is (correct behavior)
- Test results: Molniya position tests now pass (was failing by 1-11M meters)
- Documentation: Remove altitude references from technical reference
- Planning: Document bug fix decision and implementation details
Changes:
- src/config_loader.cpp: Remove altitude parsing and post-processing
- tests/test_maneuver_planning.toml: Use semi_major_axis = 6.771e6
- tests/test_maneuvers.toml: Use semi_major_axis = 6.771e6
- tests/test_orbit_rendering.toml: Use semi_major_axis = 6.771e6
- docs/technical_reference.md: Remove altitude documentation
- docs/planning/molniya-orbit-test-plan.md: Document bug fix approach
- Change scale_radius() from logarithmic to linear scaling
- Add get_initial_camera_distance() for auto camera positioning
- Implement selected-body-relative rendering (body at origin)
- Add child indicators with hollow circles and text labels
- Restructure render_simulation() with conditional logic
- Fix zoom camera min_distance check for both directions
- Camera automatically positioned based on average child distance
- Changed scale_radius() from linear to logarithmic: scale * log10(radius)
- Adjusted size_scale from 1e-9 to 0.02 for better visual hierarchy
- Lowered min_radius from 0.5f to 0.01f for fine-grained visibility
- Updated rendering documentation with new scaling values
This handles extreme radius ranges (1.5M to 700M meters) while maintaining
visible size differences between Sun, planets, and moons.
Added test case validating prograde motion in simulation frame.
Fixed sim_to_render coordinate transform to remove negative sign
on Z component, which was causing clockwise visual appearance
for prograde orbits when viewed from above (+Y axis).
- Rewrote orbital_mechanics.cpp with complete velocity component handling
- Added proper parabolic case in velocity section using semi_latus_rectum
- Parabolic velocity: vx = -sqrt(μ/p)*sin(ν), vy = sqrt(μ/p)*(1+cos(ν))
- Updated parabolic_comet.toml to use semi_latus_rectum = 2.992e11 (p=2 AU)
- With true_anomaly = 0.0, comet at perihelion (r = 1 AU, v = 42.13 km/s)
Test Results:
- 31 passed / 1 failed (up from 29/3)
- Parabolic orbit tests: All 3 assertions pass ✓
- Position and velocity mathematically correct for parabolic orbits
- Total energy approximately zero (FP precision: ~200 J/kg)
Note: test_invalid_parent_assignment still fails - pre-existing issue unrelated to parabolic implementation.
- Added union in OrbitalElements (semi_major_axis | semi_latus_rectum)
- Updated config_loader to parse and validate semi_latus_rectum for parabolic orbits
- Validation ensures correct parameter (semi_latus_rectum for e≈1, semi_major_axis for other)
- Updated orbital_mechanics.cpp parabolic case: r = p / (1 + cos(ν))
- Updated parabolic_comet.toml to use semi_latus_rectum = 1.496e11
- Tolerance for parabolic detection: |e - 1.0| < 0.005
Note: Parabolic test still fails velocity tolerance check due to floating-point precision
at r=0.5 AU, E ≈ -200 J/kg (not exactly 0)
- Added orbital_mechanics module with orbital_elements_to_cartesian()
- Updated initialize_orbital_objects() to use orbital mechanics
- Added validate_initial_positions() for post-initialization checking
- Fixed test files to use global_position/global_velocity
- Updated config loader to support spacecraft altitude parameter
- Fixed orbital_mechanics.cpp velocity calculation bug (removed duplicate scaling)
- Updated Makefile to include orbital_mechanics.o in test build
- Renamed simulation.h OrbitalMetrics to OrbitalAnalysis to avoid conflict
- Added docs/parabolic_union_implementation.md for parabolic orbit support plan
Note: Test configs still need manual fix for orbit table TOML syntax
- Replace position/velocity with orbit table parsing
- Parse all orbital element fields with defaults
- Support altitude convenience field for semi_major_axis
- Add validation for orbital elements
- Remove manual position/velocity calculation from loader
- Build succeeds (expected unused function warning)
- Rename CelestialBody/Spacecraft position/velocity to global_*
- Add OrbitalElements struct to both bodies and spacecraft
- Rename old OrbitalElements to OrbitalMetrics (for output/analysis)
- Update all references throughout codebase
- Preserve parent_index in outer structs (not in OrbitalElements)
- Build succeeds with only unused variable warning
Replace 3D billboard rendering with 2D screen-space overlays using
GetWorldToScreen() and DrawTexturePro() for constant visibility.
Changes:
- Rename render functions to *_screen_space suffix
- Use GetWorldToScreen() to transform 3D positions to screen coordinates
- Replace DrawBillboard/DrawBillboardPro() with DrawTexturePro() for 2D rendering
- Use constant pixel sizes (40px spacecraft, 20-60px markers)
- Add off-screen culling to skip rendering outside viewport
- Update render order: 3D scene first, then 2D overlays
- Update documentation to reflect screen-space approach
Benefits:
- Markers remain visible at all zoom levels (constant screen size)
- Simplified sizing with pixel values instead of world-space math
- Better performance with off-screen culling
- Clearer separation: 3D scene vs UI indicators