Browse Source

refactor: improve OrbitTracker accuracy and test_utilities code quality

- Fix orbit period detection: track accumulated signed rotation instead of
  wrapped delta, completing on true 2π revolution (was < 0.05 rad)
- Rename quadrant_transitions → wrap_count (accurate semantics)
- Add accumulated_rotation field to OrbitTracker struct
- Consolidate create_orbit_tracker into create_orbit_tracker_with_min_time
- Extract reset_tracker_fields() helper to avoid duplication
- Use vec3_dot() for kinetic energy, vec3_magnitude() for speed calculations
- Define MIN_DISTANCE_CLAMP and SECONDS_PER_DAY as file-level constants
- Add NULL checks in dump_simulation_state for spacecraft/maneuver pointers
test-refactor
cinnaboot 2 months ago
parent
commit
6edd30e759
  1. 112
      src/test_utilities.cpp
  2. 5
      src/test_utilities.h

112
src/test_utilities.cpp

@ -3,16 +3,18 @@
#include <cmath> #include <cmath>
#include <cstdio> #include <cstdio>
#define MIN_DISTANCE_CLAMP 1.0
static const double SECONDS_PER_DAY = 86400.0;
double calculate_kinetic_energy(CelestialBody* body) { double calculate_kinetic_energy(CelestialBody* body) {
double v_squared = body->global_velocity.x * body->global_velocity.x + Vec3 v = body->global_velocity;
body->global_velocity.y * body->global_velocity.y + return 0.5 * body->mass * vec3_dot(v, v);
body->global_velocity.z * body->global_velocity.z;
return 0.5 * body->mass * v_squared;
} }
double calculate_potential_energy_pair(CelestialBody* body1, CelestialBody* body2) { double calculate_potential_energy_pair(CelestialBody* body1, CelestialBody* body2) {
double distance = vec3_distance(body1->global_position, body2->global_position); double distance = vec3_distance(body1->global_position, body2->global_position);
if (distance < 1.0) distance = 1.0; if (distance < MIN_DISTANCE_CLAMP) distance = MIN_DISTANCE_CLAMP;
return -G * body1->mass * body2->mass / distance; return -G * body1->mass * body2->mass / distance;
} }
@ -46,28 +48,13 @@ OrbitalMetrics calculate_orbital_metrics(CelestialBody* body, CelestialBody* par
return metrics; return metrics;
} }
OrbitTracker* create_orbit_tracker(int body_index) {
OrbitTracker* tracker = (OrbitTracker*)malloc(sizeof(OrbitTracker));
tracker->body_index = body_index;
tracker->initial_angle = 0.0;
tracker->previous_angle = 0.0;
tracker->quadrant_transitions = 0;
tracker->orbit_completed = false;
tracker->time_at_completion = 0.0;
tracker->min_time_days = 100.0;
tracker->inclination = 0.0;
tracker->longitude_of_ascending_node = 0.0;
tracker->argument_of_periapsis = 0.0;
tracker->has_orbital_elements = false;
return tracker;
}
OrbitTracker* create_orbit_tracker_with_min_time(int body_index, double min_time_days) { OrbitTracker* create_orbit_tracker_with_min_time(int body_index, double min_time_days) {
OrbitTracker* tracker = (OrbitTracker*)malloc(sizeof(OrbitTracker)); OrbitTracker* tracker = (OrbitTracker*)malloc(sizeof(OrbitTracker));
tracker->body_index = body_index; tracker->body_index = body_index;
tracker->initial_angle = 0.0; tracker->initial_angle = 0.0;
tracker->previous_angle = 0.0; tracker->previous_angle = 0.0;
tracker->quadrant_transitions = 0; tracker->accumulated_rotation = 0.0;
tracker->wrap_count = 0;
tracker->orbit_completed = false; tracker->orbit_completed = false;
tracker->time_at_completion = 0.0; tracker->time_at_completion = 0.0;
tracker->min_time_days = min_time_days; tracker->min_time_days = min_time_days;
@ -78,6 +65,10 @@ OrbitTracker* create_orbit_tracker_with_min_time(int body_index, double min_time
return tracker; return tracker;
} }
OrbitTracker* create_orbit_tracker(int body_index) {
return create_orbit_tracker_with_min_time(body_index, 100.0);
}
OrbitTracker* create_orbit_tracker_3d(int body_index, double min_time_days, OrbitTracker* create_orbit_tracker_3d(int body_index, double min_time_days,
double inclination, double lon_ascending_node, double inclination, double lon_ascending_node,
double argument_of_periapsis) { double argument_of_periapsis) {
@ -89,14 +80,19 @@ OrbitTracker* create_orbit_tracker_3d(int body_index, double min_time_days,
return tracker; return tracker;
} }
void reset_orbit_tracker(OrbitTracker* tracker) { static void reset_tracker_fields(OrbitTracker* tracker) {
tracker->initial_angle = 0.0; tracker->initial_angle = 0.0;
tracker->previous_angle = 0.0; tracker->previous_angle = 0.0;
tracker->quadrant_transitions = 0; tracker->accumulated_rotation = 0.0;
tracker->wrap_count = 0;
tracker->orbit_completed = false; tracker->orbit_completed = false;
tracker->time_at_completion = 0.0; tracker->time_at_completion = 0.0;
} }
void reset_orbit_tracker(OrbitTracker* tracker) {
reset_tracker_fields(tracker);
}
void update_orbit_tracker(OrbitTracker* tracker, CelestialBody* body, CelestialBody* parent, double current_time) { void update_orbit_tracker(OrbitTracker* tracker, CelestialBody* body, CelestialBody* parent, double current_time) {
if (tracker->orbit_completed) return; if (tracker->orbit_completed) return;
@ -117,10 +113,11 @@ void update_orbit_tracker(OrbitTracker* tracker, CelestialBody* body, CelestialB
current_angle = atan2(relative_pos.y, relative_pos.x); current_angle = atan2(relative_pos.y, relative_pos.x);
} }
if (tracker->quadrant_transitions == 0) { if (tracker->wrap_count == 0) {
tracker->initial_angle = current_angle; tracker->initial_angle = current_angle;
tracker->previous_angle = current_angle; tracker->previous_angle = current_angle;
tracker->quadrant_transitions = 1; tracker->accumulated_rotation = 0.0;
tracker->wrap_count = 1;
return; return;
} }
@ -128,23 +125,20 @@ void update_orbit_tracker(OrbitTracker* tracker, CelestialBody* body, CelestialB
if (angle_diff > M_PI) { if (angle_diff > M_PI) {
angle_diff -= 2.0 * M_PI; angle_diff -= 2.0 * M_PI;
tracker->quadrant_transitions++; tracker->wrap_count++;
} }
if (angle_diff < -M_PI) { if (angle_diff < -M_PI) {
angle_diff += 2.0 * M_PI; angle_diff += 2.0 * M_PI;
tracker->quadrant_transitions++; tracker->wrap_count++;
} }
double total_rotation = current_angle - tracker->initial_angle; tracker->accumulated_rotation += angle_diff;
if (total_rotation < -M_PI) total_rotation += 2.0 * M_PI;
if (total_rotation > M_PI) total_rotation -= 2.0 * M_PI;
const double SECONDS_PER_DAY = 86400.0;
double min_time_seconds = tracker->min_time_days * SECONDS_PER_DAY; double min_time_seconds = tracker->min_time_days * SECONDS_PER_DAY;
if (tracker->quadrant_transitions >= 2 && if (tracker->wrap_count >= 2 &&
current_time > min_time_seconds && current_time > min_time_seconds &&
fabs(total_rotation) < 0.05) { tracker->accumulated_rotation >= 2.0 * M_PI) {
tracker->orbit_completed = true; tracker->orbit_completed = true;
tracker->time_at_completion = current_time; tracker->time_at_completion = current_time;
} }
@ -183,35 +177,35 @@ int dump_simulation_state(SimulationState* sim, const char* label,
offset += snprintf(buffer + offset, buffer_size - offset, offset += snprintf(buffer + offset, buffer_size - offset,
"Spacecraft (%d):\n", sim->craft_count); "Spacecraft (%d):\n", sim->craft_count);
for (int i = 0; i < sim->craft_count; i++) { if (sim->spacecraft) {
Spacecraft* s = &sim->spacecraft[i]; for (int i = 0; i < sim->craft_count; i++) {
double r = sqrt(s->local_position.x*s->local_position.x + Spacecraft* s = &sim->spacecraft[i];
s->local_position.y*s->local_position.y + double r = vec3_magnitude(s->local_position);
s->local_position.z*s->local_position.z); double v = vec3_magnitude(s->local_velocity);
double v = sqrt(s->local_velocity.x*s->local_velocity.x + offset += snprintf(buffer + offset, buffer_size - offset,
s->local_velocity.y*s->local_velocity.y + " [%d] %s: r=%.1f v=%.1f nu=%.5f a=%.1f e=%.6f, omega=%.6f\n",
s->local_velocity.z*s->local_velocity.z); i, s->name, r, v,
offset += snprintf(buffer + offset, buffer_size - offset, s->orbit.true_anomaly,
" [%d] %s: r=%.1f v=%.1f nu=%.5f a=%.1f e=%.6f, omega=%.6f\n", s->orbit.semi_major_axis,
i, s->name, r, v, s->orbit.eccentricity,
s->orbit.true_anomaly, s->orbit.argument_of_periapsis);
s->orbit.semi_major_axis, offset += snprintf(buffer + offset, buffer_size - offset,
s->orbit.eccentricity, " pos=(%.1f, %.1f, %.1f) vel=(%.1f, %.1f, %.1f)\n",
s->orbit.argument_of_periapsis); s->local_position.x, s->local_position.y, s->local_position.z,
offset += snprintf(buffer + offset, buffer_size - offset, s->local_velocity.x, s->local_velocity.y, s->local_velocity.z);
" 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);
} }
offset += snprintf(buffer + offset, buffer_size - offset, offset += snprintf(buffer + offset, buffer_size - offset,
"Maneuvers (%d):\n", sim->maneuver_count); "Maneuvers (%d):\n", sim->maneuver_count);
for (int i = 0; i < sim->maneuver_count; i++) { if (sim->maneuvers) {
Maneuver* m = &sim->maneuvers[i]; for (int i = 0; i < sim->maneuver_count; i++) {
offset += snprintf(buffer + offset, buffer_size - offset, Maneuver* m = &sim->maneuvers[i];
" [%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; return offset;

5
src/test_utilities.h

@ -16,7 +16,8 @@ struct OrbitalMetrics {
struct OrbitTracker { struct OrbitTracker {
double initial_angle; double initial_angle;
double previous_angle; double previous_angle;
int quadrant_transitions; double accumulated_rotation;
int wrap_count;
bool orbit_completed; bool orbit_completed;
double time_at_completion; double time_at_completion;
int body_index; int body_index;
@ -48,7 +49,7 @@ bool compare_vec3(Vec3 a, Vec3 b, double tolerance);
// Write simulation state to a caller-allocated buffer. // Write simulation state to a caller-allocated buffer.
// Returns number of characters written (excluding null terminator). // Returns number of characters written (excluding null terminator).
// Caller must ensure buffer is large enough. // Caller must ensure buffer is large enough (recommended 4096 bytes).
int dump_simulation_state(SimulationState* sim, const char* label, int dump_simulation_state(SimulationState* sim, const char* label,
char* buffer, int buffer_size); char* buffer, int buffer_size);

Loading…
Cancel
Save