3 changed files with 77 additions and 695 deletions
@ -0,0 +1,77 @@
|
||||
# Find Dominant Body Bug: Mutual SOI Issue |
||||
|
||||
## Overview |
||||
This documents the remaining issue with `find_dominant_body()` when two similar-mass bodies are positioned within each other's sphere of influence (SOI). |
||||
|
||||
**Date:** January 20, 2026 |
||||
**Status:** Failing Test (Test 4 from test_plan_invalid_parent_assignment.md) |
||||
**Related:** `docs/test_plan_invalid_parent_assignment.md` |
||||
|
||||
--- |
||||
|
||||
## Test Case 4: Mutual SOI - Similar Mass Planets |
||||
|
||||
**Purpose:** Edge case: two Earth-like planets positioned within each other's SOI |
||||
|
||||
**Actual Result:** ❌ **STILL FAILS** (separate issue, not fixed by spacecraft validation) |
||||
|
||||
**Config:** `tests/configs/mutual_soi_close.toml` |
||||
|
||||
**Setup:** |
||||
- PlanetA: mass = 5.972e24 kg, position = {1.496e11, 0, 0} |
||||
- PlanetB: mass = 5.972e24 kg, position = {1.501e11, 0, 0} |
||||
- Separation: 5e8 meters (500 million km) |
||||
- Planet SOI: ~9.25e8 meters (925 million km) |
||||
- **Both planets within each other's SOI** |
||||
|
||||
**Why This Fails:** |
||||
- Both planets start with parent=0 (Sun) |
||||
- Both are within each other's SOI |
||||
- `find_dominant_body()` logic selects closest body within SOI |
||||
- Result: PlanetA selects PlanetB as parent, PlanetB selects PlanetA as parent |
||||
- Config validation passes (both are ≥ parent.radius + body.radius from Sun) |
||||
|
||||
**Key Assertions:** |
||||
```cpp |
||||
// Both should orbit Sun (not each other) |
||||
REQUIRE(sim->bodies[PLANET_A_IDX].parent_index == SUN_IDX); |
||||
REQUIRE(sim->bodies[PLANET_B_IDX].parent_index == SUN_IDX); |
||||
|
||||
// Planets should never have each other as parents |
||||
for (int parent : history.planet_a_parents) { |
||||
REQUIRE(parent != PLANET_B_IDX); |
||||
} |
||||
for (int parent : history.planet_b_parents) { |
||||
REQUIRE(parent != PLANET_A_IDX); |
||||
} |
||||
``` |
||||
|
||||
**Expected Behavior (Option A):** |
||||
- Both planets should continue orbiting Sun |
||||
- Neither should become other's parent |
||||
- This requires future fix to `find_dominant_body()` logic (mass hierarchy check) |
||||
|
||||
--- |
||||
|
||||
## Future Work |
||||
|
||||
### Current Status |
||||
Tests 1-3 from the parent assignment test suite are now passing. Test 4 continues to fail, documenting a separate mutual SOI issue. |
||||
|
||||
### Potential Fixes for Test 4 |
||||
1. Add mass hierarchy check to `find_dominant_body()` |
||||
2. Prevent mutual SOI assignments for similar-mass bodies |
||||
3. Detect and reject invalid configs at load time |
||||
4. Implement proper N-body interaction or restricted 3-body solution |
||||
|
||||
### Enhanced Detection |
||||
- SOI overlap detection at config load time |
||||
- Automatic correction of invalid parent assignments |
||||
- Validation warnings for edge cases |
||||
|
||||
--- |
||||
|
||||
## References |
||||
- `src/simulation.cpp:64-107` - `find_dominant_body()` implementation |
||||
- `tests/configs/mutual_soi_close.toml` - Test configuration for mutual SOI case |
||||
- `tests/test_invalid_parent_assignment.cpp` - Test suite implementation |
||||
@ -1,455 +0,0 @@
|
||||
# Implementation Plan: Config-Based Spacecraft with Impulse Burn |
||||
|
||||
## Overview |
||||
Replace dynamic spacecraft spawning with config-based LEO spacecraft, implement patched conics impulse burn for Hohmann transfer, and add comprehensive test verification. |
||||
|
||||
**Date:** January 18, 2026 |
||||
**Status:** partially implemented |
||||
**Branch:** mission-planning |
||||
|
||||
--- |
||||
|
||||
## Phase 1: Update Configuration File |
||||
|
||||
### Step 1.1: Add spacecraft to `tests/configs/earth_mars_simple.toml` |
||||
|
||||
Add Spacecraft body to config with placeholder position/velocity (set at runtime by `initialize_spacecraft_leo()`). |
||||
|
||||
**Implementation:** See `tests/configs/earth_mars_simple.toml` for full config |
||||
|
||||
**Key parameters:** |
||||
- mass = 1.0 kg (test particle) |
||||
- radius = 1000.0 m |
||||
- parent_index = 1 (Earth) |
||||
- color = magenta (r=1.0, g=0.0, b=0.5) |
||||
- position/velocity: Placeholders (0,0,0) |
||||
|
||||
**TODO**: Future config format should support: |
||||
- Earth-relative position: `{ altitude_km = 200.0 }` |
||||
- Earth-relative orbit: `{ orbit_type = "circular" }` |
||||
- More intuitive spacecraft mission parameters |
||||
|
||||
--- |
||||
|
||||
## Phase 2: Mission Planning Module - New Functions |
||||
|
||||
### Step 2.1: Add function declarations to `src/mission_planning.h` |
||||
|
||||
**Implementation:** See `src/mission_planning.h` |
||||
|
||||
**Functions:** |
||||
- `initialize_spacecraft_leo()` - Initialize spacecraft in circular LEO around parent body |
||||
- `apply_transfer_burn()` - Apply patched conics impulse burn for Hohmann transfer |
||||
- `calculate_phase_angle()` - Calculate current phase angle between two bodies (in degrees) |
||||
|
||||
### Step 2.2: Implement `initialize_spacecraft_leo()` in `src/mission_planning.cpp` |
||||
|
||||
**Implementation:** `src/mission_planning.cpp:20-56` |
||||
|
||||
**Algorithm:** |
||||
- Calculate orbital radius = parent radius + altitude |
||||
- Position spacecraft radially outward from Sun (any angular position acceptable) |
||||
- Calculate circular LEO velocity: v = sqrt(G * M_parent / r) |
||||
- Set prograde orientation (tangential to Earth-Sun line) |
||||
- Set both local and global coordinates correctly |
||||
|
||||
**Key Points:** |
||||
- LEO orbit is circular at 200km altitude (~7,788 m/s) |
||||
- Spacecraft velocity = Earth velocity + LEO velocity |
||||
- Local velocity = LEO velocity only (relative to Earth) |
||||
|
||||
### Step 2.3: Implement `calculate_phase_angle()` in `src/mission_planning.cpp` |
||||
|
||||
**Implementation:** `src/mission_planning.cpp:58-78` |
||||
|
||||
**Algorithm:** |
||||
- Calculate angular positions of departure and arrival bodies relative to Sun |
||||
- Compute phase difference: θ_arrival - θ_departure |
||||
- Normalize to [0°, 360°) range |
||||
- Return phase angle in degrees |
||||
|
||||
### Step 2.4: Implement `apply_transfer_burn()` in `src/mission_planning.cpp` |
||||
|
||||
**Implementation:** `src/mission_planning.cpp:80-116` |
||||
|
||||
**Algorithm (Patched Conics Approach):** |
||||
- Calculate required heliocentric transfer velocity magnitude from params |
||||
- Determine prograde direction (tangential to departure-Sun line) |
||||
- Compute delta-v: Δv = v_transfer - v_current (vector subtraction) |
||||
- Apply impulse to spacecraft velocity |
||||
- Update local velocity relative to departure body |
||||
- Print burn information for debugging |
||||
|
||||
**Note:** Simplified single-impulse approximation. True patched conics would: |
||||
1. Calculate Δv to reach SOI boundary |
||||
2. Calculate velocity at SOI boundary |
||||
3. Add transfer Δv at SOI boundary |
||||
4. Combine into equivalent single impulse |
||||
|
||||
--- |
||||
|
||||
## Phase 3: Comprehensive Test Case |
||||
|
||||
### Step 3.1: Create new test in `tests/test_hohmann_transfer.cpp` |
||||
|
||||
**Implementation:** `tests/test_hohmann_transfer.cpp` - See file for full test |
||||
|
||||
**Test:** "Earth → Mars Hohmann Transfer with LEO Spacecraft" |
||||
|
||||
**Test Structure:** |
||||
1. Load config with 4 bodies (Sun, Earth, Mars, Spacecraft) |
||||
2. Initialize spacecraft in 200km LEO around Earth |
||||
3. Verify LEO orbit stability (parent, position, velocity, energy) |
||||
4. Calculate Hohmann transfer parameters |
||||
5. Wait for Earth-Mars launch window (within 1° tolerance) |
||||
6. Verify phase angle accuracy |
||||
7. Apply impulse burn for transfer |
||||
8. Verify post-burn energy >= 0 (escape trajectory) |
||||
9. Simulate transfer for 110% of expected duration |
||||
10. Track SOI transitions (Earth→Sun→Mars) |
||||
11. Verify final parent and energy conservation (<5% drift) |
||||
12. If Mars SOI entry, verify distance (<2×SOI) |
||||
|
||||
**Key Assertions:** |
||||
- Config loading: 4 bodies loaded, spacecraft present |
||||
- LEO stability: parent=Earth, position <1km error, velocity <10m/s error, energy <0 |
||||
- Launch window: opens in ~94 days, phase error <1° |
||||
- Transfer: post-burn energy >= 0, Earth→Sun SOI transition, energy conservation |
||||
|
||||
--- |
||||
|
||||
## Current Issue Identified |
||||
|
||||
### Problem: Incorrect Delta-V Direction After Multi-Day Wait |
||||
|
||||
**RESOLVED (Jan 20, 2026):** |
||||
|
||||
The FIXME issue below has been addressed by adding config validation in |
||||
`src/config_loader.cpp` that prevents bodies from starting too close to their |
||||
parent bodies. The spacecraft configuration has been corrected to use proper LEO |
||||
altitude (200km) instead of placeholder values. |
||||
|
||||
**Solution Applied:** |
||||
- Config validation: distance ≥ parent.radius + body.radius |
||||
- Spacecraft position corrected to 1.49606571e11 m (Earth + 6,571 km offset) |
||||
- Tests 1-3 in `test_invalid_parent_assignment.cpp` now pass |
||||
|
||||
**FIXME (Original Issue - RESOLVED):** |
||||
While running this simulation config graphically, I noticed that a larger |
||||
problem is in simulation::find_dominant_body(). Earth's parent gets set to |
||||
the satellite's index, which should never happen. |
||||
I think the actual fix should be to have non-massive satellites in a different |
||||
array than celestial bodies, and treat them differently. |
||||
However, we should add more testing for the case that a comet or other massive |
||||
celestial body gets close to another body. |
||||
We do have tests/test_soi_transition.cpp, and tests/configs/soi_transition.toml |
||||
so maybe it's an initialization problem because the new test config starts |
||||
with both bodies in each others SOI? |
||||
|
||||
**Symptom:** |
||||
- Spacecraft enters LEO orbit correctly with negative energy (bound to Earth) |
||||
- Waits 94 days for Earth-Mars launch window |
||||
- During wait period, spacecraft completes ~6.3 LEO orbits |
||||
- LEO orbit phase changes significantly over 94 days |
||||
- After wait, `apply_transfer_burn()` applies delta-v assuming spacecraft is at Earth's current orbital phase |
||||
- Result: Delta-v applied in wrong direction, resulting in retrograde burn |
||||
- Post-burn energy remains negative (spacecraft still bound to Earth) |
||||
|
||||
**Root Cause Analysis:** |
||||
|
||||
The `apply_transfer_burn()` function calculates: |
||||
1. Required heliocentric transfer velocity magnitude: `v_transfer = 32,697 m/s` |
||||
2. Prograde direction based on Earth's current position: `transfer_dir = prograde(t_current)` |
||||
3. Target velocity: `v_target = v_transfer * transfer_dir` |
||||
|
||||
However, after 94 days: |
||||
- Earth has moved to different orbital phase |
||||
- Spacecraft in LEO is still orbiting Earth |
||||
- Spacecraft's current heliocentric velocity includes Earth's motion + LEO motion |
||||
- The calculated transfer direction is based on Earth's instantaneous position, not spacecraft's actual heliocentric velocity vector |
||||
- This results in delta-v that doesn't account for spacecraft's phase in LEO |
||||
|
||||
**What Should Happen:** |
||||
1. Calculate spacecraft's current heliocentric velocity vector: `v_current` |
||||
2. Calculate required heliocentric velocity for transfer orbit: `v_transfer` |
||||
3. Apply delta-v: `Δv = v_transfer - v_current` (vector subtraction, not magnitude-based) |
||||
|
||||
**What Currently Happens:** |
||||
1. Assumes spacecraft starts at Earth's orbital position (ignores LEO phase) |
||||
2. Calculates transfer direction based on Earth's current prograde vector |
||||
3. Applies magnitude-based delta-v without considering spacecraft's actual velocity direction |
||||
4. Results in incorrect burn direction |
||||
|
||||
### Solution Required (Pending) |
||||
|
||||
NOTE: The immediate bug of Earth becoming a child of spacecraft has been |
||||
resolved by config validation. The delta-v calculation issue below remains for |
||||
future implementation. |
||||
|
||||
Modify `apply_transfer_burn()` to: |
||||
|
||||
1. **Calculate spacecraft's actual heliocentric velocity:** |
||||
```cpp |
||||
Vec3 v_current_helio = spacecraft->velocity; // Already in global frame |
||||
``` |
||||
|
||||
2. **Calculate required heliocentric transfer velocity:** |
||||
```cpp |
||||
double v_transfer_mag = params->departure_velocity; // ~32,697 m/s |
||||
|
||||
// Direction: prograde to Sun (same as Earth's orbital direction) |
||||
Vec3 sun_to_earth = vec3_sub(departure->position, sun->position); |
||||
Vec3 sun_to_earth_norm = vec3_normalize(sun_to_earth); |
||||
Vec3 transfer_dir = (Vec3){-sun_to_earth_norm.y, sun_to_earth_norm.x, 0.0}; |
||||
Vec3 v_transfer_helio = vec3_scale(transfer_dir, v_transfer_mag); |
||||
``` |
||||
|
||||
3. **Calculate delta-v as vector difference:** |
||||
```cpp |
||||
Vec3 delta_v = vec3_sub(v_transfer_helio, v_current_helio); |
||||
``` |
||||
|
||||
4. **Apply impulse:** |
||||
```cpp |
||||
spacecraft->velocity = vec3_add(spacecraft->velocity, delta_v); |
||||
spacecraft->local_velocity = vec3_sub(spacecraft->velocity, departure->velocity); |
||||
``` |
||||
|
||||
**This approach:** |
||||
- Accounts for spacecraft's actual heliocentric velocity (includes LEO phase) |
||||
- Uses vector subtraction instead of magnitude-based calculation |
||||
- Produces correct delta-v direction regardless of LEO phase |
||||
- Should result in positive post-burn energy (escape trajectory) |
||||
|
||||
--- |
||||
|
||||
## Potential Issues and Mitigation |
||||
|
||||
### Issue 1: LEO Orbit Position Sensitivity |
||||
Spacecraft LEO phase may affect optimal launch window timing. |
||||
|
||||
**Mitigation**: Test shows we wait for Earth-Mars phase angle, not spacecraft-LEO phase. This should be acceptable. |
||||
|
||||
### Issue 2: Impulse Burn Accuracy |
||||
Single-impulse approximation may not match true patched conics trajectory. |
||||
|
||||
**Mitigation**: Initial test focuses on Earth→Sun transition and energy conservation. If needed, can refine to two-impulse burn in future. |
||||
|
||||
### Issue 3: Mars SOI Entry |
||||
Spacecraft may not enter Mars SOI due to: |
||||
- Phase angle tolerance (1°) |
||||
- Transfer time approximation |
||||
- Impulse burn simplifications |
||||
|
||||
**Mitigation**: Test includes explicit INFO messages and requires only Earth→Sun transition, not Mars arrival. |
||||
|
||||
--- |
||||
|
||||
## Timeline Estimate |
||||
|
||||
- Phase 0 (Git workflow): 10 minutes |
||||
- Phase 1 (Config update): 5 minutes |
||||
- Phase 2 (Mission planning): 1-2 hours |
||||
- Phase 3 (Comprehensive test): 30 minutes |
||||
- Phase 4 (Build and test): 20 minutes |
||||
- Phase 5 (Cleanup): 20 minutes |
||||
|
||||
**Total**: 2-3 hours |
||||
|
||||
--- |
||||
|
||||
## Test Configuration Reference |
||||
|
||||
### earth_mars_simple.toml |
||||
**Implementation:** `tests/configs/earth_mars_simple.toml` |
||||
|
||||
**Bodies:** |
||||
- Sun (index 0): Root body, 1.989e30 kg |
||||
- Earth (index 1): 5.972e24 kg, 1.496e11 m from Sun |
||||
- Mars (index 2): 6.39e23 kg, 2.279e11 m from Sun |
||||
- Spacecraft (index 3): 1.0 kg, parent=Earth (position specified in config) |
||||
|
||||
**Spacecraft parameters:** |
||||
- mass = 1.0 kg |
||||
- radius = 1000.0 m |
||||
- parent_index = 1 (Earth) |
||||
- color = magenta (r=1.0, g=0.0, b=0.5) |
||||
- position: 1.49606571e11 m (Earth position + 6,571,000 m LEO offset) |
||||
- velocity: Calculated by `initialize_bodies()` using `semi_major_axis = 6.571e6` |
||||
|
||||
--- |
||||
|
||||
## Future Work (Post-Implementation) |
||||
|
||||
### Immediate Next Steps |
||||
|
||||
**TODO: Spacecraft Config Pattern Changes** |
||||
|
||||
Spacecraft now requires full position specification in config files: |
||||
- Removed placeholder pattern that expected `initialize_spacecraft_leo()` to set position at runtime |
||||
- Position must be: parent position + orbital altitude offset |
||||
- Example: Earth LEO at 200km = Earth radius (6.371e6 m) + 200,000 m = 6.571e6 m offset |
||||
- Velocity is calculated by `initialize_bodies()` using `semi_major_axis` parameter |
||||
|
||||
**Impact on `initialize_spacecraft_leo()`:** |
||||
- Currently sets position relative to parent |
||||
- May need to be deprecated or updated to validate/override config-based positions |
||||
- Consider keeping function for dynamic scenarios (e.g., multi-burn missions) |
||||
|
||||
#### 1. Config Format Improvements |
||||
- Support Earth-relative position specification (e.g., `{ altitude_km = 200.0 }`) |
||||
- Support Earth-relative orbit specification (e.g., `{ orbit_type = "circular" }`) |
||||
- More intuitive spacecraft mission parameters in TOML config |
||||
- Support multiple spacecraft in single config file |
||||
|
||||
#### 2. Improved Patched Conics Implementation |
||||
- Calculate Δv to reach SOI boundary (escape trajectory) |
||||
- Calculate velocity at SOI boundary |
||||
- Add transfer Δv at SOI boundary |
||||
- Combine into equivalent single impulse |
||||
- Test accuracy of two-impulse vs single-impulse approach |
||||
|
||||
#### 3. Inclination Support |
||||
- Extend to 3D transfers |
||||
- Need 3D angular position calculations |
||||
- Longitude of ascending node, inclination, argument of periapsis |
||||
- Phase angle calculations in 3D |
||||
- Out-of-plane maneuver calculations |
||||
|
||||
#### 4. Capture Burns |
||||
- Simulate retrograde burns for orbital capture at destination |
||||
- Calculate Δv needed for circularization |
||||
- Support parking orbits at arrival body |
||||
- Validate Mars capture burns (~1.4 km/s for Mars) |
||||
|
||||
#### 5. Adaptive Timestepping |
||||
|
||||
**Problem:** Fixed 60s timestep is: |
||||
- Too coarse for fast orbital phases (moon capture, close approaches) |
||||
- Too slow for deep-space phases (interplanetary transfers) |
||||
|
||||
**Solution:** Adaptive timestep based on orbital period |
||||
|
||||
**Implementation:** |
||||
```cpp |
||||
double calculate_adaptive_timestep(CelestialBody* body, CelestialBody* parent) { |
||||
if (parent == NULL || body->semi_major_axis <= 0.0) { |
||||
return 60.0; // Default timestep |
||||
} |
||||
|
||||
// Calculate orbital period using Kepler's third law |
||||
double T = 2.0 * M_PI * sqrt(pow(body->semi_major_axis, 3) / (G * parent->mass)); |
||||
|
||||
// Use 1/1000 of orbital period as timestep |
||||
double adaptive_dt = T / 1000.0; |
||||
|
||||
// Clamp to reasonable bounds |
||||
adaptive_dt = fmax(adaptive_dt, 10.0); // Minimum 10s |
||||
adaptive_dt = fmin(adaptive_dt, 600.0); // Maximum 600s |
||||
|
||||
return adaptive_dt; |
||||
} |
||||
``` |
||||
|
||||
**Changes required:** |
||||
- Add per-body timesteps to `SimulationState` |
||||
- Update `update_simulation()` to use adaptive timesteps |
||||
- Add synchronization mechanism for multiple timesteps |
||||
|
||||
**Expected outcome:** |
||||
- Better accuracy for fast orbits (moon capture) |
||||
- Faster simulation for deep-space phases |
||||
- Energy conserved across SOI transitions |
||||
|
||||
**Tests:** |
||||
- Verify energy drift with adaptive timesteps |
||||
- Verify orbital period accuracy with adaptive timesteps |
||||
- Test stability across SOI transitions |
||||
|
||||
### Visualization Features |
||||
|
||||
#### 6. Mission GUI |
||||
- Interactive departure window visualization |
||||
- Show current phase angle vs. required phase angle |
||||
- Countdown to launch window |
||||
- Transfer trajectory preview (predicted path) |
||||
- Delta-v budget display |
||||
|
||||
#### 7. Multiple Burns Support |
||||
- Mid-course corrections |
||||
- Gravity assist maneuvers |
||||
- Powered flybys |
||||
- Multi-stage missions |
||||
|
||||
#### 8. SOI Visualization |
||||
- Render SOI boundaries as wireframe spheres |
||||
- Color-coded by mass |
||||
- Toggle with keyboard shortcut |
||||
- Show SOI transitions in real-time |
||||
|
||||
### Advanced Features |
||||
|
||||
#### 9. Mission Planner |
||||
- Complete mission design tool |
||||
- Multi-leg missions (Earth→Mars→Phobos) |
||||
- Optimization algorithms (minimum Δv, minimum time) |
||||
- Launch date search across windows |
||||
- Mission timeline visualization |
||||
|
||||
#### 10. Real Ephemeris Integration |
||||
- Use actual planetary positions (JPL Horizons API) |
||||
- Date-based initialization |
||||
- Real mission planning with actual ephemeris data |
||||
- Compare simulation to historical missions |
||||
|
||||
#### 11. Enhanced Trajectory Analysis |
||||
- Lambert solver for general transfers |
||||
- Not just Hohmann transfers |
||||
- Arbitrary departure/arrival positions and times |
||||
- Non-planar transfers |
||||
|
||||
--- |
||||
|
||||
## Notes |
||||
|
||||
### Coordinate System |
||||
- All calculations assume planar motion (z = 0) for initial implementation |
||||
- Angular positions measured in XY plane |
||||
- Future work: Extend to 3D with inclination |
||||
|
||||
### Timekeeping |
||||
- Simulation time in seconds, conversions to days for display |
||||
- Fast-forward uses 1-day steps for efficiency during launch window wait |
||||
- Timestep remains 60s during fast-forward |
||||
|
||||
### Mass Strategy |
||||
- Spacecraft mass = 1.0 kg (negligible but non-zero) |
||||
- Physics engine handles test particles correctly (mass cancels in acceleration) |
||||
- No N-body perturbations from spacecraft on planetary bodies |
||||
|
||||
### Validation Strategy |
||||
- Compare against NASA reference missions (Viking, Curiosity, Perseverance) |
||||
- Energy conservation tracking during transfer |
||||
- Transfer time accuracy (±10% tolerance) |
||||
- SOI transition verification (Earth→Sun→Mars) |
||||
|
||||
### Testing Approach |
||||
- Unit tests for each function (formulas, calculations) |
||||
- Integration tests for full missions (LEO initialization, impulse burn, transfer) |
||||
- Regression tests against expected Hohmann transfer parameters |
||||
|
||||
### LEO Orbit Considerations |
||||
- LEO orbit at 200 km altitude (r = 6.571×10⁶ m) |
||||
- LEO velocity: ~7,788 m/s at 200 km |
||||
- LEO period: ~88.5 minutes |
||||
- Spacecraft LEO phase changes significantly during multi-day wait periods |
||||
- Transfer burn must account for spacecraft's actual heliocentric velocity (not just Earth's) |
||||
|
||||
--- |
||||
|
||||
## References |
||||
|
||||
- `docs/implementation_plan.md` - Overall system architecture |
||||
- NASA Technical Memorandum "Hohmann Transfer Calculations" |
||||
- Orbital Mechanics for Engineering Students (Curtis) |
||||
- Fundamentals of Astrodynamics (Bate, Mueller, White) |
||||
@ -1,240 +0,0 @@
|
||||
# Test Plan: Invalid Parent Assignment Bug |
||||
|
||||
## Overview |
||||
Comprehensive test suite to capture and validate parent-child relationship bugs in orbital mechanics simulation. |
||||
|
||||
**Date:** January 20, 2026 |
||||
**Status:** Complete (Tests 1-3 resolved) |
||||
**Related:** `docs/mission_planning.md` FIXME section |
||||
|
||||
--- |
||||
|
||||
## Bug Diagnosis |
||||
|
||||
### Root Cause Identified |
||||
|
||||
**The Bug (Fixed):** In `tests/configs/earth_mars_simple.toml`, spacecraft had placeholder values: |
||||
- `position = {1.496e11, 0, 0}` (IDENTICAL to Earth) |
||||
- `velocity = {0, 0, 0}` (zero velocity) |
||||
- `SOI = ~17.3 meters` (calculated from mass=1kg) |
||||
|
||||
**What Happened:** |
||||
1. Config loaded with spacecraft at Earth's exact position |
||||
2. During `find_dominant_body(1)` for Earth: |
||||
- Checks all bodies |
||||
- Finds spacecraft at distance = 0 |
||||
- `0 < 17.3` is TRUE |
||||
- Sets Earth's parent to spacecraft! |
||||
|
||||
**Why Test Failed But GUI Failed Worse:** |
||||
- Test calls `initialize_spacecraft_leo()` → spacecraft moved to proper LEO orbit (200km from Earth) |
||||
- GUI never called this → placeholder values caused immediate bug |
||||
|
||||
### Solution Implemented |
||||
|
||||
**Config Validation in `src/config_loader.cpp`:** |
||||
- Added validation loop after parsing bodies, before `initialize_bodies()` |
||||
- Validates that distance between body and parent ≥ parent.radius + body.radius |
||||
- Provides clear error message with actual and required distances |
||||
- Prevents loading invalid configs with bodies too close to their parents |
||||
|
||||
**Config Fix in `tests/configs/earth_mars_simple.toml`:** |
||||
- Changed spacecraft position from `1.496e11` (same as Earth) to `1.49606571e11` |
||||
- This places spacecraft at proper LEO altitude: Earth position + 6,571,000 m |
||||
- 6,571 km = Earth radius (6,371 km) + 200 km altitude |
||||
|
||||
--- |
||||
|
||||
## Test Cases |
||||
|
||||
### Test Case 1: Earth→Spacecraft Parent Switch |
||||
|
||||
**Purpose:** Directly detect when Earth's parent becomes spacecraft (the exact bug from FIXME) |
||||
|
||||
**Actual Result:** ✅ **PASSES** |
||||
|
||||
**How It Was Fixed:** |
||||
- Config validation now rejects bodies starting at parent's position |
||||
- Spacecraft properly positioned at LEO altitude (6,571 km from Earth center) |
||||
- `find_dominant_body()` never finds spacecraft at distance=0 |
||||
- Earth's parent remains Sun throughout simulation |
||||
|
||||
**Config:** `tests/configs/earth_mars_simple.toml` |
||||
|
||||
**Key Assertion:** |
||||
```cpp |
||||
REQUIRE(sim->bodies[EARTH_IDX].parent_index != SPACECRAFT_IDX); |
||||
``` |
||||
|
||||
--- |
||||
|
||||
### Test Case 2: Mass Hierarchy Validation |
||||
|
||||
**Purpose:** Validate that massive bodies never become children of small bodies |
||||
|
||||
**Actual Result:** ✅ **PASSES** |
||||
|
||||
**How It Was Fixed:** |
||||
- Config validation ensures proper parent-child distance |
||||
- Mass hierarchy preserved throughout simulation |
||||
- Earth never becomes child of spacecraft or other bodies |
||||
|
||||
**Config:** `tests/configs/earth_mars_simple.toml` |
||||
|
||||
**Key Assertions:** |
||||
```cpp |
||||
// Parent must be more massive |
||||
REQUIRE(mass_ratio >= 1.0); |
||||
|
||||
// For planets: parent should be significantly more massive |
||||
if (not spacecraft) { |
||||
REQUIRE(mass_ratio >= 1000.0); |
||||
} |
||||
|
||||
// Massive bodies should never have small bodies as parents |
||||
if (child_mass > 1e20) { // Planet-scale |
||||
REQUIRE(parent_mass > child_mass); |
||||
} |
||||
``` |
||||
|
||||
--- |
||||
|
||||
### Test Case 3: Config Placeholder Validation |
||||
|
||||
**Purpose:** Detect invalid config initialization (bodies starting too close together) |
||||
|
||||
**Actual Result:** ✅ **PASSES** |
||||
|
||||
**How It Was Fixed:** |
||||
- Test updated to use radius-based validation (matches config_loader logic) |
||||
- Spacecraft now at proper LEO position: 6,571,000 m from Earth center |
||||
- Validation: distance (6,571,000 m) ≥ Earth.radius + spacecraft.radius (6,372,000 m) |
||||
- Config validation catches any bodies positioned within parent's radius |
||||
|
||||
**Config:** `tests/configs/earth_mars_simple.toml` |
||||
|
||||
**Key Assertion:** |
||||
```cpp |
||||
double min_distance = sim->bodies[EARTH_IDX].radius + sim->bodies[SPACECRAFT_IDX].radius; |
||||
REQUIRE(distance >= min_distance); // parent.radius + body.radius |
||||
``` |
||||
|
||||
--- |
||||
|
||||
### Test Case 4: Mutual SOI - Similar Mass Planets |
||||
|
||||
**Purpose:** Edge case: two Earth-like planets positioned within each other's SOI |
||||
|
||||
**Actual Result:** ❌ **STILL FAILS** (separate issue, not fixed by spacecraft validation) |
||||
|
||||
**Config:** `tests/configs/mutual_soi_close.toml` |
||||
|
||||
**Setup:** |
||||
- PlanetA: mass = 5.972e24 kg, position = {1.496e11, 0, 0} |
||||
- PlanetB: mass = 5.972e24 kg, position = {1.501e11, 0, 0} |
||||
- Separation: 5e8 meters (500 million km) |
||||
- Planet SOI: ~9.25e8 meters (925 million km) |
||||
- **Both planets within each other's SOI** |
||||
|
||||
**Why This Fails:** |
||||
- Both planets start with parent=0 (Sun) |
||||
- Both are within each other's SOI |
||||
- `find_dominant_body()` logic selects closest body within SOI |
||||
- Result: PlanetA selects PlanetB as parent, PlanetB selects PlanetA as parent |
||||
- Config validation passes (both are ≥ parent.radius + body.radius from Sun) |
||||
|
||||
**Key Assertions:** |
||||
```cpp |
||||
// Both should orbit Sun (not each other) |
||||
REQUIRE(sim->bodies[PLANET_A_IDX].parent_index == SUN_IDX); |
||||
REQUIRE(sim->bodies[PLANET_B_IDX].parent_index == SUN_IDX); |
||||
|
||||
// Planets should never have each other as parents |
||||
for (int parent : history.planet_a_parents) { |
||||
REQUIRE(parent != PLANET_B_IDX); |
||||
} |
||||
for (int parent : history.planet_b_parents) { |
||||
REQUIRE(parent != PLANET_A_IDX); |
||||
} |
||||
``` |
||||
|
||||
**Expected Behavior (Option A):** |
||||
- Both planets should continue orbiting Sun |
||||
- Neither should become other's parent |
||||
- This requires future fix to `find_dominant_body()` logic (mass hierarchy check) |
||||
|
||||
--- |
||||
|
||||
## Test Matrix |
||||
|
||||
| Test Case | Config | Tests | Expected (Before Fix) | **Actual Result** | |
||||
|-----------|--------|-------|----------------------|--------------------------------| |
||||
| **1. Earth→Spacecraft** | earth_mars_simple.toml | Parent assignment | **FAIL** | **✅ PASS** | |
||||
| **2. Mass Hierarchy** | earth_mars_simple.toml | Mass ratios | **FAIL** | **✅ PASS** | |
||||
| **3. Config Validation** | earth_mars_simple.toml | Separation distance | **FAIL** | **✅ PASS** | |
||||
| **4. Mutual SOI** | mutual_soi_close.toml | Edge case behavior | **FAIL** | **❌ FAIL** (separate issue) | |
||||
|
||||
--- |
||||
|
||||
## Implementation |
||||
|
||||
### Test File |
||||
`tests/test_invalid_parent_assignment.cpp` |
||||
|
||||
### Test Config Files |
||||
- `tests/configs/earth_mars_simple.toml` (existing) |
||||
- `tests/configs/mutual_soi_close.toml` (new) |
||||
|
||||
### Build Integration |
||||
Add to CMakeLists.txt or Makefile test targets |
||||
|
||||
--- |
||||
|
||||
## Implementation Summary |
||||
|
||||
**Solution Applied (Jan 20, 2026):** |
||||
1. **Config validation in `src/config_loader.cpp`**: |
||||
- Validates parent-child distances before initialization |
||||
- Requires distance ≥ parent.radius + body.radius |
||||
- Prevents loading invalid configs |
||||
|
||||
2. **Config fix in `tests/configs/earth_mars_simple.toml`**: |
||||
- Spacecraft position corrected to proper LEO altitude |
||||
- Position: 1.49606571e11 m (Earth position + 6,571,000 m offset) |
||||
- Velocity calculated by `initialize_bodies()` using `semi_major_axis = 6.571e6` |
||||
|
||||
3. **Test updates in `tests/test_invalid_parent_assignment.cpp`**: |
||||
- Test 3 updated to use radius-based validation |
||||
- Matches config_loader validation logic |
||||
|
||||
--- |
||||
|
||||
## Future Work |
||||
|
||||
### Current Status |
||||
Tests 1-3 are now passing. Test 4 continues to fail, documenting a separate mutual SOI issue. |
||||
|
||||
### Potential Fixes for Test 4 |
||||
1. Add mass hierarchy check to `find_dominant_body()` |
||||
2. Prevent mutual SOI assignments for similar-mass bodies |
||||
3. Detect and reject invalid configs at load time |
||||
4. Implement proper N-body interaction or restricted 3-body solution |
||||
|
||||
### Enhanced Detection |
||||
- SOI overlap detection at config load time |
||||
- Automatic correction of invalid parent assignments |
||||
- Validation warnings for edge cases |
||||
|
||||
--- |
||||
|
||||
## References |
||||
- `docs/mission_planning.md:125-135` - FIXME section describing bug (now resolved) |
||||
- `src/config_loader.cpp:138-157` - Config validation implementation |
||||
- `tests/configs/earth_mars_simple.toml` - Corrected spacecraft position (LEO altitude) |
||||
- `src/simulation.cpp:64-107` - `find_dominant_body()` implementation |
||||
- `src/simulation.cpp:212-233` - `initialize_bodies()` implementation |
||||
- `tests/test_invalid_parent_assignment.cpp` - Test suite implementation |
||||
|
||||
## Commits |
||||
- `0239cc1` - Add test suite for invalid parent assignment bugs |
||||
- `899fa3b` - Add config validation for parent-child distances |
||||
Loading…
Reference in new issue