vibe coding an orbital mechanics simulation to try out claude code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

4.9 KiB

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

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()

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)