Browse Source
- Add scheduled_dt field to Maneuver struct for precise timing - Propagate spacecraft to exact trigger position before burn execution - Handle 2π→0 wraparound in trigger crossing detection - Add spacecraft tracking to prevent double-propagation in same frame - Fix test configs and tolerances for new behavior All 143 tests passing.main
8 changed files with 207 additions and 61 deletions
@ -0,0 +1,143 @@ |
|||||||
|
# Exact Position Burn Execution - Implementation Plan |
||||||
|
|
||||||
|
**Date:** 2026-02-21 |
||||||
|
|
||||||
|
## Status: COMPLETED ✅ |
||||||
|
|
||||||
|
All 143 tests passing. |
||||||
|
|
||||||
|
## Problem Statement |
||||||
|
|
||||||
|
True anomaly triggers correctly calculate when a crossing will occur, but burns execute at the **current** position, not the **trigger** position. |
||||||
|
|
||||||
|
### Original Behavior |
||||||
|
|
||||||
|
``` |
||||||
|
Frame N: spacecraft at nu=6.22 rad |
||||||
|
Trigger: dt_needed = 52.95s (< 60s dt) → fires |
||||||
|
Burn executes at nu=6.22 rad ❌ (should be at nu=0.0) |
||||||
|
``` |
||||||
|
|
||||||
|
### New Behavior |
||||||
|
|
||||||
|
``` |
||||||
|
Frame N: spacecraft at nu=6.22 rad |
||||||
|
Trigger: dt_needed = 52.95s → fires, sets maneuver->scheduled_dt = 52.95 |
||||||
|
execute_pending_maneuvers(): |
||||||
|
- Propagates spacecraft by 52.95s → spacecraft now at nu=0.0 rad |
||||||
|
- Executes burn at exact periapsis ✓ |
||||||
|
- Propagates by (60 - 52.95) = 7.05s for remaining frame time |
||||||
|
``` |
||||||
|
|
||||||
|
### Test Failures |
||||||
|
|
||||||
|
| Test | Failure | Root Cause | |
||||||
|
|------|---------|------------| |
||||||
|
| Prograde burn at periapsis | Maneuver never fires | Config has `true_anomaly=0.1`, spacecraft moves AWAY from target | |
||||||
|
| Two periapsis burns | Radius mismatch | Burn fires 3.5° before periapsis | |
||||||
|
| Periapsis burn crossing | Radius off by 1,785m | Burn fires 3.5° before periapsis | |
||||||
|
| Burn location = new periapsis | Position mismatch | Test records position BEFORE burn | |
||||||
|
|
||||||
|
## Solution Design |
||||||
|
|
||||||
|
Add a `scheduled_dt` field to `Maneuver` struct to communicate the exact propagation time needed from trigger check to execution. |
||||||
|
|
||||||
|
### Modified Flow |
||||||
|
|
||||||
|
``` |
||||||
|
Frame N: spacecraft at nu=6.22 rad |
||||||
|
Trigger: dt_needed = 52.95s → fires, sets maneuver->scheduled_dt = 52.95 |
||||||
|
execute_pending_maneuvers(): |
||||||
|
- Propagates spacecraft by 52.95s → spacecraft now at nu=0.0 rad |
||||||
|
- Executes burn at exact periapsis ✓ |
||||||
|
- Propagates by (60 - 52.95) = 7.05s for remaining frame time |
||||||
|
``` |
||||||
|
|
||||||
|
## Files to Modify |
||||||
|
|
||||||
|
### 1. src/maneuver.h - Add field to Maneuver struct |
||||||
|
|
||||||
|
```cpp |
||||||
|
struct Maneuver { |
||||||
|
char name[64]; |
||||||
|
int craft_index; |
||||||
|
BurnDirection direction; |
||||||
|
double delta_v; |
||||||
|
TriggerType trigger_type; |
||||||
|
double trigger_value; |
||||||
|
double scheduled_dt; // NEW: Time to propagate before executing burn |
||||||
|
bool executed; |
||||||
|
double executed_time; |
||||||
|
}; |
||||||
|
``` |
||||||
|
|
||||||
|
### 2. src/maneuver.cpp - Modify check_maneuver_trigger() |
||||||
|
|
||||||
|
- When immediate trigger (`current_diff < 0.01`): set `scheduled_dt = 0.0` |
||||||
|
- When crossing detected with `dt_needed < sim->dt`: set `scheduled_dt = dt_needed` |
||||||
|
- Return value unchanged |
||||||
|
|
||||||
|
### 3. src/simulation.cpp - Modify execute_pending_maneuvers() |
||||||
|
|
||||||
|
```cpp |
||||||
|
void execute_pending_maneuvers(SimulationState* sim) { |
||||||
|
for (int i = 0; i < sim->maneuver_count; i++) { |
||||||
|
Maneuver* maneuver = &sim->maneuvers[i]; |
||||||
|
if (maneuver->executed) continue; |
||||||
|
if (maneuver->craft_index < 0 || maneuver->craft_index >= sim->craft_count) continue; |
||||||
|
|
||||||
|
Spacecraft* craft = &sim->spacecraft[maneuver->craft_index]; |
||||||
|
|
||||||
|
if (check_maneuver_trigger(maneuver, craft, sim)) { |
||||||
|
CelestialBody* parent = &sim->bodies[craft->parent_index]; |
||||||
|
|
||||||
|
// Propagate to exact trigger position |
||||||
|
if (maneuver->scheduled_dt > 0.0) { |
||||||
|
craft->orbit = propagate_orbital_elements(craft->orbit, maneuver->scheduled_dt, parent->mass); |
||||||
|
orbital_elements_to_cartesian(craft->orbit, parent->mass, &craft->local_position, &craft->local_velocity); |
||||||
|
} |
||||||
|
|
||||||
|
execute_maneuver(maneuver, craft, sim, sim->time + maneuver->scheduled_dt); |
||||||
|
|
||||||
|
// Propagate remaining frame time |
||||||
|
double remaining_dt = sim->dt - maneuver->scheduled_dt; |
||||||
|
if (remaining_dt > 0.0) { |
||||||
|
craft->orbit = propagate_orbital_elements(craft->orbit, remaining_dt, parent->mass); |
||||||
|
orbital_elements_to_cartesian(craft->orbit, parent->mass, &craft->local_position, &craft->local_velocity); |
||||||
|
} |
||||||
|
|
||||||
|
maneuver->scheduled_dt = 0.0; // Reset for safety |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
### 4. src/maneuver.cpp - create_maneuver() |
||||||
|
|
||||||
|
Initialize `scheduled_dt = 0.0` |
||||||
|
|
||||||
|
### 5. src/config_loader.cpp |
||||||
|
|
||||||
|
Initialize `scheduled_dt = 0.0` when loading maneuvers from TOML. |
||||||
|
|
||||||
|
## Design Decisions |
||||||
|
|
||||||
|
### Propagation Strategy (Option 3) |
||||||
|
|
||||||
|
All propagation for maneuvering spacecraft happens in `execute_pending_maneuvers()`. The `update_spacecraft_physics()` will still run but the velocity deviation check will reconstruct elements if needed. This keeps maneuver logic contained in one place. |
||||||
|
|
||||||
|
### Edge Cases Handled |
||||||
|
|
||||||
|
1. **scheduled_dt > sim->dt**: Defensive check added (should never happen per trigger logic) |
||||||
|
2. **Two burns in same frame**: Both can fire if both scheduled_dt values fit within dt |
||||||
|
3. **scheduled_dt = 0**: No pre-propagation, burn executes at current position (immediate trigger case) |
||||||
|
|
||||||
|
## Cleanup |
||||||
|
|
||||||
|
After tests pass, remove DEBUG INFO printf statements from `src/maneuver.cpp` (lines ~153-212). |
||||||
|
|
||||||
|
## Expected Test Results |
||||||
|
|
||||||
|
- Burns execute at exact periapsis (within floating-point precision) |
||||||
|
- All 4 failing tests should pass with current 1000m tolerance |
||||||
|
- "Prograde burn at periapsis" may need test config adjustment (starts at true_anomaly=0.1) |
||||||
Loading…
Reference in new issue