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