|
|
|
|
@ -1,688 +0,0 @@
|
|
|
|
|
# Patched Conics and SOI Transition Implementation Plan |
|
|
|
|
|
|
|
|
|
**Date:** January 14, 2026 |
|
|
|
|
**Status:** Implementation Ready (Decisions Made) |
|
|
|
|
**Branch:** patched-conics |
|
|
|
|
|
|
|
|
|
**Decisions Made:** |
|
|
|
|
1. ✅ Hysteresis: Adaptive approach (Option B) |
|
|
|
|
2. ✅ Integration timing: Current approach (Option A), TODO for future |
|
|
|
|
3. ✅ Test priorities: Create all 3 test cases first |
|
|
|
|
4. ✅ Adaptive timesteps: Deferred to later work (Phase 5) |
|
|
|
|
|
|
|
|
|
**Next Step:** Begin Phase 1 - Fix Root Body Transitions |
|
|
|
|
|
|
|
|
|
## Overview |
|
|
|
|
|
|
|
|
|
This plan implements support for patched conics trajectory simulation, enabling satellites to transition between multiple spheres of influence (SOI) in complex orbital scenarios: |
|
|
|
|
- Planet → Star → Planet transfers |
|
|
|
|
- Star → Planet → Moon rendezvous |
|
|
|
|
- Multi-leg interplanetary missions |
|
|
|
|
|
|
|
|
|
## Current State Analysis |
|
|
|
|
|
|
|
|
|
### ✅ What's Already Working |
|
|
|
|
|
|
|
|
|
1. **SOI Transitions Are Already Implemented** |
|
|
|
|
- Lines 103-121 in `simulation.cpp` show coordinate transformation logic |
|
|
|
|
- Converts local→global using old parent, then global→local using new parent |
|
|
|
|
- This is essentially Phase 3 implementation from hierarchical_frames_plan.md |
|
|
|
|
|
|
|
|
|
2. **Local Frame Integration** (Phase 2) ✅ |
|
|
|
|
- All bodies integrate in local coordinates |
|
|
|
|
- Global coordinates computed after each timestep |
|
|
|
|
- Improved numerical precision for nested orbits |
|
|
|
|
|
|
|
|
|
3. **Parent-First Update Order** (Phase 4) ✅ |
|
|
|
|
- Root bodies skipped in loop (fixed at origin) |
|
|
|
|
- Child bodies integrate using parent coordinates |
|
|
|
|
- Hierarchical update order implemented |
|
|
|
|
|
|
|
|
|
### 🔴 Critical Issues for Patched Conics |
|
|
|
|
|
|
|
|
|
**Issue 1: Cannot Transition to Root Bodies** |
|
|
|
|
```cpp |
|
|
|
|
if (new_parent != body->parent_index && new_parent != -1) { |
|
|
|
|
// ... transition logic |
|
|
|
|
} |
|
|
|
|
``` |
|
|
|
|
The `new_parent != -1` condition prevents switching to Sun (parent_index = -1). This breaks the scenario: Planet→Sun→Moon rendezvous is impossible. |
|
|
|
|
|
|
|
|
|
**Issue 2: Hysteresis Barrier** |
|
|
|
|
The 0.5x hysteresis factor in `find_dominant_body()` (line 71) creates one-way barriers: |
|
|
|
|
- Planet→Sun: Possible (easy entry) |
|
|
|
|
- Sun→Planet: **Impossible** (can't exit due to hysteresis) |
|
|
|
|
|
|
|
|
|
**Issue 3: Integration After Transition** |
|
|
|
|
Transition happens before integration in the same timestep, using coordinates from the end of the previous timestep. This may cause velocity discontinuities. |
|
|
|
|
|
|
|
|
|
### ⚠️ Potential Issues |
|
|
|
|
|
|
|
|
|
**Issue 4: Numerical Precision** |
|
|
|
|
Satellites crossing between star/planet/moon scales will see position magnitude changes of 10⁸ to 10¹¹ meters, potentially losing precision. |
|
|
|
|
|
|
|
|
|
**Issue 5: Fixed Timestep** |
|
|
|
|
60s timestep may be too coarse for fast orbital phases (moon capture) and too slow for deep-space phases. |
|
|
|
|
|
|
|
|
|
## Implementation Phases |
|
|
|
|
|
|
|
|
|
### Phase 1: Fix Root Body Transitions (Critical) |
|
|
|
|
|
|
|
|
|
**Goal:** Allow satellites to switch to/from Sun (parent_index = -1) |
|
|
|
|
|
|
|
|
|
**Changes:** |
|
|
|
|
1. Remove `new_parent != -1` check in `simulation.cpp` line 104 |
|
|
|
|
2. Add special handling for root body transitions: |
|
|
|
|
```cpp |
|
|
|
|
if (new_parent != body->parent_index) { |
|
|
|
|
// Convert local → global using old parent |
|
|
|
|
if (body->parent_index >= 0) { |
|
|
|
|
// old_parent is a real body |
|
|
|
|
CelestialBody* old_parent = &sim->bodies[body->parent_index]; |
|
|
|
|
body->position = vec3_add(body->local_position, old_parent->position); |
|
|
|
|
body->velocity = vec3_add(body->local_velocity, old_parent->velocity); |
|
|
|
|
} else { |
|
|
|
|
// old_parent is root (Sun): local = global |
|
|
|
|
body->position = body->local_position; |
|
|
|
|
body->velocity = body->local_velocity; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
body->parent_index = new_parent; |
|
|
|
|
|
|
|
|
|
// Convert global → local using new parent |
|
|
|
|
if (new_parent >= 0) { |
|
|
|
|
// new_parent is a real body |
|
|
|
|
CelestialBody* new_parent_body = &sim->bodies[new_parent]; |
|
|
|
|
body->local_position = vec3_sub(body->position, new_parent_body->position); |
|
|
|
|
body->local_velocity = vec3_sub(body->velocity, new_parent_body->velocity); |
|
|
|
|
} else { |
|
|
|
|
// new_parent is root (Sun): global = local |
|
|
|
|
body->local_position = body->position; |
|
|
|
|
body->local_velocity = body->velocity; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
3. **Update `find_dominant_body()`** to properly handle -1 returns |
|
|
|
|
|
|
|
|
|
**Files to modify:** |
|
|
|
|
- `src/simulation.cpp` (lines 103-121) |
|
|
|
|
- `src/simulation.h` (no changes needed) |
|
|
|
|
|
|
|
|
|
**Estimated complexity:** Low |
|
|
|
|
**Risk:** Medium (affects core transition logic) |
|
|
|
|
|
|
|
|
|
**Expected outcome:** |
|
|
|
|
- ✅ Satellites can transition to/from Sun |
|
|
|
|
- ✅ Enables Planet→Sun→Planet transfers |
|
|
|
|
- ✅ Enables Star→Planet→Moon rendezvous |
|
|
|
|
|
|
|
|
|
**Tests to add:** |
|
|
|
|
- Test satellite transitioning from Earth to Sun |
|
|
|
|
- Test satellite transitioning from Sun to Mars |
|
|
|
|
- Test full round-trip: Earth→Sun→Earth |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
### Phase 2: Adaptive Hysteresis (Critical for Round-Trips) |
|
|
|
|
|
|
|
|
|
**Current Problem:** |
|
|
|
|
The 0.5x hysteresis factor prevents oscillation but creates one-way barriers: |
|
|
|
|
```cpp |
|
|
|
|
if (distance < dist_to_current * 0.5) { |
|
|
|
|
dominant = i; |
|
|
|
|
} |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
**Decision:** Adaptive Hysteresis (Option B) - **DECIDED ✅** |
|
|
|
|
|
|
|
|
|
- Keep hysteresis but only apply when already in SOI |
|
|
|
|
- Allow free switching when outside current SOI |
|
|
|
|
- **Pros:** Prevents oscillation while enabling round-trips |
|
|
|
|
- **Cons:** More complex logic |
|
|
|
|
|
|
|
|
|
**Option B Implementation:** |
|
|
|
|
```cpp |
|
|
|
|
if (can_switch && i != dominant) { |
|
|
|
|
if (dominant == -1) { |
|
|
|
|
dominant = i; |
|
|
|
|
} else { |
|
|
|
|
CelestialBody* current_parent = &sim->bodies[dominant]; |
|
|
|
|
double dist_to_current = vec3_distance(body->position, current_parent->position); |
|
|
|
|
|
|
|
|
|
if (outside_current_soi) { |
|
|
|
|
// Outside current SOI: switch to closest body (no hysteresis) |
|
|
|
|
if (distance < dist_to_current) { |
|
|
|
|
dominant = i; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
// Inside current SOI: apply hysteresis to prevent oscillations |
|
|
|
|
if (distance < dist_to_current * 0.5) { |
|
|
|
|
dominant = i; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
**Files to modify:** |
|
|
|
|
- `src/simulation.cpp` (line 70-75) |
|
|
|
|
|
|
|
|
|
**Estimated complexity:** Low-Medium |
|
|
|
|
**Risk:** Medium (may affect transition behavior) |
|
|
|
|
|
|
|
|
|
**Expected outcome:** |
|
|
|
|
- ✅ Enables round-trip transitions (Earth→Sun→Earth) |
|
|
|
|
- ✅ Maintains stability by preventing oscillation when inside SOI |
|
|
|
|
- ✅ Allows free switching when outside current SOI |
|
|
|
|
|
|
|
|
|
**Tests to add:** |
|
|
|
|
- Validate Satellite→Planet→Satellite round-trip |
|
|
|
|
- Validate Earth→Sun→Mars→Sun→Earth full round-trip |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
### Phase 3: Refactor Transition to Separate Function |
|
|
|
|
|
|
|
|
|
**Goal:** Cleaner code, easier to test, better separation of concerns |
|
|
|
|
|
|
|
|
|
**Current state:** Transition logic is inline in `update_simulation()` (lines 105-120) |
|
|
|
|
|
|
|
|
|
**Proposed refactoring:** |
|
|
|
|
|
|
|
|
|
**Add to `simulation.h`:** |
|
|
|
|
```cpp |
|
|
|
|
void transition_to_new_parent(SimulationState* sim, CelestialBody* body, |
|
|
|
|
int old_parent_idx, int new_parent_idx); |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
**Add to `simulation.cpp`:** |
|
|
|
|
```cpp |
|
|
|
|
void transition_to_new_parent(SimulationState* sim, CelestialBody* body, |
|
|
|
|
int old_parent_idx, int new_parent_idx) { |
|
|
|
|
// Current state is in old parent's frame |
|
|
|
|
Vec3 old_local_pos = body->local_position; |
|
|
|
|
Vec3 old_local_vel = body->local_velocity; |
|
|
|
|
|
|
|
|
|
// Convert to global coordinates using old parent |
|
|
|
|
if (old_parent_idx >= 0 && old_parent_idx < sim->body_count) { |
|
|
|
|
CelestialBody* old_parent = &sim->bodies[old_parent_idx]; |
|
|
|
|
body->position = vec3_add(old_local_pos, old_parent->position); |
|
|
|
|
body->velocity = vec3_add(old_local_vel, old_parent->velocity); |
|
|
|
|
} else { |
|
|
|
|
// old_parent is root (Sun): local = global |
|
|
|
|
body->position = old_local_pos; |
|
|
|
|
body->velocity = old_local_vel; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Update parent index |
|
|
|
|
body->parent_index = new_parent_idx; |
|
|
|
|
|
|
|
|
|
// Convert to local coordinates using new parent |
|
|
|
|
if (new_parent_idx >= 0 && new_parent_idx < sim->body_count) { |
|
|
|
|
CelestialBody* new_parent_body = &sim->bodies[new_parent_idx]; |
|
|
|
|
body->local_position = vec3_sub(body->position, new_parent_body->position); |
|
|
|
|
body->local_velocity = vec3_sub(body->velocity, new_parent_body->velocity); |
|
|
|
|
} else { |
|
|
|
|
// new_parent is root (Sun): global = local |
|
|
|
|
body->local_position = body->position; |
|
|
|
|
body->local_velocity = body->velocity; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
**Update `update_simulation()`:** |
|
|
|
|
```cpp |
|
|
|
|
int new_parent = find_dominant_body(sim, i); |
|
|
|
|
if (new_parent != body->parent_index) { |
|
|
|
|
transition_to_new_parent(sim, body, body->parent_index, new_parent); |
|
|
|
|
} |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
**Files to modify:** |
|
|
|
|
- `src/simulation.h` (add function declaration) |
|
|
|
|
- `src/simulation.cpp` (extract and refactor transition logic) |
|
|
|
|
|
|
|
|
|
**Estimated complexity:** Low |
|
|
|
|
**Risk:** Low (pure refactor, no behavior change) |
|
|
|
|
|
|
|
|
|
**Expected outcome:** |
|
|
|
|
- ✅ Cleaner code with better separation of concerns |
|
|
|
|
- ✅ Easier to unit test transition logic |
|
|
|
|
- ✅ Follows Phase 3 plan from hierarchical_frames_plan.md |
|
|
|
|
|
|
|
|
|
**Tests to add:** |
|
|
|
|
- Unit tests for `transition_to_new_parent()` with all scenarios: |
|
|
|
|
- Body→Body transition |
|
|
|
|
- Body→Root transition |
|
|
|
|
- Root→Body transition |
|
|
|
|
- Root→Root transition (edge case) |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
### Phase 4: Multi-Body Transition Test Configs |
|
|
|
|
|
|
|
|
|
**Goal:** Create realistic test scenarios for patched conics |
|
|
|
|
|
|
|
|
|
**Decision:** Create all three test cases first, expect failures for unimplemented features - **DECIDED ✅** |
|
|
|
|
|
|
|
|
|
**Implementation approach:** |
|
|
|
|
1. Create all three test configurations |
|
|
|
|
2. Run tests to establish baseline failures |
|
|
|
|
3. Tests validate features as they're implemented |
|
|
|
|
4. Comprehensive coverage from the start |
|
|
|
|
|
|
|
|
|
#### Test Config 1: Satellite Rendezvous |
|
|
|
|
**File:** `tests/configs/satellite_rendezvous.toml` |
|
|
|
|
|
|
|
|
|
```toml |
|
|
|
|
[[bodies]] |
|
|
|
|
name = "Sun" |
|
|
|
|
mass = 1.989e30 |
|
|
|
|
radius = 6.96e8 |
|
|
|
|
position = { x = 0.0, y = 0.0, z = 0.0 } |
|
|
|
|
parent_index = -1 |
|
|
|
|
color = { r = 1.0, g = 1.0, b = 0.0 } |
|
|
|
|
eccentricity = 0.0 |
|
|
|
|
semi_major_axis = 0.0 |
|
|
|
|
|
|
|
|
|
[[bodies]] |
|
|
|
|
name = "Earth" |
|
|
|
|
mass = 5.972e24 |
|
|
|
|
radius = 6.371e6 |
|
|
|
|
position = { x = 1.496e11, y = 0.0, z = 0.0 } |
|
|
|
|
parent_index = 0 |
|
|
|
|
color = { r = 0.0, g = 0.5, b = 1.0 } |
|
|
|
|
eccentricity = 0.0 |
|
|
|
|
semi_major_axis = 1.496e11 |
|
|
|
|
|
|
|
|
|
[[bodies]] |
|
|
|
|
name = "Moon" |
|
|
|
|
mass = 7.342e22 |
|
|
|
|
radius = 1.737e6 |
|
|
|
|
position = { x = 1.49984e11, y = 0.0, z = 0.0 } |
|
|
|
|
parent_index = 1 |
|
|
|
|
color = { r = 0.7, g = 0.7, b = 0.7 } |
|
|
|
|
eccentricity = 0.0 |
|
|
|
|
semi_major_axis = 3.844e8 |
|
|
|
|
|
|
|
|
|
[[bodies]] |
|
|
|
|
name = "Satellite" |
|
|
|
|
mass = 1.0e3 |
|
|
|
|
radius = 1.0e1 |
|
|
|
|
position = { x = 1.500e11, y = 1.0e8, z = 0.0 } |
|
|
|
|
parent_index = 1 |
|
|
|
|
color = { r = 1.0, g = 0.0, b = 1.0 } |
|
|
|
|
eccentricity = 0.2 |
|
|
|
|
semi_major_axis = 4.0e8 |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
**Scenario:** Satellite launches from Earth, transfers to Moon, returns |
|
|
|
|
|
|
|
|
|
#### Test Config 2: Interplanetary Transfer |
|
|
|
|
**File:** `tests/configs/interplanetary_transfer.toml` |
|
|
|
|
|
|
|
|
|
```toml |
|
|
|
|
[[bodies]] |
|
|
|
|
name = "Sun" |
|
|
|
|
mass = 1.989e30 |
|
|
|
|
radius = 6.96e8 |
|
|
|
|
position = { x = 0.0, y = 0.0, z = 0.0 } |
|
|
|
|
parent_index = -1 |
|
|
|
|
color = { r = 1.0, g = 1.0, b = 0.0 } |
|
|
|
|
eccentricity = 0.0 |
|
|
|
|
semi_major_axis = 0.0 |
|
|
|
|
|
|
|
|
|
[[bodies]] |
|
|
|
|
name = "Earth" |
|
|
|
|
mass = 5.972e24 |
|
|
|
|
radius = 6.371e6 |
|
|
|
|
position = { x = 1.496e11, y = 0.0, z = 0.0 } |
|
|
|
|
parent_index = 0 |
|
|
|
|
color = { r = 0.0, g = 0.5, b = 1.0 } |
|
|
|
|
eccentricity = 0.0 |
|
|
|
|
semi_major_axis = 1.496e11 |
|
|
|
|
|
|
|
|
|
[[bodies]] |
|
|
|
|
name = "Mars" |
|
|
|
|
mass = 6.39e23 |
|
|
|
|
radius = 3.3895e6 |
|
|
|
|
position = { x = 2.279e11, y = 0.0, z = 0.0 } |
|
|
|
|
parent_index = 0 |
|
|
|
|
color = { r = 0.8, g = 0.3, b = 0.1 } |
|
|
|
|
eccentricity = 0.0 |
|
|
|
|
semi_major_axis = 2.279e11 |
|
|
|
|
|
|
|
|
|
[[bodies]] |
|
|
|
|
name = "Probe" |
|
|
|
|
mass = 1.0e3 |
|
|
|
|
radius = 1.0e1 |
|
|
|
|
position = { x = 1.496e11, y = 0.0, z = 0.0 } |
|
|
|
|
parent_index = 1 |
|
|
|
|
color = { r = 0.0, g = 1.0, b = 0.0 } |
|
|
|
|
eccentricity = 0.5 |
|
|
|
|
semi_major_axis = 1.888e11 |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
**Scenario:** Probe: Earth→Sun→Mars |
|
|
|
|
|
|
|
|
|
#### Test Config 3: Moon Capture |
|
|
|
|
**File:** `tests/configs/moon_capture.toml` |
|
|
|
|
|
|
|
|
|
```toml |
|
|
|
|
[[bodies]] |
|
|
|
|
name = "Sun" |
|
|
|
|
mass = 1.989e30 |
|
|
|
|
radius = 6.96e8 |
|
|
|
|
position = { x = 0.0, y = 0.0, z = 0.0 } |
|
|
|
|
parent_index = -1 |
|
|
|
|
color = { r = 1.0, g = 1.0, b = 0.0 } |
|
|
|
|
eccentricity = 0.0 |
|
|
|
|
semi_major_axis = 0.0 |
|
|
|
|
|
|
|
|
|
[[bodies]] |
|
|
|
|
name = "Jupiter" |
|
|
|
|
mass = 1.898e27 |
|
|
|
|
radius = 6.9911e7 |
|
|
|
|
position = { x = 7.785e11, y = 0.0, z = 0.0 } |
|
|
|
|
parent_index = 0 |
|
|
|
|
color = { r = 0.9, g = 0.7, b = 0.5 } |
|
|
|
|
eccentricity = 0.0 |
|
|
|
|
semi_major_axis = 7.785e11 |
|
|
|
|
|
|
|
|
|
[[bodies]] |
|
|
|
|
name = "Ganymede" |
|
|
|
|
mass = 1.48e23 |
|
|
|
|
radius = 2.634e6 |
|
|
|
|
position = { x = 7.796e11, y = 0.0, z = 0.0 } |
|
|
|
|
parent_index = 1 |
|
|
|
|
color = { r = 0.6, g = 0.6, b = 0.5 } |
|
|
|
|
eccentricity = 0.0 |
|
|
|
|
semi_major_axis = 1.070e9 |
|
|
|
|
|
|
|
|
|
[[bodies]] |
|
|
|
|
name = "Comet" |
|
|
|
|
mass = 1.0e14 |
|
|
|
|
radius = 5.0e3 |
|
|
|
|
position = { x = 1.0e12, y = 0.0, z = 0.0 } |
|
|
|
|
parent_index = 0 |
|
|
|
|
color = { r = 0.5, g = 0.8, b = 1.0 } |
|
|
|
|
eccentricity = 2.0 |
|
|
|
|
semi_major_axis = -5.0e11 |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
**Scenario:** Comet: Sun→Jupiter→Ganymede→Sun |
|
|
|
|
|
|
|
|
|
**Files to create:** |
|
|
|
|
- `tests/configs/satellite_rendezvous.toml` |
|
|
|
|
- `tests/configs/interplanetary_transfer.toml` |
|
|
|
|
- `tests/configs/moon_capture.toml` |
|
|
|
|
|
|
|
|
|
**Estimated complexity:** Low |
|
|
|
|
**Risk:** Low (configs only, no code changes) |
|
|
|
|
|
|
|
|
|
**Expected outcome:** |
|
|
|
|
- ✅ Realistic test scenarios for patched conics |
|
|
|
|
- ✅ Covers multi-leg missions |
|
|
|
|
- ✅ Tests root body transitions |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
### Phase 5: Adaptive Timestepping (Performance + Accuracy) |
|
|
|
|
|
|
|
|
|
**Goal:** Different timesteps for different orbital phases |
|
|
|
|
|
|
|
|
|
**Decision:** DEFERRED to later work - **DECIDED ✅** |
|
|
|
|
|
|
|
|
|
**Problem:** Fixed 60s timestep is: |
|
|
|
|
- Too coarse for fast orbital phases (moon capture) |
|
|
|
|
- Too slow for deep-space phases |
|
|
|
|
|
|
|
|
|
**Proposed 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:** |
|
|
|
|
1. Add per-body timesteps to `SimulationState` |
|
|
|
|
2. Update `update_simulation()` to use adaptive timesteps |
|
|
|
|
3. Add synchronization mechanism (multiple timesteps) |
|
|
|
|
|
|
|
|
|
**Complexity:** High |
|
|
|
|
**Risk:** High (may affect energy conservation, requires careful testing) |
|
|
|
|
|
|
|
|
|
**Expected outcome:** |
|
|
|
|
- ✅ Better accuracy for fast orbits (moon capture) |
|
|
|
|
- ✅ Faster simulation for deep-space phases |
|
|
|
|
- ✅ Energy conserved across transitions |
|
|
|
|
|
|
|
|
|
**Tests to add:** |
|
|
|
|
- Verify energy drift with adaptive timesteps |
|
|
|
|
- Verify orbital period accuracy with adaptive timesteps |
|
|
|
|
- Test stability across SOI transitions |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
### Phase 6: Enhanced Debugging & Visualization |
|
|
|
|
|
|
|
|
|
**Goal:** Better tools for debugging transitions and planning missions |
|
|
|
|
|
|
|
|
|
**Features to add:** |
|
|
|
|
|
|
|
|
|
#### 6.1 Transition Logging |
|
|
|
|
```cpp |
|
|
|
|
void log_transition(SimulationState* sim, CelestialBody* body, |
|
|
|
|
int old_parent, int new_parent, double time) { |
|
|
|
|
printf("[%.2f days] %s: parent %d → %d\n", |
|
|
|
|
time / 86400.0, body->name, old_parent, new_parent); |
|
|
|
|
} |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
#### 6.2 Visual SOI Boundaries |
|
|
|
|
- Add wireframe spheres in renderer for SOI radii |
|
|
|
|
- Toggle with keyboard key |
|
|
|
|
- Use different colors for different bodies |
|
|
|
|
|
|
|
|
|
#### 6.3 Trajectory Prediction |
|
|
|
|
- Predict future trajectory for given time span |
|
|
|
|
- Show predicted SOI crossings |
|
|
|
|
- Assist with mission planning |
|
|
|
|
|
|
|
|
|
**Files to modify:** |
|
|
|
|
- `src/simulation.cpp` (logging) |
|
|
|
|
- `src/renderer.cpp` (SOI visualization) |
|
|
|
|
- `src/renderer.cpp` (trajectory prediction - new function) |
|
|
|
|
|
|
|
|
|
**Estimated complexity:** Medium |
|
|
|
|
**Risk:** Low (mostly UI/visualization) |
|
|
|
|
|
|
|
|
|
**Expected outcome:** |
|
|
|
|
- ✅ Better debugging tools for transitions |
|
|
|
|
- ✅ Visual SOI boundaries |
|
|
|
|
- ✅ Trajectory prediction for mission planning |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
## Decisions Made |
|
|
|
|
|
|
|
|
|
### Decision 1: Hysteresis Strategy ✅ |
|
|
|
|
**Chosen:** Option B - Adaptive hysteresis approach |
|
|
|
|
|
|
|
|
|
**Rationale:** |
|
|
|
|
- Prevents oscillation while enabling round-trips |
|
|
|
|
- Maintains stability during normal operation |
|
|
|
|
- Allows free switching when outside current SOI |
|
|
|
|
- Best balance between stability and flexibility |
|
|
|
|
|
|
|
|
|
**Implementation:** Will use adaptive hysteresis in Phase 2 (lines 70-75 in simulation.cpp) |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
### Decision 2: Integration Timing ✅ |
|
|
|
|
**Chosen:** Option A - Keep current approach (transition at start of timestep) |
|
|
|
|
|
|
|
|
|
**Rationale:** |
|
|
|
|
- Simple, works for most cases |
|
|
|
|
- Start with proven approach |
|
|
|
|
- Defer optimization to future work |
|
|
|
|
|
|
|
|
|
**TODO:** Consider half-step transitions (Option B) if velocity discontinuities become problematic |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
### Decision 3: Test Priorities ✅ |
|
|
|
|
**Chosen:** Option C - Implement all three test cases first |
|
|
|
|
|
|
|
|
|
**Rationale:** |
|
|
|
|
- Create all test configurations upfront |
|
|
|
|
- Expect failures for unimplemented features |
|
|
|
|
- Use tests as validation throughout implementation |
|
|
|
|
- Comprehensive coverage from the start |
|
|
|
|
|
|
|
|
|
**Implementation:** |
|
|
|
|
1. Create all three test configs (Phase 4) |
|
|
|
|
2. Run tests to establish baseline failures |
|
|
|
|
3. Implement features to make tests pass |
|
|
|
|
4. Re-run tests after each phase |
|
|
|
|
|
|
|
|
|
**Test scenarios:** |
|
|
|
|
- Satellite Rendezvous (Earth→Moon→Earth) |
|
|
|
|
- Interplanetary Transfer (Earth→Sun→Mars) |
|
|
|
|
- Moon Capture (Sun→Jupiter→Ganymede→Sun) |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
### Decision 4: Adaptive Timesteps Priority ✅ |
|
|
|
|
**Chosen:** Option B - Defer to later work |
|
|
|
|
|
|
|
|
|
**Rationale:** |
|
|
|
|
- Focus on SOI transitions first |
|
|
|
|
- Fixed 60s timestep works adequately for testing |
|
|
|
|
- Optimize performance and accuracy after core features complete |
|
|
|
|
|
|
|
|
|
**TODO:** Implement adaptive timesteps in future update (Phase 5) |
|
|
|
|
- Will address: Too coarse for fast orbits, too slow for deep space |
|
|
|
|
- Estimated complexity: High |
|
|
|
|
- Timeline: 2-3 days |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
## Success Criteria |
|
|
|
|
|
|
|
|
|
### Phase 1 Success |
|
|
|
|
- [ ] Satellite can transition to/from Sun |
|
|
|
|
- [ ] Tests pass for Earth→Sun→Mars |
|
|
|
|
- [ ] No energy conservation issues during transitions |
|
|
|
|
|
|
|
|
|
### Phase 2 Success |
|
|
|
|
- [ ] Round-trip transitions work (Earth→Sun→Earth) |
|
|
|
|
- [ ] No oscillation at SOI boundaries |
|
|
|
|
- [ ] Tests validate stability |
|
|
|
|
|
|
|
|
|
### Phase 3 Success |
|
|
|
|
- [ ] Transition logic extracted to separate function |
|
|
|
|
- [ ] Unit tests cover all transition scenarios |
|
|
|
|
- [ ] Code is cleaner and more maintainable |
|
|
|
|
|
|
|
|
|
### Phase 4 Success |
|
|
|
|
- [ ] Three test configs created |
|
|
|
|
- [ ] Baseline test failures documented |
|
|
|
|
- [ ] Configs are realistic and well-documented |
|
|
|
|
|
|
|
|
|
### Phase 5 Success (Deferred) |
|
|
|
|
- [ ] Adaptive timesteps implemented |
|
|
|
|
- [ ] Energy drift < 1% with adaptive timesteps |
|
|
|
|
- [ ] Performance improved for deep-space phases |
|
|
|
|
|
|
|
|
|
### Phase 6 Success (Optional) |
|
|
|
|
- [ ] Transition logging works |
|
|
|
|
- [ ] SOI boundaries visualized |
|
|
|
|
- [ ] Trajectory prediction functional |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
## Timeline Estimate |
|
|
|
|
|
|
|
|
|
- **Phase 1:** 1-2 days (fix root body transitions) |
|
|
|
|
- **Phase 2:** 1 day (adaptive hysteresis - decision made ✅) |
|
|
|
|
- **Phase 3:** 0.5 days (refactoring) |
|
|
|
|
- **Phase 4:** 1 day (test configs - create all three first) |
|
|
|
|
- **Phase 5:** 2-3 days (adaptive timesteps - DEFERRED ⏸️) |
|
|
|
|
- **Phase 6:** 1-2 days (debugging/visualization - optional) |
|
|
|
|
|
|
|
|
|
**Total for core features (Phases 1-4):** 4-5 days |
|
|
|
|
**Total with all features:** 8-10 days (including deferred Phases 5-6) |
|
|
|
|
|
|
|
|
|
**Implementation approach:** |
|
|
|
|
1. Create all three test configs (Phase 4) |
|
|
|
|
2. Implement core transition features (Phases 1-3) |
|
|
|
|
3. Validate with tests after each phase |
|
|
|
|
4. Defer Phases 5-6 to future work |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
## Dependencies |
|
|
|
|
|
|
|
|
|
### Required |
|
|
|
|
- ✅ Hierarchical coordinate frames (complete) |
|
|
|
|
- ✅ Local frame integration (complete) |
|
|
|
|
- ✅ Parent-first update order (complete) |
|
|
|
|
- ⏸️ Root body transitions (Phase 1 - pending) |
|
|
|
|
|
|
|
|
|
### Optional |
|
|
|
|
- ⏸️ Adaptive timesteps (Phase 5 - optional) |
|
|
|
|
- ⏸️ Debugging tools (Phase 6 - optional) |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
## Risks and Mitigations |
|
|
|
|
|
|
|
|
|
### High Risk |
|
|
|
|
- **Energy conservation during transitions** |
|
|
|
|
- Mitigation: Careful testing, energy drift checks |
|
|
|
|
- Backup: Keep old code for rollback |
|
|
|
|
|
|
|
|
|
- **Numerical precision across scales** |
|
|
|
|
- Mitigation: Use local frames (already implemented) |
|
|
|
|
- Backup: Double precision (already used) |
|
|
|
|
|
|
|
|
|
### Medium Risk |
|
|
|
|
- **Oscillation at SOI boundaries** |
|
|
|
|
- Mitigation: Adaptive hysteresis (Phase 2) |
|
|
|
|
- Backup: Increase hysteresis factor if needed |
|
|
|
|
|
|
|
|
|
- **Timestep too coarse for fast orbits** |
|
|
|
|
- Mitigation: Adaptive timesteps (Phase 5 - optional) |
|
|
|
|
- Backup: Reduce fixed timestep if needed |
|
|
|
|
|
|
|
|
|
### Low Risk |
|
|
|
|
- **Code complexity increases** |
|
|
|
|
- Mitigation: Good unit tests, refactoring (Phase 3) |
|
|
|
|
- Backup: Keep functions small and focused |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
## References |
|
|
|
|
|
|
|
|
|
- `docs/hierarchical_frames_plan.md` - Phase 3: SOI Transition with Frame Transform |
|
|
|
|
- `docs/implementation_plan.md` - SOI Transition Mechanics section |
|
|
|
|
- `src/simulation.cpp` - Current SOI transition implementation (lines 103-121) |
|
|
|
|
- `tests/test_soi_transition.cpp` - Current SOI transition tests |