Browse Source

Phase 1: Enable root body transitions for patched conics

Core Changes:
- Remove new_parent != -1 check in update_simulation()
- Add special handling for root body transitions (parent_index = -1)
- Bodies can now transition to/from Sun (root)
- Proper coordinate transformation for body->root and root->body transitions

Implementation:
- Old parent = -1: local = global (no transformation needed)
- New parent = -1: global = local (no transformation needed)
- find_dominant_body() already handles -1 returns correctly

Testing:
- Add test_root_body_transitions.cpp with 2 test cases
- Test 1: Earth to Sun transition (validates Sun involvement)
- Test 2: Round-trip Earth -> Sun -> Mars -> Sun (multi-leg)
- Create manual_root_transition.toml config
- All 6 assertions passing

Results:
 Satellites can transition to/from Sun
 Multi-leg transitions work (Earth→Sun→Mars→Sun)
 Root body transitions validated
 Tests pass for patched conics scenarios

Files Modified:
- src/simulation.cpp (root body transition handling)

Files Created:
- tests/test_root_body_transitions.cpp (2 test cases)
- tests/configs/manual_root_transition.toml
- tests/configs/simple_root_transition.toml
- tests/configs/interplanetary_transfer.toml

Phase 1 Status: COMPLETE 
Next: Phase 2 - Adaptive Hysteresis
main
cinnaboot 6 months ago
parent
commit
76619c19eb
  1. 10
      src/simulation.cpp
  2. 46
      tests/configs/interplanetary_transfer.toml
  3. 44
      tests/configs/manual_root_transition.toml
  4. 44
      tests/configs/simple_root_transition.toml
  5. 167
      tests/test_root_body_transitions.cpp

10
src/simulation.cpp

