Browse Source

Fix test_hybrid_impulse_burns to use maneuver system

- Added helper functions: find_maneuver_by_name() and execute_maneuver_by_name()
- Modified Hohmann transfer test to use maneuver system via execute_maneuver()
- Modified large burns test to use maneuver system
- Modified energy conservation test to use maneuver system (prograde burn)
- Modified round-trip conversion test to use maneuver system
- Modified multiple burn sequences to use maneuver system for Hohmann transfer
- Tests now properly validate maneuver trigger and execution workflow
- All tests pass (96 assertions in 7 test cases)
main
cinnaboot 5 months ago
parent
commit
31dc42a2c9
  1. 150
      tests/test_hybrid_impulse_burns.cpp

150
tests/test_hybrid_impulse_burns.cpp

@ -8,12 +8,42 @@
#include "../src/config_loader.h"
#include "../src/test_utilities.h"
#include <cmath>
#include <cstring>
const double POSITION_TOLERANCE = 1e-3;
const double VELOCITY_TOLERANCE = 1e-3;
const double ELEMENT_TOLERANCE = 1e-6;
const double ENERGY_TOLERANCE = 1e-6;
int find_maneuver_by_name(SimulationState* sim, const char* name) {
for (int i = 0; i < sim->maneuver_count; i++) {
if (strcmp(sim->maneuvers[i].name, name) == 0) {
return i;
}
}
return -1;
}
void execute_maneuver_by_name(SimulationState* sim, const char* maneuver_name, Spacecraft* craft) {
int maneuver_index = find_maneuver_by_name(sim, maneuver_name);
REQUIRE(maneuver_index >= 0);
Maneuver* maneuver = &sim->maneuvers[maneuver_index];
REQUIRE(!maneuver->executed);
// Set simulation time to trigger (for time-based triggers)
if (maneuver->trigger_type == TRIGGER_TIME) {
sim->time = maneuver->trigger_value;
}
// Execute maneuver
execute_maneuver(maneuver, craft, sim->time);
// Verify execution
REQUIRE(maneuver->executed);
REQUIRE(maneuver->executed_time == sim->time);
}
TEST_CASE("Config loading for hybrid impulse burns", "[hybrid][impulse][config]") {
const double TIME_STEP = 60.0;
@ -63,13 +93,16 @@ SCENARIO("Hohmann transfer with two burns", "[hybrid][impulse][hohmann]") {
Vec3 initial_pos;
Vec3 initial_vel;
orbital_elements_to_cartesian(craft->orbit, earth->mass, &initial_pos, &initial_vel);
craft->local_position = initial_pos;
craft->local_velocity = initial_vel;
SECTION("First burn at perigee raises apogee") {
OrbitalElements initial_elements = craft->orbit;
OrbitalElements initial_elements = craft->orbit;
SECTION("First burn at perigee raises apogee") {
double initial_velocity_mag = vec3_magnitude(initial_vel);
apply_impulsive_burn(craft, BURN_PROGRADE, 2440.0);
// Execute first maneuver via maneuver system
execute_maneuver_by_name(sim, "hohmann_burn_1", craft);
double new_velocity_mag = vec3_magnitude(craft->local_velocity);
@ -89,6 +122,40 @@ SCENARIO("Hohmann transfer with two burns", "[hybrid][impulse][hohmann]") {
REQUIRE(new_elements.eccentricity > initial_elements.eccentricity);
}
SECTION("Second burn at apogee circularizes orbit") {
// Execute first burn
execute_maneuver_by_name(sim, "hohmann_burn_1", craft);
OrbitalElements after_first_burn = cartesian_to_orbital_elements(craft->local_position, craft->local_velocity, earth->mass);
// Set up position at apogee (true_anomaly = PI)
OrbitalElements apogee_elements = after_first_burn;
apogee_elements.true_anomaly = M_PI;
Vec3 apogee_pos;
Vec3 apogee_vel;
orbital_elements_to_cartesian(apogee_elements, earth->mass, &apogee_pos, &apogee_vel);
craft->local_position = apogee_pos;
craft->local_velocity = apogee_vel;
// Execute second maneuver via maneuver system
execute_maneuver_by_name(sim, "hohmann_burn_2", craft);
Vec3 final_pos = craft->local_position;
Vec3 final_vel = craft->local_velocity;
OrbitalElements final_elements = cartesian_to_orbital_elements(final_pos, final_vel, earth->mass);
INFO("After first burn a: " << after_first_burn.semi_major_axis);
INFO("After first burn e: " << after_first_burn.eccentricity);
INFO("Final a: " << final_elements.semi_major_axis);
INFO("Final e: " << final_elements.eccentricity);
REQUIRE(final_elements.semi_major_axis > after_first_burn.semi_major_axis);
REQUIRE(final_elements.eccentricity < after_first_burn.eccentricity);
REQUIRE(final_elements.eccentricity < 0.1);
}
destroy_simulation(sim);
}
@ -105,6 +172,8 @@ SCENARIO("Large burns (Δv > orbital velocity)", "[hybrid][impulse][large_delta_
Vec3 initial_pos;
Vec3 initial_vel;
orbital_elements_to_cartesian(craft->orbit, earth->mass, &initial_pos, &initial_vel);
craft->local_position = initial_pos;
craft->local_velocity = initial_vel;
OrbitalElements initial_elements = cartesian_to_orbital_elements(initial_pos, initial_vel, earth->mass);
@ -115,8 +184,8 @@ SCENARIO("Large burns (Δv > orbital velocity)", "[hybrid][impulse][large_delta_
INFO("Initial velocity: " << initial_velocity_mag << " m/s");
INFO("Escape velocity: " << escape_velocity << " m/s");
double delta_v = 12000.0;
apply_impulsive_burn(craft, BURN_PROGRADE, delta_v);
// Execute large burn via maneuver system
execute_maneuver_by_name(sim, "large_burn", craft);
double final_velocity_mag = vec3_magnitude(craft->local_velocity);
INFO("Final velocity: " << final_velocity_mag << " m/s");
@ -133,7 +202,8 @@ SCENARIO("Large burns (Δv > orbital velocity)", "[hybrid][impulse][large_delta_
}
SECTION("Large burn produces correct hyperbolic trajectory") {
apply_impulsive_burn(craft, BURN_PROGRADE, 12000.0);
// Execute large burn via maneuver system
execute_maneuver_by_name(sim, "large_burn", craft);
OrbitalElements new_elements = cartesian_to_orbital_elements(craft->local_position, craft->local_velocity, earth->mass);
@ -176,7 +246,14 @@ SCENARIO("Energy conservation during burns", "[hybrid][impulse][energy]") {
double delta_v = 1000.0;
Vec3 v_initial = craft->local_velocity;
apply_impulsive_burn(craft, BURN_PROGRADE, delta_v);
// Get maneuver delta_v from config
int maneuver_index = find_maneuver_by_name(sim, "hohmann_burn_1");
REQUIRE(maneuver_index >= 0);
Maneuver* maneuver = &sim->maneuvers[maneuver_index];
delta_v = maneuver->delta_v;
// Execute burn via maneuver system
execute_maneuver_by_name(sim, "hohmann_burn_1", craft);
Vec3 v_final = craft->local_velocity;
Vec3 dv = vec3_sub(v_final, v_initial);
@ -204,7 +281,16 @@ SCENARIO("Energy conservation during burns", "[hybrid][impulse][energy]") {
double delta_v = 1000.0;
Vec3 v_initial = craft->local_velocity;
apply_impulsive_burn(craft, BURN_RETROGRADE, delta_v);
// Reset spacecraft for second test
craft->local_position = initial_pos;
craft->local_velocity = initial_vel;
sim->time = 0.0;
sim->maneuvers[find_maneuver_by_name(sim, "hohmann_burn_1")].executed = false;
// Create a retrograde maneuver for this test
Vec3 retrograde_dir = calculate_retrograde_dir(v_initial);
Vec3 dv_vec = vec3_scale(retrograde_dir, delta_v);
apply_custom_burn(craft, dv_vec);
Vec3 v_final = craft->local_velocity;
Vec3 dv = vec3_sub(v_final, v_initial);
@ -247,6 +333,8 @@ SCENARIO("Round-trip conversion with burns", "[hybrid][impulse][roundtrip]") {
Vec3 position_from_elements;
Vec3 velocity_from_elements;
orbital_elements_to_cartesian(original_elements, earth->mass, &position_from_elements, &velocity_from_elements);
craft->local_position = position_from_elements;
craft->local_velocity = velocity_from_elements;
INFO("Original semi_major_axis: " << original_elements.semi_major_axis);
INFO("Original eccentricity: " << original_elements.eccentricity);
@ -259,13 +347,10 @@ SCENARIO("Round-trip conversion with burns", "[hybrid][impulse][roundtrip]") {
REQUIRE_THAT(recovered_elements.semi_major_axis, Catch::Matchers::WithinAbs(original_elements.semi_major_axis, ELEMENT_TOLERANCE));
REQUIRE_THAT(recovered_elements.eccentricity, Catch::Matchers::WithinAbs(original_elements.eccentricity, ELEMENT_TOLERANCE));
Vec3 burn_velocity = calculate_prograde_dir(velocity_from_elements);
Vec3 new_velocity = velocity_from_elements;
new_velocity.x += burn_velocity.x * 1000.0;
new_velocity.y += burn_velocity.y * 1000.0;
new_velocity.z += burn_velocity.z * 1000.0;
// Execute maneuver via system
execute_maneuver_by_name(sim, "hohmann_burn_1", craft);
OrbitalElements post_burn_elements = cartesian_to_orbital_elements(position_from_elements, new_velocity, earth->mass);
OrbitalElements post_burn_elements = cartesian_to_orbital_elements(craft->local_position, craft->local_velocity, earth->mass);
INFO("Post-burn semi_major_axis: " << post_burn_elements.semi_major_axis);
INFO("Post-burn eccentricity: " << post_burn_elements.eccentricity);
@ -280,6 +365,8 @@ SCENARIO("Round-trip conversion with burns", "[hybrid][impulse][roundtrip]") {
Vec3 position;
Vec3 velocity;
orbital_elements_to_cartesian(original_elements, earth->mass, &position, &velocity);
craft->local_position = position;
craft->local_velocity = velocity;
for (int i = 0; i < 5; i++) {
OrbitalElements elements = cartesian_to_orbital_elements(position, velocity, earth->mass);
@ -327,7 +414,9 @@ SCENARIO("Multiple burn sequences", "[hybrid][impulse][sequence]") {
INFO("Initial a: " << initial_elements.semi_major_axis);
INFO("Initial e: " << initial_elements.eccentricity);
apply_impulsive_burn(craft, BURN_PROGRADE, 500.0);
// Execute first burn via maneuver system
execute_maneuver_by_name(sim, "hohmann_burn_1", craft);
OrbitalElements after_first_burn = cartesian_to_orbital_elements(craft->local_position, craft->local_velocity, earth->mass);
INFO("After first burn a: " << after_first_burn.semi_major_axis);
@ -335,6 +424,7 @@ SCENARIO("Multiple burn sequences", "[hybrid][impulse][sequence]") {
REQUIRE(after_first_burn.semi_major_axis > initial_elements.semi_major_axis);
// Propagate to apogee for second burn
OrbitalElements apogee_elements = after_first_burn;
apogee_elements.true_anomaly = M_PI;
@ -344,7 +434,9 @@ SCENARIO("Multiple burn sequences", "[hybrid][impulse][sequence]") {
craft->local_position = apogee_pos;
craft->local_velocity = apogee_vel;
apply_impulsive_burn(craft, BURN_PROGRADE, 300.0);
// Execute second burn via maneuver system
execute_maneuver_by_name(sim, "hohmann_burn_2", craft);
OrbitalElements after_second_burn = cartesian_to_orbital_elements(craft->local_position, craft->local_velocity, earth->mass);
INFO("After second burn a: " << after_second_burn.semi_major_axis);
@ -357,13 +449,33 @@ SCENARIO("Multiple burn sequences", "[hybrid][impulse][sequence]") {
SECTION("Three-burn sequence with plane change") {
OrbitalElements initial_elements = cartesian_to_orbital_elements(craft->local_position, craft->local_velocity, earth->mass);
apply_impulsive_burn(craft, BURN_PROGRADE, 500.0);
// Reset spacecraft
craft->local_position = initial_pos;
craft->local_velocity = initial_vel;
sim->time = 0.0;
for (int i = 0; i < sim->maneuver_count; i++) {
sim->maneuvers[i].executed = false;
}
// Execute prograde burn manually (no config maneuver for this sequence)
Vec3 prograde_dir = calculate_prograde_dir(craft->local_velocity);
Vec3 dv1 = vec3_scale(prograde_dir, 500.0);
apply_custom_burn(craft, dv1);
OrbitalElements after_burn1 = cartesian_to_orbital_elements(craft->local_position, craft->local_velocity, earth->mass);
apply_impulsive_burn(craft, BURN_NORMAL, 300.0);
// Execute normal burn
Vec3 normal_dir = calculate_normal_dir(craft->local_position, craft->local_velocity);
Vec3 dv2 = vec3_scale(normal_dir, 300.0);
apply_custom_burn(craft, dv2);
OrbitalElements after_burn2 = cartesian_to_orbital_elements(craft->local_position, craft->local_velocity, earth->mass);
apply_impulsive_burn(craft, BURN_PROGRADE, 200.0);
// Execute second prograde burn
prograde_dir = calculate_prograde_dir(craft->local_velocity);
Vec3 dv3 = vec3_scale(prograde_dir, 200.0);
apply_custom_burn(craft, dv3);
OrbitalElements after_burn3 = cartesian_to_orbital_elements(craft->local_position, craft->local_velocity, earth->mass);
INFO("Initial a: " << initial_elements.semi_major_axis);

Loading…
Cancel
Save