Browse Source

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
main
cinnaboot 6 months ago
parent
commit
51511a1a4b
  1. 449
      docs/mission_planning.md

449
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` ### Step 1.1: Add spacecraft to `tests/configs/earth_mars_simple.toml`
Append to config file: Add Spacecraft body to config with placeholder position/velocity (set at runtime by `initialize_spacecraft_leo()`).
```toml
[[bodies]] **Implementation:** See `tests/configs/earth_mars_simple.toml` for full config
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. **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: **TODO**: Future config format should support:
- Earth-relative position (e.g., `{ altitude_km = 200.0 }`) - Earth-relative position: `{ altitude_km = 200.0 }`
- Earth-relative velocity (e.g., `{ orbit_type = "circular" }`) - Earth-relative orbit: `{ orbit_type = "circular" }`
- More intuitive spacecraft mission parameters - 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` ### Step 2.1: Add function declarations to `src/mission_planning.h`
```cpp **Implementation:** See `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 **Functions:**
void apply_transfer_burn(SimulationState* sim, int spacecraft_idx, - `initialize_spacecraft_leo()` - Initialize spacecraft in circular LEO around parent body
int departure_idx, TransferParameters* params); - `apply_transfer_burn()` - Apply patched conics impulse burn for Hohmann transfer
- `calculate_phase_angle()` - Calculate current phase angle between two bodies (in degrees)
// 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` ### Step 2.2: Implement `initialize_spacecraft_leo()` in `src/mission_planning.cpp`
**Algorithm**: **Implementation:** `src/mission_planning.cpp:20-56`
```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);
// 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 **Algorithm:**
double v_leo = sqrt(G * parent->mass / orbital_radius); - 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
// Direction: tangential to Earth-Sun line (prograde) **Key Points:**
// If sun_to_earth = (x, y, 0), then tangent = (-y, x, 0) - LEO orbit is circular at 200km altitude (~7,788 m/s)
Vec3 leo_tangent = (Vec3){-direction.y, direction.x, 0.0}; - Spacecraft velocity = Earth velocity + LEO velocity
Vec3 leo_velocity = vec3_scale(leo_tangent, v_leo); - Local velocity = LEO velocity only (relative to Earth)
// 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` ### Step 2.3: Implement `calculate_phase_angle()` in `src/mission_planning.cpp`
**Algorithm**: **Implementation:** `src/mission_planning.cpp:58-78`
```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;
}
```
### 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 **Algorithm:**
Vec3 delta_v = vec3_sub(v_transfer_helio, current_helio); - 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
// Apply impulse burn ### Step 2.4: Implement `apply_transfer_burn()` in `src/mission_planning.cpp`
spacecraft->velocity = vec3_add(spacecraft->velocity, delta_v);
// Update local velocity **Implementation:** `src/mission_planning.cpp:80-116`
spacecraft->local_velocity = vec3_sub(spacecraft->velocity, departure->velocity);
// Print burn information **Algorithm (Patched Conics Approach):**
printf("Transfer burn applied:\n"); - Calculate required heliocentric transfer velocity magnitude from params
printf(" Current heliocentric velocity: (%.2f, %.2f, %.2f) m/s\n", - Determine prograde direction (tangential to departure-Sun line)
current_helio.x, current_helio.y, current_helio.z); - Compute delta-v: Δv = v_transfer - v_current (vector subtraction)
printf(" Target heliocentric velocity: (%.2f, %.2f, %.2f) m/s\n", - Apply impulse to spacecraft velocity
v_transfer_helio.x, v_transfer_helio.y, v_transfer_helio.z); - Update local velocity relative to departure body
printf(" Delta-v: (%.2f, %.2f, %.2f) m/s\n", - Print burn information for debugging
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: **Note:** Simplified single-impulse approximation. True patched conics would:
1. Calculate Δv to reach SOI boundary (escape trajectory) 1. Calculate Δv to reach SOI boundary
2. Calculate velocity at SOI boundary 2. Calculate velocity at SOI boundary
3. Add transfer Δv at SOI boundary 3. Add transfer Δv at SOI boundary
4. Combine into equivalent single impulse 4. Combine into equivalent single impulse
For initial implementation, we'll use single impulse as approximation.
--- ---
## Phase 3: Comprehensive Test Case ## Phase 3: Comprehensive Test Case
### Step 3.1: Create new test in `tests/test_hohmann_transfer.cpp` ### Step 3.1: Create new test in `tests/test_hohmann_transfer.cpp`
```cpp **Implementation:** `tests/test_hohmann_transfer.cpp` - See file for full test
TEST_CASE("Earth → Mars Hohmann Transfer with LEO Spacecraft", "[mission][hohmann][config][integration]") {
const double TIME_STEP = 60.0; **Test:** "Earth → Mars Hohmann Transfer with LEO Spacecraft"
const double SECONDS_PER_DAY = 86400.0;
const double LEO_ALTITUDE_M = 200000.0; // 200 km **Test Structure:**
1. Load config with 4 bodies (Sun, Earth, Mars, Spacecraft)
// 1. Load config with LEO spacecraft 2. Initialize spacecraft in 200km LEO around Earth
SimulationState* sim = create_simulation(4, TIME_STEP); 3. Verify LEO orbit stability (parent, position, velocity, energy)
REQUIRE(load_system_config(sim, "tests/configs/earth_mars_simple.toml")); 4. Calculate Hohmann transfer parameters
5. Wait for Earth-Mars launch window (within 1° tolerance)
const int SUN_IDX = 0; 6. Verify phase angle accuracy
const int EARTH_IDX = 1; 7. Apply impulse burn for transfer
const int MARS_IDX = 2; 8. Verify post-burn energy >= 0 (escape trajectory)
const int CRAFT_IDX = 3; 9. Simulate transfer for 110% of expected duration
10. Track SOI transitions (Earth→Sun→Mars)
// Verify spacecraft loaded 11. Verify final parent and energy conservation (<5% drift)
REQUIRE(sim->body_count == 4); 12. If Mars SOI entry, verify distance (<2×SOI)
REQUIRE(strcmp(sim->bodies[CRAFT_IDX].name, "Spacecraft") == 0);
**Key Assertions:**
// 2. Initialize spacecraft LEO orbit - Config loading: 4 bodies loaded, spacecraft present
initialize_spacecraft_leo(&sim->bodies[CRAFT_IDX], &sim->bodies[EARTH_IDX], - LEO stability: parent=Earth, position <1km error, velocity <10m/s error, energy <0
LEO_ALTITUDE_M); - Launch window: opens in ~94 days, phase error <1°
- Transfer: post-burn energy >= 0, Earth→Sun SOI transition, energy conservation
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, &params);
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);
}
```
--- ---
@ -578,50 +323,20 @@ Spacecraft may not enter Mars SOI due to:
## Test Configuration Reference ## Test Configuration Reference
### earth_mars_simple.toml ### earth_mars_simple.toml
```toml **Implementation:** `tests/configs/earth_mars_simple.toml`
[[bodies]]
name = "Sun" **Bodies:**
mass = 1.989e30 - Sun (index 0): Root body, 1.989e30 kg
radius = 6.96e8 - Earth (index 1): 5.972e24 kg, 1.496e11 m from Sun
position = { x = 0.0, y = 0.0, z = 0.0 } - Mars (index 2): 6.39e23 kg, 2.279e11 m from Sun
parent_index = -1 - Spacecraft (index 3): 1.0 kg, parent=Earth (position/velocity set at runtime)
color = { r = 1.0, g = 1.0, b = 0.0 }
eccentricity = 0.0 **Spacecraft parameters:**
semi_major_axis = 0.0 - mass = 1.0 kg
- radius = 1000.0 m
[[bodies]] - parent_index = 1 (Earth)
name = "Earth" - color = magenta (r=1.0, g=0.0, b=0.5)
mass = 5.972e24 - position/velocity: Placeholders (0,0,0) - set by `initialize_spacecraft_leo()`
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
```
--- ---

Loading…
Cancel
Save