Browse Source

use interpolated maneuvers with time trigger

this still has a bug, but we're committing here along with the testing
changes before attempt to fix
main
cinnaboot 3 months ago
parent
commit
7b9a557662
  1. 30
      src/maneuver.cpp
  2. 3
      src/simulation.cpp
  3. 48
      src/test_utilities.cpp
  4. 7
      src/test_utilities.h
  5. 37
      tests/test_rendezvous.cpp

30
src/maneuver.cpp

@ -115,8 +115,34 @@ OrbitalElements preview_burn_result(const Spacecraft* craft, BurnDirection direc
// TODO: add parabolic (Barker's equation) and hyperbolic branches. // TODO: add parabolic (Barker's equation) and hyperbolic branches.
bool check_maneuver_trigger(Maneuver* maneuver, Spacecraft* craft, SimulationState* sim) { bool check_maneuver_trigger(Maneuver* maneuver, Spacecraft* craft, SimulationState* sim) {
switch (maneuver->trigger_type) { switch (maneuver->trigger_type) {
case TRIGGER_TIME: case TRIGGER_TIME: {
return sim->time >= maneuver->trigger_value; // Fire at the step that contains the trigger time.
// The orbit state is at sim->time (start of current step).
// We propagate forward to trigger_value, burn, then propagate
// the remaining time to reach sim->time + sim->dt.
if (sim->time > maneuver->trigger_value) {
// Trigger is before the start of this step — clamp to 0
// (should have fired in an earlier step; fire immediately)
maneuver->scheduled_dt = 0.0;
return true;
}
if (sim->time + sim->dt <= maneuver->trigger_value) {
return false;
}
double dt_to_burn = maneuver->trigger_value - sim->time;
// Clamp to valid range [0, sim->dt]
if (dt_to_burn < 0.0) {
dt_to_burn = 0.0;
}
if (dt_to_burn > sim->dt) {
dt_to_burn = sim->dt;
}
maneuver->scheduled_dt = dt_to_burn;
return true;
}
case TRIGGER_TRUE_ANOMALY: { case TRIGGER_TRUE_ANOMALY: {
if (craft->parent_index < 0 || craft->parent_index >= sim->body_count) { if (craft->parent_index < 0 || craft->parent_index >= sim->body_count) {

3
src/simulation.cpp

@ -329,7 +329,8 @@ void update_spacecraft_physics(SimulationState* sim) {
craft->orbit = propagate_orbital_elements(craft->orbit, burn_dt, parent->mass); craft->orbit = propagate_orbital_elements(craft->orbit, burn_dt, parent->mass);
orbital_elements_to_cartesian(craft->orbit, parent->mass, &craft->local_position, &craft->local_velocity); orbital_elements_to_cartesian(craft->orbit, parent->mass, &craft->local_position, &craft->local_velocity);
execute_maneuver(fired_maneuver, craft, sim, sim->time + burn_dt); double burn_time = sim->time + burn_dt;
execute_maneuver(fired_maneuver, craft, sim, burn_time);
double remaining_dt = sim->dt - burn_dt; double remaining_dt = sim->dt - burn_dt;
craft->orbit = propagate_orbital_elements(craft->orbit, remaining_dt, parent->mass); craft->orbit = propagate_orbital_elements(craft->orbit, remaining_dt, parent->mass);

48
src/test_utilities.cpp

@ -166,16 +166,23 @@ bool compare_vec3(Vec3 a, Vec3 b, double tolerance) {
fabs(a.z - b.z) <= tolerance; fabs(a.z - b.z) <= tolerance;
} }
void dump_simulation_state(SimulationState* sim, const char* label) { int dump_simulation_state(SimulationState* sim, const char* label,
printf("\n=== %s (t=%.0f s) ===\n", label, sim->time); char* buffer, int buffer_size) {
int offset = 0;
printf("Bodies (%d):\n", sim->body_count); offset += snprintf(buffer + offset, buffer_size - offset,
"\n=== %s (t=%.0f s) ===\n", label, sim->time);
offset += snprintf(buffer + offset, buffer_size - offset,
"Bodies (%d):\n", sim->body_count);
for (int i = 0; i < sim->body_count; i++) { for (int i = 0; i < sim->body_count; i++) {
printf(" [%d] %s: mass=%.2e kg\n", offset += snprintf(buffer + offset, buffer_size - offset,
i, sim->bodies[i].name, sim->bodies[i].mass); " [%d] %s: mass=%.2e kg\n",
i, sim->bodies[i].name, sim->bodies[i].mass);
} }
printf("Spacecraft (%d):\n", sim->craft_count); offset += snprintf(buffer + offset, buffer_size - offset,
"Spacecraft (%d):\n", sim->craft_count);
for (int i = 0; i < sim->craft_count; i++) { for (int i = 0; i < sim->craft_count; i++) {
Spacecraft* s = &sim->spacecraft[i]; Spacecraft* s = &sim->spacecraft[i];
double r = sqrt(s->local_position.x*s->local_position.x + double r = sqrt(s->local_position.x*s->local_position.x +
@ -184,19 +191,28 @@ void dump_simulation_state(SimulationState* sim, const char* label) {
double v = sqrt(s->local_velocity.x*s->local_velocity.x + double v = sqrt(s->local_velocity.x*s->local_velocity.x +
s->local_velocity.y*s->local_velocity.y + s->local_velocity.y*s->local_velocity.y +
s->local_velocity.z*s->local_velocity.z); s->local_velocity.z*s->local_velocity.z);
printf(" [%d] %s: r=%.1f v=%.1f nu=%.5f a=%.1f e=%.6f\n", offset += snprintf(buffer + offset, buffer_size - offset,
i, s->name, r, v, " [%d] %s: r=%.1f v=%.1f nu=%.5f a=%.1f e=%.6f, omega=%.6f\n",
s->orbit.true_anomaly, s->orbit.semi_major_axis, s->orbit.eccentricity); i, s->name, r, v,
printf(" pos=(%.1f, %.1f, %.1f) vel=(%.1f, %.1f, %.1f)\n", s->orbit.true_anomaly,
s->local_position.x, s->local_position.y, s->local_position.z, s->orbit.semi_major_axis,
s->local_velocity.x, s->local_velocity.y, s->local_velocity.z); s->orbit.eccentricity,
s->orbit.argument_of_periapsis);
offset += snprintf(buffer + offset, buffer_size - offset,
" pos=(%.1f, %.1f, %.1f) vel=(%.1f, %.1f, %.1f)\n",
s->local_position.x, s->local_position.y, s->local_position.z,
s->local_velocity.x, s->local_velocity.y, s->local_velocity.z);
} }
printf("Maneuvers (%d):\n", sim->maneuver_count); offset += snprintf(buffer + offset, buffer_size - offset,
"Maneuvers (%d):\n", sim->maneuver_count);
for (int i = 0; i < sim->maneuver_count; i++) { for (int i = 0; i < sim->maneuver_count; i++) {
Maneuver* m = &sim->maneuvers[i]; Maneuver* m = &sim->maneuvers[i];
printf(" [%d] %s: craft=%d dir=%d dv=%.4f trigger=%d val=%.2f exec=%d\n", offset += snprintf(buffer + offset, buffer_size - offset,
i, m->name, m->craft_index, m->direction, m->delta_v, " [%d] %s: craft=%d dir=%d dv=%.4f trigger=%d val=%.2f exec=%d\n",
m->trigger_type, m->trigger_value, m->executed); i, m->name, m->craft_index, m->direction, m->delta_v,
m->trigger_type, m->trigger_value, m->executed);
} }
return offset;
} }

7
src/test_utilities.h

@ -46,7 +46,10 @@ void destroy_orbit_tracker(OrbitTracker* tracker);
bool compare_double(double a, double b, double tolerance); bool compare_double(double a, double b, double tolerance);
bool compare_vec3(Vec3 a, Vec3 b, double tolerance); bool compare_vec3(Vec3 a, Vec3 b, double tolerance);
// Debug helper: dump simulation state to console // Write simulation state to a caller-allocated buffer.
void dump_simulation_state(SimulationState* sim, const char* label); // Returns number of characters written (excluding null terminator).
// Caller must ensure buffer is large enough.
int dump_simulation_state(SimulationState* sim, const char* label,
char* buffer, int buffer_size);
#endif #endif

37
tests/test_rendezvous.cpp

@ -27,7 +27,9 @@ static int find_spacecraft_by_name(SimulationState* sim, const char* name) {
return -1; return -1;
} }
// ── Test-only output helper ────────────────────────────────────────────────── // ============================================================================
// Test-only output helper
// ============================================================================
struct TestOutput { struct TestOutput {
char buf[32768]; char buf[32768];
@ -36,6 +38,34 @@ struct TestOutput {
void dump_state(SimulationState* sim, const char* label) { void dump_state(SimulationState* sim, const char* label) {
int n = dump_simulation_state(sim, label, buf + offset, sizeof(buf) - offset); int n = dump_simulation_state(sim, label, buf + offset, sizeof(buf) - offset);
if (n > 0) offset += n; if (n > 0) offset += n;
int target_idx = -1, chaser_idx = -1;
for (int i = 0; i < sim->craft_count; i++) {
if (strcmp(sim->spacecraft[i].name, "Target_Satellite") == 0)
target_idx = i;
if (strcmp(sim->spacecraft[i].name, "Chaser_Lower") == 0)
chaser_idx = i;
}
if (target_idx >= 0 && chaser_idx >= 0) {
Vec3 target_pos = sim->spacecraft[target_idx].local_position;
Vec3 chaser_pos = sim->spacecraft[chaser_idx].local_position;
double target_angle = atan2(target_pos.y, target_pos.x);
double chaser_angle = atan2(chaser_pos.y, chaser_pos.x);
double angular_sep = chaser_angle - target_angle;
while (angular_sep > M_PI) angular_sep -= 2.0 * M_PI;
while (angular_sep < -M_PI) angular_sep += 2.0 * M_PI;
Vec3 diff = vec3_sub(chaser_pos, target_pos);
double sep_mag = vec3_magnitude(diff);
n = snprintf(buf + offset, sizeof(buf) - offset,
" Angular separation (Chaser-Target): %.6f rad (%.4f deg)\n"
" Separation magnitude: %.2f m\n",
angular_sep, angular_sep * 180.0 / M_PI, sep_mag);
if (n > 0) offset += n;
}
} }
}; };
@ -535,7 +565,7 @@ SCENARIO("Hohmann transfer rendezvous with validation", "[rendezvous_hohmann][in
if (i == 0) out.dump_state(sim, "T=0 (initial)"); if (i == 0) out.dump_state(sim, "T=0 (initial)");
if (i == static_cast<int>(wait_time / sim->dt)) out.dump_state(sim, "JUST BEFORE DEPARTURE"); if (i == static_cast<int>(wait_time / sim->dt)) out.dump_state(sim, "JUST BEFORE DEPARTURE");
if (i == static_cast<int>(wait_time / sim->dt) + 1) out.dump_state(sim, "AFTER DEPARTURE BURN"); if (i == static_cast<int>(wait_time / sim->dt) + 1) out.dump_state(sim, "AFTER DEPARTURE BURN");
if (i == static_cast<int>(arrival_time / sim->dt)) out.dump_state(sim, "JUST BEFORE ARRIVAL"); if (i == static_cast<int>(arrival_time / sim->dt) - 1) out.dump_state(sim, "JUST BEFORE ARRIVAL BURN");
if (sim->maneuvers[arr_idx].executed && !transfer_complete) { if (sim->maneuvers[arr_idx].executed && !transfer_complete) {
out.dump_state(sim, "AFTER ARRIVAL BURN"); out.dump_state(sim, "AFTER ARRIVAL BURN");
transfer_complete = true; transfer_complete = true;
@ -543,6 +573,8 @@ SCENARIO("Hohmann transfer rendezvous with validation", "[rendezvous_hohmann][in
} }
} }
INFO(out.buf);
// Verify rendezvous quality // Verify rendezvous quality
double final_radius = vec3_magnitude(chaser->local_position); double final_radius = vec3_magnitude(chaser->local_position);
double radius_error = fabs(final_radius - r2); double radius_error = fabs(final_radius - r2);
@ -566,7 +598,6 @@ SCENARIO("Hohmann transfer rendezvous with validation", "[rendezvous_hohmann][in
INFO(" Target speed: " << target_speed << " m/s"); INFO(" Target speed: " << target_speed << " m/s");
INFO(" Separation: " << separation_distance << " m"); INFO(" Separation: " << separation_distance << " m");
INFO(" Relative velocity: " << relative_velocity << " m/s"); INFO(" Relative velocity: " << relative_velocity << " m/s");
INFO(out.buf);
// Verify maneuvers executed // Verify maneuvers executed
REQUIRE(sim->maneuvers[dep_idx].executed); REQUIRE(sim->maneuvers[dep_idx].executed);

Loading…
Cancel
Save