# Maneuver Planning System - Implementation Plan ## Overview Add a maneuver planning system to SimulationState that allows pre-defining burns in config files, which will execute automatically when trigger conditions are met. --- ## Design Decisions 1. **Trigger Types:** Start with TIME and TRUE_ANOMALY only 2. **SOI Triggers:** Not implementing SOI_ENTRY/EXIT to start 3. **Custom Burns:** Not implementing custom burns to start (delta_v only) 4. **Error Handling:** Fail config load if spacecraft_name doesn't exist or if maneuver name is not unique 5. **Manual Triggers:** Deferred until later --- ## 1. Data Structures ### A. Maneuver Struct (`src/maneuver.h`) ```cpp enum TriggerType { TRIGGER_TIME, // Execute at specific simulation time (seconds) TRIGGER_TRUE_ANOMALY // Execute at specific orbital true anomaly (radians) }; struct Maneuver { char name[64]; // Maneuver identifier (must be unique) int craft_index; // Index of spacecraft in sim->spacecraft // Burn parameters BurnDirection direction; // Prograde, retrograde, normal, etc. double delta_v; // Magnitude (for standard directions) // Trigger condition TriggerType trigger_type; double trigger_value; // Time (seconds) or true anomaly (radians) // Execution state bool executed; // Has the burn been applied? double executed_time; // Simulation time when executed }; ``` ### B. SimulationState Extension (`src/simulation.h`) ```cpp struct SimulationState { CelestialBody* bodies; int body_count; int max_bodies; Spacecraft* spacecraft; int craft_count; int max_craft; Maneuver* maneuvers; // NEW: Planned maneuvers array int maneuver_count; // NEW: Current number of maneuvers int max_maneuvers; // NEW: Maximum capacity double time; double dt; char config_name[256]; }; ``` --- ## 2. Config File Format ```toml [[maneuvers]] name = "orbit_raise_1" spacecraft_name = "LEO_Satellite" trigger_type = "time" trigger_value = 3600.0 # 1 hour into simulation direction = "prograde" delta_v = 500.0 [[maneuvers]] name = "plane_change" spacecraft_name = "LEO_Satellite" trigger_type = "true_anomaly" trigger_value = 1.5708 # 90 degrees in radians direction = "normal" delta_v = 300.0 ``` **Config Fields:** - `name` - Maneuver identifier (must be unique) - `spacecraft_name` - Which spacecraft (string, resolved to index after loading) - `trigger_type` - One of: "time", "true_anomaly" - `trigger_value` - Numeric threshold (time in seconds, angle in radians) - `direction` - One of: "prograde", "retrograde", "normal", "antinormal", "radial_in", "radial_out" - `delta_v` - Magnitude for standard directions --- ## 3. Implementation Plan ### Phase 1: Data Structures 1. **Update `src/maneuver.h`:** - Add `TriggerType` enum (TIME, TRUE_ANOMALY) - Add `Maneuver` struct with all required fields 2. **Update `src/simulation.h`:** - Add maneuver array fields to SimulationState - Add `max_maneuvers` parameter to `create_simulation()` signature 3. **Update `src/simulation.cpp`:** - Allocate maneuver array in `create_simulation()` - Free maneuver array in `destroy_simulation()` - Initialize maneuver_count to 0 ### Phase 2: Config Loading 4. **Update `src/config_loader.h`:** - No API changes - internal helper only 5. **Update `src/config_loader.cpp`:** - Add static helper `parse_toml_maneuver()` to parse individual maneuver entries - Add `load_maneuvers_from_toml(SimulationState* sim, toml_result_t result)` helper - Call from `load_system_config()` after loading spacecraft - Resolve spacecraft_name → craft_index (requires spacecraft loaded first) - Validate spacecraft_name exists (fail config if not found) - Validate maneuver name uniqueness (fail config if duplicate) 6. **String parsing in config_loader.cpp:** - Parse trigger_type string → enum ("time" → TRIGGER_TIME, "true_anomaly" → TRIGGER_TRUE_ANOMALY) - Parse direction string → BurnDirection enum ### Phase 3: Execution Logic 7. **Update `src/maneuver.h`:** - Add function declarations for trigger checking and execution 8. **Update `src/maneuver.cpp`:** - Add `check_maneuver_trigger(Maneuver* maneuver, Spacecraft* craft, SimulationState* sim, bool* triggered)` - Add `execute_maneuver(Maneuver* maneuver, Spacecraft* craft, double current_time)` - Update executed flag and executed_time 9. **Update `src/simulation.cpp`:** - In `update_simulation()`, after spacecraft updates: - Call `execute_pending_maneuvers(sim)` - Add `execute_pending_maneuvers(SimulationState* sim)` function that: - Loops through maneuvers - Skips executed ones - Checks trigger condition - Executes if triggered - Updates spacecraft velocity - Marks as executed ### Phase 4: Trigger Condition Logic 10. **Implement trigger checks:** - **TIME:** Check `sim->time >= trigger_value` - **TRUE_ANOMALY:** Calculate current true anomaly from position/velocity, compare to threshold - True anomaly ν = atan2(√(1-e²) * sin(E), cos(E)) where E is eccentric anomaly - Or use direct position/velocity: ν = atan2(r·v, r·v⊥) (simpler) - Use normalized position/velocity for stability 11. **True anomaly calculation:** - Position vector r = local_position - Velocity vector v = local_velocity - Angular momentum h = r × v - Eccentricity vector e = (v × h) / μ - r/|r| - True anomaly ν = acos( (e·r) / (|e|·|r|) ) - Check sign using (r·h) to determine quadrant (prograde vs retrograde) ### Phase 5: Testing 12. **Create test config** (`tests/configs/maneuver_sequence.toml`): - Time-based prograde burn to raise orbit - True anomaly-based normal burn for plane change - Verify spacecraft with specified name exists 13. **Create test file** (`tests/test_maneuver_planning.cpp`): - Test config loading with valid maneuvers - Test config failure with duplicate maneuver names - Test config failure with non-existent spacecraft - Test time-based trigger execution - Test true anomaly trigger execution - Test maneuver executed state tracking - Test maneuvers only fire once 14. **Update all test files:** - Update `create_simulation()` calls to include max_maneuvers parameter 15. **Update main.cpp:** - Update `create_simulation()` call with max_maneuvers parameter --- ## 4. Key Design Decisions ### A. Spacecraft Resolution - Config uses `spacecraft_name` (string) - After loading spacecraft array, resolve names to indices - Validate that spacecraft exists before loading maneuvers - Fail config load if spacecraft not found ### B. Maneuver Name Uniqueness - Maneuver names must be unique identifiers - Check for duplicates while loading - Fail config load if duplicate found ### C. Trigger Types (Initial Implementation) - **TIME:** Simple time-based trigger - **TRUE_ANOMALY:** Orbital position-based trigger - Deferred: SOI_ENTRY, SOI_EXIT, DISTANCE, MANUAL ### D. Burn Directions (Initial Implementation) - Standard directions: prograde, retrograde, normal, antinormal, radial_in, radial_out - Deferred: custom burns with arbitrary delta_v vector ### E. Execution Timing - Check triggers **after** physics update in `update_simulation()` - Apply burns to spacecraft `local_velocity` and `velocity` - Mark as executed to prevent re-firing - Record executed_time for debugging ### F. True Anomaly Calculation - Use eccentric anomaly approach for numerical stability - Alternative: use position/velocity dot products directly - Need to calculate orbital elements from current state --- ## 5. Future Enhancements - Additional trigger types (distance, SOI entry/exit) - Custom burns with arbitrary delta-v vectors - Manual trigger API for interactive control - Maneuver groups/chaining - Delta-v budget tracking - Burn timeline visualization - Undo/revert maneuver capability