From 51511a1a4b38ee40e9c494493de04e25226fe133 Mon Sep 17 00:00:00 2001 From: cinnaboot Date: Mon, 19 Jan 2026 10:50:02 -0500 Subject: [PATCH] Condense mission planning documentation - Replace full function implementations with summaries - Add references to actual source files for implementation details - Replace full test code with test structure summary - Replace full TOML config with config summary - Reduced document from 744 to 459 lines (38% reduction) - Improved maintainability: source code is now source of truth --- docs/mission_planning.md | 451 +++++++-------------------------------- 1 file changed, 83 insertions(+), 368 deletions(-) diff --git a/docs/mission_planning.md b/docs/mission_planning.md index f220189..54e146e 100644 --- a/docs/mission_planning.md +++ b/docs/mission_planning.md @@ -36,27 +36,20 @@ git stash pop # Apply debug changes ### Step 1.1: Add spacecraft to `tests/configs/earth_mars_simple.toml` -Append to config file: -```toml -[[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 -``` +Add Spacecraft body to config with placeholder position/velocity (set at runtime by `initialize_spacecraft_leo()`). + +**Implementation:** See `tests/configs/earth_mars_simple.toml` for full config -**Note**: Position/velocity are placeholders; will be calculated by `initialize_spacecraft_leo()` at runtime. +**Key parameters:** +- mass = 1.0 kg (test particle) +- radius = 1000.0 m +- parent_index = 1 (Earth) +- color = magenta (r=1.0, g=0.0, b=0.5) +- position/velocity: Placeholders (0,0,0) -**TODO**: Future config file format should support: -- Earth-relative position (e.g., `{ altitude_km = 200.0 }`) -- Earth-relative velocity (e.g., `{ orbit_type = "circular" }`) +**TODO**: Future config format should support: +- Earth-relative position: `{ altitude_km = 200.0 }` +- Earth-relative orbit: `{ orbit_type = "circular" }` - More intuitive spacecraft mission parameters --- @@ -65,334 +58,86 @@ semi_major_axis = 6.571e6 # Placeholder, will be set during initialization ### Step 2.1: Add function declarations to `src/mission_planning.h` -```cpp -// Initialize spacecraft in circular LEO around parent body -void initialize_spacecraft_leo(CelestialBody* spacecraft, CelestialBody* parent, - double altitude_m); +**Implementation:** See `src/mission_planning.h` -// 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); -``` +**Functions:** +- `initialize_spacecraft_leo()` - Initialize spacecraft in circular LEO around parent body +- `apply_transfer_burn()` - Apply patched conics impulse burn for Hohmann transfer +- `calculate_phase_angle()` - Calculate current phase angle between two bodies (in degrees) ### Step 2.2: Implement `initialize_spacecraft_leo()` in `src/mission_planning.cpp` -**Algorithm**: -```cpp -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); +**Implementation:** `src/mission_planning.cpp:20-56` - // Position: Earth position + offset radially outward - Vec3 offset = vec3_scale(direction, orbital_radius); - spacecraft->position = vec3_add(parent->position, offset); +**Algorithm:** +- Calculate orbital radius = parent radius + altitude +- Position spacecraft radially outward from Sun (any angular position acceptable) +- Calculate circular LEO velocity: v = sqrt(G * M_parent / r) +- Set prograde orientation (tangential to Earth-Sun line) +- Set both local and global coordinates correctly - // 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 +**Key Points:** +- LEO orbit is circular at 200km altitude (~7,788 m/s) +- Spacecraft velocity = Earth velocity + LEO velocity +- Local velocity = LEO velocity only (relative to Earth) ### Step 2.3: Implement `calculate_phase_angle()` in `src/mission_planning.cpp` -**Algorithm**: -```cpp -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; -} -``` +**Implementation:** `src/mission_planning.cpp:58-78` + +**Algorithm:** +- Calculate angular positions of departure and arrival bodies relative to Sun +- Compute phase difference: θ_arrival - θ_departure +- Normalize to [0°, 360°) range +- Return phase angle in degrees ### Step 2.4: Implement `apply_transfer_burn()` in `src/mission_planning.cpp` -**Algorithm (Patched Conics Approach)**: -```cpp -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); -} -``` +**Implementation:** `src/mission_planning.cpp:80-116` + +**Algorithm (Patched Conics Approach):** +- Calculate required heliocentric transfer velocity magnitude from params +- Determine prograde direction (tangential to departure-Sun line) +- Compute delta-v: Δv = v_transfer - v_current (vector subtraction) +- Apply impulse to spacecraft velocity +- Update local velocity relative to departure body +- Print burn information for debugging -**Note**: This is a simplified single-impulse approach. A true patched conics calculation would: -1. Calculate Δv to reach SOI boundary (escape trajectory) +**Note:** Simplified single-impulse approximation. True patched conics would: +1. Calculate Δv to reach SOI boundary 2. Calculate velocity at SOI boundary 3. Add transfer Δv at SOI boundary 4. 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` -```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); -} -``` +**Implementation:** `tests/test_hohmann_transfer.cpp` - See file for full test + +**Test:** "Earth → Mars Hohmann Transfer with LEO Spacecraft" + +**Test Structure:** +1. Load config with 4 bodies (Sun, Earth, Mars, Spacecraft) +2. Initialize spacecraft in 200km LEO around Earth +3. Verify LEO orbit stability (parent, position, velocity, energy) +4. Calculate Hohmann transfer parameters +5. Wait for Earth-Mars launch window (within 1° tolerance) +6. Verify phase angle accuracy +7. Apply impulse burn for transfer +8. Verify post-burn energy >= 0 (escape trajectory) +9. Simulate transfer for 110% of expected duration +10. Track SOI transitions (Earth→Sun→Mars) +11. Verify final parent and energy conservation (<5% drift) +12. If Mars SOI entry, verify distance (<2×SOI) + +**Key Assertions:** +- Config loading: 4 bodies loaded, spacecraft present +- LEO stability: parent=Earth, position <1km error, velocity <10m/s error, energy <0 +- Launch window: opens in ~94 days, phase error <1° +- Transfer: post-burn energy >= 0, Earth→Sun SOI transition, energy conservation --- @@ -578,50 +323,20 @@ Spacecraft may not enter Mars SOI due to: ## Test Configuration Reference ### earth_mars_simple.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 = "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 -``` +**Implementation:** `tests/configs/earth_mars_simple.toml` + +**Bodies:** +- Sun (index 0): Root body, 1.989e30 kg +- Earth (index 1): 5.972e24 kg, 1.496e11 m from Sun +- Mars (index 2): 6.39e23 kg, 2.279e11 m from Sun +- Spacecraft (index 3): 1.0 kg, parent=Earth (position/velocity set at runtime) + +**Spacecraft parameters:** +- mass = 1.0 kg +- radius = 1000.0 m +- parent_index = 1 (Earth) +- color = magenta (r=1.0, g=0.0, b=0.5) +- position/velocity: Placeholders (0,0,0) - set by `initialize_spacecraft_leo()` ---