@ -61,33 +61,169 @@ TEST_CASE("Prograde burn at periapsis preserves periapsis distance", "[maneuver]
destroy_simulation ( sim ) ;
}
TEST_CASE ( " Prograde burn at periapsis raises apoapsis not periapsis " , " [maneuver][periapsis][apoapsis ] " ) {
TEST_CASE ( " Two periapsis burns execute at same location " , " [maneuver][periapsis][sequential ] " ) {
const double TIME_STEP = 60.0 ;
const int ORBIT_STEPS = 175 ;
SimulationState * sim = create_simulation ( 10 , 10 , 100 , TIME_STEP ) ;
REQUIRE ( load_system_config ( sim , " tests/test_periapsis_burn.toml " ) ) ;
Spacecraft * craft = & sim - > spacecraft [ 0 ] ;
CelestialBody * parent = & sim - > bodies [ craft - > parent_index ] ;
int maneuver_indices [ 2 ] ;
int maneuver_count_for_craft = 0 ;
for ( int i = 0 ; i < sim - > maneuver_count ; i + + ) {
if ( sim - > maneuvers [ i ] . craft_index = = 0 ) {
maneuver_indices [ maneuver_count_for_craft + + ] = i ;
}
}
REQUIRE ( maneuver_count_for_craft = = 2 ) ;
double initial_periapsis = craft - > orbit . semi_major_axis * ( 1.0 - craft - > orbit . eccentricity ) ;
double initial_apoapsis = craft - > orbit . semi_major_axis * ( 1.0 + craft - > orbit . eccentricity ) ;
update_simulation ( sim ) ;
INFO ( " Initial orbit: " ) ;
INFO ( " Periapsis: " < < initial_periapsis ) ;
INFO ( " Apoapsis: " < < initial_apoapsis ) ;
INFO ( " Eccentricity: " < < craft - > orbit . eccentricity ) ;
double burn1_time = - 1.0 ;
double burn1_radius = - 1.0 ;
double burn1_true_anomaly = - 10.0 ;
double burn2_time = - 1.0 ;
double burn2_radius = - 1.0 ;
double burn2_true_anomaly = - 10.0 ;
for ( int i = 0 ; i < ORBIT_STEPS ; i + + ) {
update_simulation ( sim ) ;
if ( sim - > maneuvers [ maneuver_indices [ 0 ] ] . executed & & burn1_time < 0 ) {
burn1_time = sim - > time ;
burn1_radius = vec3_magnitude ( craft - > local_position ) ;
Vec3 r = craft - > local_position ;
Vec3 v = craft - > local_velocity ;
Vec3 h = vec3_cross ( r , v ) ;
Vec3 e_vec = calculate_eccentricity_vector ( r , v , h , G * parent - > mass ) ;
double e_mag = vec3_magnitude ( e_vec ) ;
burn1_true_anomaly = calculate_true_anomaly ( r , v , e_vec , e_mag , burn1_radius ) ;
burn1_true_anomaly = normalize_angle ( burn1_true_anomaly ) ;
INFO ( " First burn executed at step " < < i ) ;
INFO ( " Time: " < < burn1_time ) ;
INFO ( " Radius: " < < burn1_radius ) ;
INFO ( " True anomaly: " < < burn1_true_anomaly < < " rad ( " < < burn1_true_anomaly * 180.0 / M_PI < < " °) " ) ;
INFO ( " Periapsis: " < < initial_periapsis ) ;
INFO ( " Apoapsis: " < < initial_apoapsis ) ;
}
if ( sim - > maneuvers [ maneuver_indices [ 1 ] ] . executed & & burn2_time < 0 ) {
burn2_time = sim - > time ;
burn2_radius = vec3_magnitude ( craft - > local_position ) ;
Vec3 r = craft - > local_position ;
Vec3 v = craft - > local_velocity ;
Vec3 h = vec3_cross ( r , v ) ;
Vec3 e_vec = calculate_eccentricity_vector ( r , v , h , G * parent - > mass ) ;
double e_mag = vec3_magnitude ( e_vec ) ;
burn2_true_anomaly = calculate_true_anomaly ( r , v , e_vec , e_mag , burn2_radius ) ;
burn2_true_anomaly = normalize_angle ( burn2_true_anomaly ) ;
INFO ( " Second burn executed at step " < < i ) ;
INFO ( " Time: " < < burn2_time ) ;
INFO ( " Radius: " < < burn2_radius ) ;
INFO ( " True anomaly: " < < burn2_true_anomaly < < " rad ( " < < burn2_true_anomaly * 180.0 / M_PI < < " °) " ) ;
}
}
REQUIRE ( sim - > maneuvers [ maneuver_indices [ 0 ] ] . executed ) ;
REQUIRE ( sim - > maneuvers [ maneuver_indices [ 1 ] ] . executed ) ;
INFO ( " Burn comparison: " ) ;
INFO ( " Burn 1: time= " < < burn1_time < < " , radius= " < < burn1_radius < < " , true_anomaly= " < < burn1_true_anomaly ) ;
INFO ( " Burn 2: time= " < < burn2_time < < " , radius= " < < burn2_radius < < " , true_anomaly= " < < burn2_true_anomaly ) ;
REQUIRE_THAT ( burn1_radius , Catch : : Matchers : : WithinAbs ( initial_periapsis , 1000.0 ) ) ;
REQUIRE_THAT ( burn2_radius , Catch : : Matchers : : WithinAbs ( initial_periapsis , 1000.0 ) ) ;
REQUIRE ( fabs ( burn1_true_anomaly ) < 0.5 ) ;
REQUIRE ( fabs ( burn2_true_anomaly ) < 0.5 ) ;
double period = 2.0 * M_PI * sqrt ( pow ( craft - > orbit . semi_major_axis , 3.0 ) / ( G * parent - > mass ) ) ;
INFO ( " Expected orbital period: " < < period < < " seconds " ) ;
INFO ( " Actual time between burns: " < < ( burn2_time - burn1_time ) < < " seconds " ) ;
REQUIRE_THAT ( burn2_time - burn1_time , Catch : : Matchers : : WithinAbs ( period , TIME_STEP * 2.0 ) ) ;
double final_sma = craft - > orbit . semi_major_axis ;
double final_ecc = craft - > orbit . eccentricity ;
double final_periapsis = final_sma * ( 1.0 - final_ecc ) ;
double final_apoapsis = final_sma * ( 1.0 + final_ecc ) ;
destroy_simulation ( sim ) ;
}
INFO ( " Initial: peri= " < < initial_periapsis < < " apo= " < < initial_apoapsis ) ;
INFO ( " Final: peri= " < < final_periapsis < < " apo= " < < final_apoapsis ) ;
TEST_CASE ( " Periapsis burn fires when crossing periapsis " , " [maneuver][periapsis][crossing] " ) {
const double TIME_STEP = 60.0 ;
SimulationState * sim = create_simulation ( 10 , 10 , 100 , TIME_STEP ) ;
REQUIRE ( final_apoapsis > initial_apoapsis ) ;
REQUIRE_THAT ( final_periapsis , Catch : : Matchers : : WithinAbs ( initial_periapsis , 1.0 ) ) ;
REQUIRE ( load_system_config ( sim , " tests/test_periapsis_burn.toml " ) ) ;
Spacecraft * craft = & sim - > spacecraft [ 1 ] ; // TestSatelliteCrossing
CelestialBody * parent = & sim - > bodies [ craft - > parent_index ] ;
int maneuver_index = - 1 ;
for ( int i = 0 ; i < sim - > maneuver_count ; i + + ) {
if ( sim - > maneuvers [ i ] . craft_index = = 1 ) {
maneuver_index = i ;
break ;
}
}
REQUIRE ( maneuver_index > = 0 ) ;
double initial_periapsis = craft - > orbit . semi_major_axis * ( 1.0 - craft - > orbit . eccentricity ) ;
double initial_apoapsis = craft - > orbit . semi_major_axis * ( 1.0 + craft - > orbit . eccentricity ) ;
INFO ( " Initial orbit: " ) ;
INFO ( " Periapsis: " < < initial_periapsis ) ;
INFO ( " Apoapsis: " < < initial_apoapsis ) ;
INFO ( " Initial true anomaly: " < < craft - > orbit . true_anomaly < < " rad " ) ;
double burn_time = - 1.0 ;
double burn_radius = - 1.0 ;
double burn_true_anomaly = - 10.0 ;
int max_steps = 1000 ;
for ( int i = 0 ; i < max_steps & & ! sim - > maneuvers [ maneuver_index ] . executed ; i + + ) {
update_simulation ( sim ) ;
if ( sim - > maneuvers [ maneuver_index ] . executed ) {
burn_time = sim - > time ;
burn_radius = vec3_magnitude ( craft - > local_position ) ;
Vec3 r = craft - > local_position ;
Vec3 v = craft - > local_velocity ;
Vec3 h = vec3_cross ( r , v ) ;
Vec3 e_vec = calculate_eccentricity_vector ( r , v , h , G * parent - > mass ) ;
double e_mag = vec3_magnitude ( e_vec ) ;
burn_true_anomaly = calculate_true_anomaly ( r , v , e_vec , e_mag , burn_radius ) ;
burn_true_anomaly = normalize_angle ( burn_true_anomaly ) ;
INFO ( " Burn executed at step " < < i ) ;
INFO ( " Time: " < < burn_time ) ;
INFO ( " Radius: " < < burn_radius ) ;
INFO ( " True anomaly: " < < burn_true_anomaly < < " rad ( " < < burn_true_anomaly * 180.0 / M_PI < < " °) " ) ;
INFO ( " Periapsis: " < < initial_periapsis ) ;
INFO ( " Apoapsis: " < < initial_apoapsis ) ;
}
}
REQUIRE ( sim - > maneuvers [ maneuver_index ] . executed ) ;
REQUIRE_THAT ( burn_radius , Catch : : Matchers : : WithinAbs ( initial_periapsis , 1000.0 ) ) ;
REQUIRE ( fabs ( burn_true_anomaly ) < 0.5 ) ;
destroy_simulation ( sim ) ;
}
TEST_CASE ( " Burn location equals new periapsis after prograde burn " , " [maneuver][periapsis][location] " ) {
const double TIME_STEP = 60.0 ;
SimulationState * sim = create_simulation ( 10 , 10 , 100 , TIME_STEP ) ;