26 KiB
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: Ready to implement Branch: mission-planning
Phase 0: Git Workflow Preparation
Step 0.1: Stash debug changes on main
git stash push -m "Debug printf statements for spacecraft parent switch investigation"
Step 0.2: Checkout and update mission-planning branch
git checkout mission-planning
git rebase main # Or git merge main if cleaner
Step 0.3: Apply debug changes to mission-planning branch
git stash list # Verify stash exists
git stash pop # Apply debug changes
Verification: Confirm debug printf statements are in src/simulation.cpp after applying stash
Phase 1: Update Configuration File
Step 1.1: Add spacecraft to tests/configs/earth_mars_simple.toml
Append to config file:
[[bodies]]
name = "Spacecraft"
mass = 1.0
radius = 1000.0
# Position and velocity will be initialized at runtime for LEO orbit
position = { x = 0.0, y = 0.0, z = 0.0 }
velocity = { x = 0.0, y = 0.0, z = 0.0 }
parent_index = 1 # Earth
color = { r = 1.0, g = 0.0, b = 0.5 }
eccentricity = 0.0
# Semi-major axis will be: Earth radius + 200km
semi_major_axis = 6.571e6 # Placeholder, will be set during initialization
Note: Position/velocity are placeholders; will be calculated by initialize_spacecraft_leo() at runtime.
TODO: Future config file format should support:
- Earth-relative position (e.g.,
{ altitude_km = 200.0 }) - Earth-relative velocity (e.g.,
{ 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
// Initialize spacecraft in circular LEO around parent body
void initialize_spacecraft_leo(CelestialBody* spacecraft, CelestialBody* parent,
double altitude_m);
// Apply patched conics impulse burn for Hohmann transfer
void apply_transfer_burn(SimulationState* sim, int spacecraft_idx,
int departure_idx, TransferParameters* params);
// Helper: Calculate current phase angle between two bodies (in degrees)
double calculate_phase_angle(SimulationState* sim, int departure_idx, int arrival_idx);
Step 2.2: Implement initialize_spacecraft_leo() in src/mission_planning.cpp
Algorithm:
void initialize_spacecraft_leo(CelestialBody* spacecraft, CelestialBody* parent,
double altitude_m) {
// Calculate orbital radius (distance from Earth center)
double orbital_radius = parent->radius + altitude_m;
// Position spacecraft radially outward from Earth-Sun line
// Get vector from Sun to Earth
Vec3 sun_to_earth = vec3_sub(parent->position,
(Vec3){0.0, 0.0, 0.0}); // Sun at origin
Vec3 direction = vec3_normalize(sun_to_earth);
// Position: Earth position + offset radially outward
Vec3 offset = vec3_scale(direction, orbital_radius);
spacecraft->position = vec3_add(parent->position, offset);
// Initialize local coordinates (relative to parent)
spacecraft->local_position = offset;
spacecraft->local_velocity = (Vec3){0.0, 0.0, 0.0}; // Will be set below
// Calculate circular LEO velocity magnitude
double v_leo = sqrt(G * parent->mass / orbital_radius);
// Direction: tangential to Earth-Sun line (prograde)
// If sun_to_earth = (x, y, 0), then tangent = (-y, x, 0)
Vec3 leo_tangent = (Vec3){-direction.y, direction.x, 0.0};
Vec3 leo_velocity = vec3_scale(leo_tangent, v_leo);
// Spacecraft velocity = Earth velocity + LEO velocity
spacecraft->velocity = vec3_add(parent->velocity, leo_velocity);
// Local velocity relative to Earth = LEO velocity only
spacecraft->local_velocity = leo_velocity;
// Update semi-major axis for reference
spacecraft->semi_major_axis = orbital_radius;
// SOI will be calculated by config loader
}
Key Points:
- Spacecraft positioned radially outward from Sun (any position is acceptable)
- LEO orbit is circular at 200km altitude
- Prograde orientation (same direction as Earth's orbital velocity)
- Both local and global coordinates set correctly
Step 2.3: Implement calculate_phase_angle() in src/mission_planning.cpp
Algorithm:
double calculate_phase_angle(SimulationState* sim, int departure_idx, int arrival_idx) {
CelestialBody* departure = &sim->bodies[departure_idx];
CelestialBody* arrival = &sim->bodies[arrival_idx];
CelestialBody* sun = &sim->bodies[0]; // Assume Sun at index 0
// Calculate angular positions relative to Sun
double theta_depart = calculate_angular_position(departure, sun);
double theta_arrival = calculate_angular_position(arrival, sun);
// Calculate phase difference
double phase_rad = theta_arrival - theta_depart;
// Normalize to [0, 2π)
while (phase_rad < 0.0) {
phase_rad += 2.0 * M_PI;
}
while (phase_rad >= 2.0 * M_PI) {
phase_rad -= 2.0 * M_PI;
}
// Convert to degrees
return phase_rad * 180.0 / M_PI;
}
Step 2.4: Implement apply_transfer_burn() in src/mission_planning.cpp
Algorithm (Patched Conics Approach):
void apply_transfer_burn(SimulationState* sim, int spacecraft_idx,
int departure_idx, TransferParameters* params) {
CelestialBody* spacecraft = &sim->bodies[spacecraft_idx];
CelestialBody* departure = &sim->bodies[departure_idx];
CelestialBody* sun = &sim->bodies[0]; // Assume Sun at index 0
// Calculate required heliocentric transfer velocity
// v_transfer = params->departure_velocity
// Direction: prograde (tangential to Earth-Sun line)
Vec3 sun_to_earth = vec3_sub(departure->position, sun->position);
Vec3 sun_to_earth_norm = vec3_normalize(sun_to_earth);
// Tangent direction (prograde): (-y, x, 0)
Vec3 transfer_dir = (Vec3){-sun_to_earth_norm.y, sun_to_earth_norm.x, 0.0};
Vec3 v_transfer_helio = vec3_scale(transfer_dir, params->departure_velocity);
// Current heliocentric velocity
Vec3 current_helio = spacecraft->velocity;
// Calculate total Δv to apply
Vec3 delta_v = vec3_sub(v_transfer_helio, current_helio);
// Apply impulse burn
spacecraft->velocity = vec3_add(spacecraft->velocity, delta_v);
// Update local velocity
spacecraft->local_velocity = vec3_sub(spacecraft->velocity, departure->velocity);
// Print burn information
printf("Transfer burn applied:\n");
printf(" Current heliocentric velocity: (%.2f, %.2f, %.2f) m/s\n",
current_helio.x, current_helio.y, current_helio.z);
printf(" Target heliocentric velocity: (%.2f, %.2f, %.2f) m/s\n",
v_transfer_helio.x, v_transfer_helio.y, v_transfer_helio.z);
printf(" Delta-v: (%.2f, %.2f, %.2f) m/s\n",
delta_v.x, delta_v.y, delta_v.z);
printf(" Delta-v magnitude: %.2f m/s (%.3f km/s)\n",
vec3_magnitude(delta_v), vec3_magnitude(delta_v) / 1000.0);
}
Note: This is a simplified single-impulse approach. A true patched conics calculation would:
- Calculate Δv to reach SOI boundary (escape trajectory)
- Calculate velocity at SOI boundary
- Add transfer Δv at SOI boundary
- Combine into equivalent single impulse
For initial implementation, we'll use single impulse as approximation.
Phase 3: Comprehensive Test Case
Step 3.1: Create new test in tests/test_hohmann_transfer.cpp
TEST_CASE("Earth → Mars Hohmann Transfer with LEO Spacecraft", "[mission][hohmann][config][integration]") {
const double TIME_STEP = 60.0;
const double SECONDS_PER_DAY = 86400.0;
const double LEO_ALTITUDE_M = 200000.0; // 200 km
// 1. Load config with LEO spacecraft
SimulationState* sim = create_simulation(4, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/earth_mars_simple.toml"));
const int SUN_IDX = 0;
const int EARTH_IDX = 1;
const int MARS_IDX = 2;
const int CRAFT_IDX = 3;
// Verify spacecraft loaded
REQUIRE(sim->body_count == 4);
REQUIRE(strcmp(sim->bodies[CRAFT_IDX].name, "Spacecraft") == 0);
// 2. Initialize spacecraft LEO orbit
initialize_spacecraft_leo(&sim->bodies[CRAFT_IDX], &sim->bodies[EARTH_IDX],
LEO_ALTITUDE_M);
INFO("Spacecraft initialized at %.2f km altitude", LEO_ALTITUDE_M / 1000.0);
INFO("Spacecraft parent: %d (Earth)", sim->bodies[CRAFT_IDX].parent_index);
// 3. Verify initial LEO orbit is stable
REQUIRE(sim->bodies[CRAFT_IDX].parent_index == EARTH_IDX);
double dist_to_earth = vec3_distance(sim->bodies[CRAFT_IDX].position,
sim->bodies[EARTH_IDX].position);
double expected_radius = sim->bodies[EARTH_IDX].radius + LEO_ALTITUDE_M;
REQUIRE(fabs(dist_to_earth - expected_radius) < 1000.0); // Within 1 km
// Verify LEO velocity magnitude
double leo_velocity_mag = sqrt(G * sim->bodies[EARTH_IDX].mass / dist_to_earth);
double v_leo_relative = vec3_magnitude(sim->bodies[CRAFT_IDX].local_velocity);
INFO("Expected LEO velocity: %.2f m/s", leo_velocity_mag);
INFO("Actual LEO velocity: %.2f m/s", v_leo_relative);
REQUIRE(fabs(v_leo_relative - leo_velocity_mag) < 10.0); // Within 10 m/s
// Verify negative total energy (bound to Earth)
OrbitalMetrics leo_metrics = calculate_orbital_metrics(&sim->bodies[CRAFT_IDX],
&sim->bodies[EARTH_IDX]);
INFO("LEO total energy: %.2e J", leo_metrics.total_energy);
REQUIRE(leo_metrics.total_energy < 0.0);
// 4. Calculate Hohmann transfer parameters
double r_earth = vec3_distance(sim->bodies[EARTH_IDX].position,
sim->bodies[SUN_IDX].position);
double r_mars = vec3_distance(sim->bodies[MARS_IDX].position,
sim->bodies[SUN_IDX].position);
TransferParameters params = calculate_hohmann_transfer(r_earth, r_mars,
sim->bodies[SUN_IDX].mass);
INFO("Transfer time: %.2f days", params.transfer_time / SECONDS_PER_DAY);
INFO("Required phase angle: %.3f degrees", params.phase_angle_deg);
INFO("Delta-v injection: %.3f km/s", params.delta_v_injection / 1000.0);
// 5. Wait for Earth-Mars launch window
double wait_start_time = sim->time;
wait_for_launch_window(sim, EARTH_IDX, MARS_IDX, params.phase_angle_deg, 1.0);
double wait_duration = sim->time - wait_start_time;
INFO("Launch window opened after %.2f days", wait_duration / SECONDS_PER_DAY);
// 6. Verify launch window accuracy (within 1°)
double current_phase = calculate_phase_angle(sim, EARTH_IDX, MARS_IDX);
double phase_error = fabs(current_phase - params.phase_angle_deg);
if (phase_error > 180.0) phase_error = fabs(phase_error - 360.0);
INFO("Current phase angle: %.3f degrees", current_phase);
INFO("Required phase angle: %.3f degrees", params.phase_angle_deg);
INFO("Phase angle error: %.3f degrees", phase_error);
REQUIRE(phase_error < 1.0);
// 7. Apply impulse burn for transfer
double pre_burn_time = sim->time;
OrbitalMetrics pre_burn_metrics = calculate_orbital_metrics(&sim->bodies[CRAFT_IDX],
&sim->bodies[SUN_IDX]);
apply_transfer_burn(sim, CRAFT_IDX, EARTH_IDX, ¶ms);
OrbitalMetrics post_burn_metrics = calculate_orbital_metrics(&sim->bodies[CRAFT_IDX],
&sim->bodies[SUN_IDX]);
INFO("Pre-burn heliocentric energy: %.2e J", pre_burn_metrics.total_energy);
INFO("Post-burn heliocentric energy: %.2e J", post_burn_metrics.total_energy);
INFO("Energy added: %.2e J",
post_burn_metrics.total_energy - pre_burn_metrics.total_energy);
// Verify spacecraft is now in escape trajectory (positive or zero energy)
REQUIRE(post_burn_metrics.total_energy >= 0.0);
// 8. Track SOI transitions during transfer
int earth_soi_exit_step = 0;
int sun_soi_enter_step = 0;
int mars_soi_enter_step = 0;
double transfer_duration = params.transfer_time * 1.1;
int max_steps = (int)(transfer_duration / sim->dt);
INFO("Simulating for %.2f days (%d steps)",
transfer_duration / SECONDS_PER_DAY, max_steps);
for (int step = 0; step < max_steps; step++) {
update_simulation(sim);
// Track Earth SOI exit
if (earth_soi_exit_step == 0 &&
sim->bodies[CRAFT_IDX].parent_index != EARTH_IDX) {
earth_soi_exit_step = step;
INFO("Earth SOI exit at step %d (t = %.2f days)",
step, sim->time / SECONDS_PER_DAY);
}
// Track Sun SOI entry (after leaving Earth)
if (earth_soi_exit_step > 0 && sun_soi_enter_step == 0 &&
sim->bodies[CRAFT_IDX].parent_index == SUN_IDX) {
sun_soi_enter_step = step;
INFO("Sun SOI entry at step %d (t = %.2f days)",
step, sim->time / SECONDS_PER_DAY);
}
// Track Mars SOI entry
if (mars_soi_enter_step == 0 &&
sim->bodies[CRAFT_IDX].parent_index == MARS_IDX) {
mars_soi_enter_step = step;
INFO("Mars SOI entry at step %d (t = %.2f days)",
step, sim->time / SECONDS_PER_DAY);
}
}
// 9. Verify Earth → Sun transition occurred
INFO("Earth SOI exit step: %d", earth_soi_exit_step);
INFO("Sun SOI entry step: %d", sun_soi_enter_step);
REQUIRE(earth_soi_exit_step > 0);
REQUIRE(sun_soi_enter_step > 0);
// Final parent should be Sun or Mars
int final_parent = sim->bodies[CRAFT_IDX].parent_index;
REQUIRE(final_parent == SUN_IDX || final_parent == MARS_IDX);
INFO("Final parent: %d (%s)", final_parent,
final_parent == SUN_IDX ? "Sun" : "Mars");
// 10. Verify spacecraft followed transfer orbit (energy conservation)
OrbitalMetrics final_metrics = calculate_orbital_metrics(&sim->bodies[CRAFT_IDX],
&sim->bodies[SUN_IDX]);
double energy_drift = fabs(final_metrics.total_energy - post_burn_metrics.total_energy);
if (post_burn_metrics.total_energy != 0.0) {
energy_drift /= fabs(post_burn_metrics.total_energy);
}
INFO("Final orbital radius: %.2f AU",
final_metrics.orbital_radius / 1.496e11);
INFO("Final energy: %.2e J", final_metrics.total_energy);
INFO("Expected energy: %.2e J", post_burn_metrics.total_energy);
INFO("Energy drift: %.2f%%", energy_drift * 100.0);
REQUIRE(energy_drift < 0.05); // < 5% energy conservation
// 11. If Mars SOI entry occurred, verify distance
if (mars_soi_enter_step > 0) {
double dist_to_mars = vec3_distance(sim->bodies[CRAFT_IDX].position,
sim->bodies[MARS_IDX].position);
INFO("Distance to Mars: %.2f km", dist_to_mars / 1000.0);
INFO("Mars SOI radius: %.2f km", sim->bodies[MARS_IDX].soi_radius / 1000.0);
REQUIRE(dist_to_mars < 2.0 * sim->bodies[MARS_IDX].soi_radius);
} else {
INFO("Spacecraft did not enter Mars SOI within simulation time");
INFO("This may be due to phase angle or timing inaccuracies");
}
destroy_simulation(sim);
}
Phase 4: Build and Test
Step 4.1: Update Makefile (if needed)
Verify mission_planning.o is in OBJECTS list and build rule exists.
Step 4.2: Build test executable
make clean
make test-build
Step 4.3: Run comprehensive test
./orbit_test -s 'Earth → Mars Hohmann Transfer with LEO Spacecraft'
Step 4.4: Verify all tests still pass
make test
Phase 5: Cleanup and Documentation
Step 5.1: Remove deprecated function
Remove spawn_spacecraft_on_transfer() from:
src/mission_planning.hsrc/mission_planning.cpp
Step 5.2: Update mission planning documentation
Update docs/mission_planning.md:
- Mark Phase 4 as complete
- Note config-based approach implemented
- Document patched conics impulse burn
- Remove spawn_spacecraft_on_transfer references
Step 5.3: Add TODO comment for config format
Add in docs/mission_planning.md:
TODO: Future config file 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
Summary of Changes
New Files/Functions Added
initialize_spacecraft_leo()- Initialize spacecraft in LEOapply_transfer_burn()- Apply patched conics impulse burncalculate_phase_angle()- Calculate phase angle between bodies- Comprehensive test case with SOI transition tracking
Files Modified
tests/configs/earth_mars_simple.toml- Add spacecraft bodysrc/mission_planning.h- Add function declarationssrc/mission_planning.cpp- Implement new functionstests/test_hohmann_transfer.cpp- Add comprehensive test
Functions Removed
spawn_spacecraft_on_transfer()- Still present in code but no longer used
Current Issue Identified
Problem: Incorrect Delta-V Direction After Multi-Day Wait
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:
- Required heliocentric transfer velocity magnitude:
v_transfer = 32,697 m/s - Prograde direction based on Earth's current position:
transfer_dir = prograde(t_current) - 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:
- Calculate spacecraft's current heliocentric velocity vector:
v_current - Calculate required heliocentric velocity for transfer orbit:
v_transfer - Apply delta-v:
Δv = v_transfer - v_current(vector subtraction, not magnitude-based)
What Currently Happens:
- Assumes spacecraft starts at Earth's orbital position (ignores LEO phase)
- Calculates transfer direction based on Earth's current prograde vector
- Applies magnitude-based delta-v without considering spacecraft's actual velocity direction
- Results in incorrect burn direction
Solution Required
Modify apply_transfer_burn() to:
- Calculate spacecraft's actual heliocentric velocity:
Vec3 v_current_helio = spacecraft->velocity; // Already in global frame
- Calculate required heliocentric transfer velocity:
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);
- Calculate delta-v as vector difference:
Vec3 delta_v = vec3_sub(v_transfer_helio, v_current_helio);
- Apply impulse:
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
[[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 = "Spacecraft"
mass = 1.0
radius = 1000.0
# Position and velocity will be initialized at runtime for LEO orbit
position = { x = 0.0, y = 0.0, z = 0.0 }
velocity = { x = 0.0, y = 0.0, z = 0.0 }
parent_index = 1 # Earth
color = { r = 1.0, g = 0.0, b = 0.5 }
eccentricity = 0.0
# Semi-major axis will be: Earth radius + 200km
semi_major_axis = 6.571e6 # Placeholder, will be set during initialization
Future Work (Post-Implementation)
Immediate Next Steps
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)
Visualization Features
5. 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
6. Multiple Burns Support
- Mid-course corrections
- Gravity assist maneuvers
- Powered flybys
- Multi-stage missions
7. SOI Visualization
- Render SOI boundaries as wireframe spheres
- Color-coded by mass
- Toggle with keyboard shortcut
- Show SOI transitions in real-time
Advanced Features
8. 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
9. 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
10. 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)