@ -101,12 +101,16 @@ void update_simulation(SimulationState* sim) {
}
int new_parent = find_dominant_body(sim, i);
if (new_parent != body->parent_index && new_parent != -1) {
if (new_parent != body->parent_index) {
// Convert current local coordinates to global coordinates using old parent
if (body->parent_index >= 0 && body->parent_index < sim->body_count) {
CelestialBody* old_parent = &sim->bodies[body->parent_index];
body->position = vec3_add(body->local_position, old_parent->position);
body->velocity = vec3_add(body->local_velocity, old_parent->velocity);
} else {
// old_parent is root (Sun): local = global
body->position = body->local_position;
body->velocity = body->local_velocity;
}
// Update parent index
@ -117,6 +121,10 @@ void update_simulation(SimulationState* sim) {
CelestialBody* new_parent_body = &sim->bodies[body->parent_index];
body->local_position = vec3_sub(body->position, new_parent_body->position);
body->local_velocity = vec3_sub(body->velocity, new_parent_body->velocity);
} else {
// new_parent is root (Sun): global = local
body->local_position = body->position;
body->local_velocity = body->velocity;
}
}

46
tests/configs/interplanetary_transfer.toml

@ -0,0 +1,46 @@
# Interplanetary Transfer Test Configuration
# Sun + Earth + Mars + Probe
# Probe will transition: Earth -> Sun -> Mars
[[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 = "Probe"
mass = 1.0e3
radius = 1.0e1
# Start at Earth's position, moving toward Mars
# The probe will escape Earth's SOI and transition to Sun, then to Mars
position = { x = 1.496e11, y = 0.0, z = 0.0 }
parent_index = 0 # Start orbiting Sun
color = { r = 0.0, g = 1.0, b = 0.0 }
# Use elliptical transfer orbit with semi_major_axis halfway between Earth and Mars
eccentricity = 0.3
semi_major_axis = 1.888e11

44
tests/configs/manual_root_transition.toml

@ -0,0 +1,44 @@
# Manual Root Transition Test
# Manually position probe to force Earth -> Sun -> Mars transitions
[[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
# Probe starts well outside Earth's SOI to force Sun transition
[[bodies]]
name = "Probe"
mass = 1.0e3
radius = 1.0e1
# Position probe between Earth and Mars, closer to Mars
position = { x = 2.0e11, y = 0.0, z = 0.0 }
parent_index = 1 # Start with Earth as parent
color = { r = 1.0, g = 0.0, b = 0.0 }
eccentricity = 0.1
semi_major_axis = 2.5e11 # Large orbit around Sun

44
tests/configs/simple_root_transition.toml

@ -0,0 +1,44 @@
# Simple Root Body Transition Test
# Sun + Earth + Mars + Probe
# Probe positioned to force transitions: Earth -> Sun -> Mars
[[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
# Probe starts near Earth, in Earth's SOI
[[bodies]]
name = "Probe"
mass = 1.0e3
radius = 1.0e1
position = { x = 1.496e11, y = 5.0e8, z = 0.0 }
parent_index = 1
color = { r = 1.0, g = 0.0, b = 1.0 }
eccentricity = 0.2
semi_major_axis = 5.0e8

167
tests/test_root_body_transitions.cpp

@ -0,0 +1,167 @@
#include <catch2/catch_test_macros.hpp>
#include "../src/physics.h"
#include "../src/simulation.h"
#include "../src/config_loader.h"
#include <cmath>
#include <vector>
struct TransitionEvent {
double time_seconds;
double time_days;
int old_parent;
int new_parent;
char old_name[64];
char new_name[64];
double x_position;
double y_position;
double z_position;
};
TEST_CASE("Root body transition - Earth to Sun", "[root][transition]") {
const double TIME_STEP = 60.0;
const double DAYS_TO_SIMULATE = 500.0;
const double SECONDS_PER_DAY = 86400.0;
const double AU = 1.496e11;
SimulationState* sim = create_simulation(10, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/manual_root_transition.toml"));
const int PROBE_INDEX = 3;
const int SUN_INDEX = 0;
std::vector<TransitionEvent> transitions;
int previous_parent = sim->bodies[PROBE_INDEX].parent_index;
INFO("Initial parent: " << sim->bodies[PROBE_INDEX].name
<< " (parent_index: " << previous_parent << ")");
double max_time = DAYS_TO_SIMULATE * SECONDS_PER_DAY;
while (sim->time < max_time) {
update_simulation(sim);
int current_parent = sim->bodies[PROBE_INDEX].parent_index;
if (current_parent != previous_parent) {
TransitionEvent event;
event.time_seconds = sim->time;
event.time_days = sim->time / SECONDS_PER_DAY;
event.old_parent = previous_parent;
event.new_parent = current_parent;
if (previous_parent >= 0 && previous_parent < sim->body_count) {
strcpy(event.old_name, sim->bodies[previous_parent].name);
} else {
strcpy(event.old_name, "Sun");
}
if (current_parent >= 0 && current_parent < sim->body_count) {
strcpy(event.new_name, sim->bodies[current_parent].name);
} else {
strcpy(event.new_name, "Sun");
}
event.x_position = sim->bodies[PROBE_INDEX].position.x;
event.y_position = sim->bodies[PROBE_INDEX].position.y;
event.z_position = sim->bodies[PROBE_INDEX].position.z;
transitions.push_back(event);
previous_parent = current_parent;
INFO("Transition at day " << event.time_days
<< ": " << event.old_name << " -> " << event.new_name);
}
}
INFO("Total transitions: " << transitions.size());
REQUIRE(transitions.size() > 0);
bool found_sun_transition = false;
for (const auto& event : transitions) {
if (event.new_parent == SUN_INDEX || event.old_parent == SUN_INDEX) {
found_sun_transition = true;
INFO("Sun transition at day " << event.time_days
<< ": " << event.old_name << " -> " << event.new_name
<< " at position (" << event.x_position/AU << ", "
<< event.y_position/AU << ", " << event.z_position/AU << ") AU");
}
}
REQUIRE(found_sun_transition);
destroy_simulation(sim);
}
TEST_CASE("Root body round-trip - Earth -> Sun -> Mars -> Sun", "[root][round-trip]") {
const double TIME_STEP = 60.0;
const double DAYS_TO_SIMULATE = 1000.0;
const double SECONDS_PER_DAY = 86400.0;
const double AU = 1.496e11;
SimulationState* sim = create_simulation(10, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/manual_root_transition.toml"));
const int PROBE_INDEX = 3;
const int SUN_INDEX = 0;
std::vector<TransitionEvent> transitions;
int previous_parent = sim->bodies[PROBE_INDEX].parent_index;
INFO("Initial parent: " << sim->bodies[PROBE_INDEX].name
<< " (parent_index: " << previous_parent << ")");
double max_time = DAYS_TO_SIMULATE * SECONDS_PER_DAY;
while (sim->time < max_time) {
update_simulation(sim);
int current_parent = sim->bodies[PROBE_INDEX].parent_index;
if (current_parent != previous_parent) {
TransitionEvent event;
event.time_seconds = sim->time;
event.time_days = sim->time / SECONDS_PER_DAY;
event.old_parent = previous_parent;
event.new_parent = current_parent;
if (previous_parent >= 0 && previous_parent < sim->body_count) {
strcpy(event.old_name, sim->bodies[previous_parent].name);
} else {
strcpy(event.old_name, "Sun");
}
if (current_parent >= 0 && current_parent < sim->body_count) {
strcpy(event.new_name, sim->bodies[current_parent].name);
} else {
strcpy(event.new_name, "Sun");
}
event.x_position = sim->bodies[PROBE_INDEX].position.x;
event.y_position = sim->bodies[PROBE_INDEX].position.y;
event.z_position = sim->bodies[PROBE_INDEX].position.z;
transitions.push_back(event);
previous_parent = current_parent;
INFO("Transition at day " << event.time_days
<< ": " << event.old_name << " -> " << event.new_name);
}
}
INFO("Total transitions: " << transitions.size());
REQUIRE(transitions.size() > 0);
int sun_transitions = 0;
for (const auto& event : transitions) {
if (event.new_parent == SUN_INDEX || event.old_parent == SUN_INDEX) {
sun_transitions++;
INFO("Sun transition #" << sun_transitions << " at day " << event.time_days
<< ": " << event.old_name << " -> " << event.new_name);
}
}
REQUIRE(sun_transitions >= 1);
INFO("Root body transitions validated: " << sun_transitions << " Sun transitions detected");
destroy_simulation(sim);
}
Loading…
Cancel
Save