Browse Source
- Merge 5 SCENARIOs into 1 with shared fixture - Reorganize 23 SECTIONs by test type (geometry → vis-viva → period → timestep → long-term) - Add 2 missing timestep tests (apogee radius, velocity comparison) - Replace all qualitative checks (a > b) with WithinAbs against precalculated values - Use named tolerance constants everywhere — zero hardcoded numbers in WithinAbs - Add VEL_CHANGE_SMALL_DT fixture constant - Refactor precalc script to load from TOML via Simulator class - Output matches new test orderingtest-refactor
2 changed files with 400 additions and 439 deletions
@ -1,234 +1,217 @@ |
|||||||
#!/usr/bin/env python3 |
#!/usr/bin/env python3 |
||||||
""" |
""" |
||||||
Precalculate expected values for test_analytical_propagation.cpp. |
Precalculate expected values for test_analytical_propagation.cpp. |
||||||
Computes orbital parameters, propagation results, and error bounds. |
Loads config from tests/test_analytical_propagation.toml, then computes |
||||||
|
orbital parameters, propagation results, and error bounds. |
||||||
""" |
""" |
||||||
|
|
||||||
import math |
import math |
||||||
import sys |
import sys |
||||||
sys.path.insert(0, '.') |
sys.path.insert(0, "scripts") |
||||||
from sim_engine import ( |
from sim_engine import Simulator, propagate, orbital_to_cartesian, vmag, G |
||||||
OrbitalElements, Spacecraft, Body, orbital_to_cartesian, |
|
||||||
propagate, G, vmag, vnorm |
def main(): |
||||||
) |
sim = Simulator("tests/test_analytical_propagation.toml", dt=60.0) |
||||||
|
|
||||||
# ============================================================================= |
earth = sim.get_body("Earth") |
||||||
# Spacecraft parameters from TOML |
craft_apsides = sim.get_craft("Apsides_Test_Spacecraft") |
||||||
# ============================================================================= |
craft_timestep = sim.get_craft("Timestep_Test_Spacecraft") |
||||||
|
|
||||||
craft_apsides = Spacecraft( |
earth_mass = earth.mass |
||||||
name="Apsides_Test_Spacecraft", |
mu = G * earth_mass |
||||||
mass=1000.0, |
|
||||||
parent_index=0, |
a1 = craft_apsides.orbit.a |
||||||
orbit=OrbitalElements(a=2.0e7, e=0.6, nu=0.0, inc=0.0, Omega=0.0, omega=0.0), |
e1 = craft_apsides.orbit.e |
||||||
) |
period1 = 2.0 * math.pi * math.sqrt(a1**3 / mu) |
||||||
|
n1 = math.sqrt(mu / a1**3) |
||||||
craft_timestep = Spacecraft( |
|
||||||
name="Timestep_Test_Spacecraft", |
a2 = craft_timestep.orbit.a |
||||||
mass=1000.0, |
e2 = craft_timestep.orbit.e |
||||||
parent_index=0, |
period2 = 2.0 * math.pi * math.sqrt(a2**3 / mu) |
||||||
orbit=OrbitalElements(a=1.5e7, e=0.4, nu=0.0, inc=0.0, Omega=0.0, omega=0.0), |
n2 = math.sqrt(mu / a2**3) |
||||||
) |
|
||||||
|
def print_comment_block(title): |
||||||
earth_mass = 5.972e24 |
print(f"\n// === {title} ===") |
||||||
mu = G * earth_mass |
|
||||||
|
def print_const(name, value, comment=""): |
||||||
def print_comment_block(title): |
c = f" // {comment}" if comment else "" |
||||||
print(f"\n// === {title} ===") |
print(f"const double {name} = {value:.15e};{c}") |
||||||
|
|
||||||
def print_const(name, value, comment=""): |
# ============================================================================= |
||||||
c = f" // {comment}" if comment else "" |
# 1. Apsides geometry — both spacecraft |
||||||
print(f"const double {name} = {value:.15e};{c}") |
# ============================================================================= |
||||||
|
|
||||||
# ============================================================================= |
print_comment_block("Apsides geometry (both spacecraft)") |
||||||
# 1. Apsides calculations |
|
||||||
# ============================================================================= |
# Apsides spacecraft |
||||||
|
r_peri1 = a1 * (1.0 - e1) |
||||||
print_comment_block("Apsides Test Spacecraft (a=2e7, e=0.6)") |
r_apo1 = a1 * (1.0 + e1) |
||||||
|
peri1 = craft_apsides.orbit |
||||||
a1 = craft_apsides.orbit.a |
_, vel_peri1 = orbital_to_cartesian(peri1, earth_mass) |
||||||
e1 = craft_apsides.orbit.e |
v_peri1 = vmag(vel_peri1) |
||||||
r_peri1 = a1 * (1.0 - e1) |
apo1_el = type(peri1)(a=a1, e=e1, nu=math.pi, inc=0.0, Omega=0.0, omega=0.0) |
||||||
r_apo1 = a1 * (1.0 + e1) |
_, vel_apo1 = orbital_to_cartesian(apo1_el, earth_mass) |
||||||
period1 = 2.0 * math.pi * math.sqrt(a1**3 / mu) |
v_apo1 = vmag(vel_apo1) |
||||||
n1 = math.sqrt(mu / a1**3) |
nu45_1_el = type(peri1)(a=a1, e=e1, nu=math.pi/4.0, inc=0.0, Omega=0.0, omega=0.0) |
||||||
|
_, vel_45_1 = orbital_to_cartesian(nu45_1_el, earth_mass) |
||||||
print(f"\n// Orbital period: {period1:.6f} s ({period1/3600:.2f} hours)") |
v_45_1 = vmag(vel_45_1) |
||||||
print(f"// Mean motion: {n1:.15e} rad/s") |
|
||||||
|
print(f"// Apsides spacecraft: a={a1:.0f}, e={e1}, period={period1:.2f}s") |
||||||
# Velocity at perigee (nu=0) |
print(f"// Mean motion: {n1:.15e} rad/s") |
||||||
peri1 = OrbitalElements(a=a1, e=e1, nu=0.0, inc=0.0, Omega=0.0, omega=0.0) |
print(f"// r_peri={r_peri1:.3f} m, r_apo={r_apo1:.3f} m") |
||||||
pos_peri1, vel_peri1 = orbital_to_cartesian(peri1, earth_mass) |
print(f"// v_peri={v_peri1:.6f} m/s, v_apo={v_apo1:.6f} m/s") |
||||||
v_peri1 = vmag(vel_peri1) |
print(f"// v_at_pi4={v_45_1:.6f} m/s") |
||||||
|
|
||||||
# Velocity at apogee (nu=pi) |
# Timestep spacecraft |
||||||
apo1 = OrbitalElements(a=a1, e=e1, nu=math.pi, inc=0.0, Omega=0.0, omega=0.0) |
r_peri2 = a2 * (1.0 - e2) |
||||||
pos_apo1, vel_apo1 = orbital_to_cartesian(apo1, earth_mass) |
r_apo2 = a2 * (1.0 + e2) |
||||||
v_apo1 = vmag(vel_apo1) |
peri2 = craft_timestep.orbit |
||||||
|
_, vel_peri2 = orbital_to_cartesian(peri2, earth_mass) |
||||||
# Velocity at nu=pi/4 |
v_peri2 = vmag(vel_peri2) |
||||||
nu45_1 = OrbitalElements(a=a1, e=e1, nu=math.pi/4.0, inc=0.0, Omega=0.0, omega=0.0) |
apo2_el = type(peri2)(a=a2, e=e2, nu=math.pi, inc=0.0, Omega=0.0, omega=0.0) |
||||||
pos_45_1, vel_45_1 = orbital_to_cartesian(nu45_1, earth_mass) |
_, vel_apo2 = orbital_to_cartesian(apo2_el, earth_mass) |
||||||
v_45_1 = vmag(vel_45_1) |
v_apo2 = vmag(vel_apo2) |
||||||
r_45_1 = vmag(pos_45_1) |
|
||||||
|
print(f"// Timestep spacecraft: a={a2:.0f}, e={e2}, period={period2:.2f}s") |
||||||
print_const("A1_R_PERI", r_peri1, "m") |
print(f"// Mean motion: {n2:.15e} rad/s") |
||||||
print_const("A1_R_APO", r_apo1, "m") |
print(f"// r_peri={r_peri2:.3f} m, r_apo={r_apo2:.3f} m") |
||||||
print_const("A1_V_PERI", v_peri1, "m/s") |
print(f"// v_peri={v_peri2:.6f} m/s, v_apo={v_apo2:.6f} m/s") |
||||||
print_const("A1_V_APO", v_apo1, "m/s") |
|
||||||
print_const("A1_V_AT_PI4", v_45_1, "m/s at nu=pi/4") |
print_const("A1_R_PERI", r_peri1, "m") |
||||||
print_const("A1_R_AT_PI4", r_45_1, "m at nu=pi/4") |
print_const("A1_R_APO", r_apo1, "m") |
||||||
print_const("A1_PERIOD", period1, "seconds") |
print_const("A1_V_PERI", v_peri1, "m/s") |
||||||
|
print_const("A1_V_APO", v_apo1, "m/s") |
||||||
# ============================================================================= |
print_const("A1_V_AT_PI4", v_45_1, "m/s at nu=pi/4") |
||||||
# 2. Timestep Test Spacecraft (a=1.5e7, e=0.4) |
print_const("A1_PERIOD", period1, "seconds") |
||||||
# ============================================================================= |
print_const("A2_R_PERI", r_peri2, "m") |
||||||
|
print_const("A2_R_APO", r_apo2, "m") |
||||||
print_comment_block("Timestep Test Spacecraft (a=1.5e7, e=0.4)") |
print_const("A2_V_PERI", v_peri2, "m/s") |
||||||
|
print_const("A2_V_APO", v_apo2, "m/s") |
||||||
a2 = craft_timestep.orbit.a |
print_const("A2_PERIOD", period2, "seconds") |
||||||
e2 = craft_timestep.orbit.e |
|
||||||
r_peri2 = a2 * (1.0 - e2) |
# ============================================================================= |
||||||
r_apo2 = a2 * (1.0 + e2) |
# 2. Vis-viva checks at multiple true anomalies |
||||||
period2 = 2.0 * math.pi * math.sqrt(a2**3 / mu) |
# ============================================================================= |
||||||
n2 = math.sqrt(mu / a2**3) |
|
||||||
|
print_comment_block("Vis-viva checks at multiple true anomalies") |
||||||
print(f"\n// Orbital period: {period2:.6f} s ({period2/3600:.2f} hours)") |
|
||||||
print(f"// Mean motion: {n2:.15e} rad/s") |
true_anomalies = [0.0, math.pi/4.0, math.pi/2.0, 3.0*math.pi/4.0, math.pi] |
||||||
|
for nu in true_anomalies: |
||||||
# Velocity at perigee |
deg = nu * 180.0 / math.pi |
||||||
peri2 = OrbitalElements(a=a2, e=e2, nu=0.0, inc=0.0, Omega=0.0, omega=0.0) |
el = type(craft_apsides.orbit)(a=a1, e=e1, nu=nu, inc=0.0, Omega=0.0, omega=0.0) |
||||||
pos_peri2, vel_peri2 = orbital_to_cartesian(peri2, earth_mass) |
pos, vel = orbital_to_cartesian(el, earth_mass) |
||||||
v_peri2 = vmag(vel_peri2) |
r = vmag(pos) |
||||||
r_peri2_calc = vmag(pos_peri2) |
v = vmag(vel) |
||||||
|
expected_v = math.sqrt(mu * (2.0/r - 1.0/a1)) |
||||||
print_const("A2_R_PERI", r_peri2, "m") |
v_error = abs(v - expected_v) |
||||||
print_const("A2_R_APO", r_apo2, "m") |
rel_error = v_error / expected_v * 100.0 |
||||||
print_const("A2_V_PERI", v_peri2, "m/s") |
print(f"// nu={deg:6.1f}deg: r={r:.3f} m, v={v:.6f} m/s, expected_v={expected_v:.6f} m/s, rel_err={rel_error:.8f}%") |
||||||
print_const("A2_PERIOD", period2, "seconds") |
|
||||||
|
# ============================================================================= |
||||||
# ============================================================================= |
# 3. Period return — full orbit closure for both spacecraft |
||||||
# 3. Vis-viva checks at multiple true anomalies |
# ============================================================================= |
||||||
# ============================================================================= |
|
||||||
|
print_comment_block("Period return — full orbit closure") |
||||||
print_comment_block("Vis-viva checks at multiple true anomalies") |
|
||||||
|
# Apsides spacecraft: propagate 1 period from nu=0 |
||||||
true_anomalies = [0.0, math.pi/4.0, math.pi/2.0, 3.0*math.pi/4.0, math.pi] |
el1 = type(craft_apsides.orbit)(a=a1, e=e1, nu=0.0, inc=0.0, Omega=0.0, omega=0.0) |
||||||
for nu in true_anomalies: |
_, vel1_init = orbital_to_cartesian(el1, earth_mass) |
||||||
deg = nu * 180.0 / math.pi |
prop1 = propagate(el1, period1, earth_mass) |
||||||
el = OrbitalElements(a=a1, e=e1, nu=nu, inc=0.0, Omega=0.0, omega=0.0) |
_, vel1_final = orbital_to_cartesian(prop1, earth_mass) |
||||||
pos, vel = orbital_to_cartesian(el, earth_mass) |
vel_change1 = math.sqrt((vel1_final[0]-vel1_init[0])**2 + (vel1_final[1]-vel1_init[1])**2 + (vel1_final[2]-vel1_init[2])**2) |
||||||
r = vmag(pos) |
print(f"// Apsides after 1 period: vel_change={vel_change1:.15e} m/s, final_nu={prop1.nu:.15e} rad") |
||||||
v = vmag(vel) |
|
||||||
expected_v = math.sqrt(mu * (2.0/r - 1.0/a1)) |
# Timestep spacecraft: propagate 1 period from nu=0 |
||||||
v_error = abs(v - expected_v) |
el2 = type(craft_timestep.orbit)(a=a2, e=e2, nu=0.0, inc=0.0, Omega=0.0, omega=0.0) |
||||||
rel_error = v_error / expected_v * 100.0 |
_, vel2_init = orbital_to_cartesian(el2, earth_mass) |
||||||
print(f"// nu={deg:6.1f}deg: r={r:.3f} m, v={v:.6f} m/s, expected_v={expected_v:.6f} m/s, rel_err={rel_error:.8f}%") |
prop2 = propagate(el2, period2, earth_mass) |
||||||
|
_, vel2_final = orbital_to_cartesian(prop2, earth_mass) |
||||||
# ============================================================================= |
vel_change2 = math.sqrt((vel2_final[0]-vel2_init[0])**2 + (vel2_final[1]-vel2_init[1])**2 + (vel2_final[2]-vel2_init[2])**2) |
||||||
# 4. Propagation accuracy tests |
print(f"// Timestep after 1 period: vel_change={vel_change2:.15e} m/s, final_nu={prop2.nu:.15e} rad") |
||||||
# ============================================================================= |
|
||||||
|
# ============================================================================= |
||||||
print_comment_block("Propagation accuracy tests") |
# 4. Timestep accuracy |
||||||
|
# ============================================================================= |
||||||
# Initial state for timestep craft |
|
||||||
init_el = OrbitalElements(a=a2, e=e2, nu=0.0, inc=0.0, Omega=0.0, omega=0.0) |
print_comment_block("Timestep accuracy") |
||||||
init_pos, init_vel = orbital_to_cartesian(init_el, earth_mass) |
|
||||||
init_r = vmag(init_pos) |
# Initial state for timestep craft |
||||||
init_v = vmag(init_vel) |
init_el = type(craft_timestep.orbit)(a=a2, e=e2, nu=0.0, inc=0.0, Omega=0.0, omega=0.0) |
||||||
|
init_pos, init_vel = orbital_to_cartesian(init_el, earth_mass) |
||||||
# Large timestep: 2x period |
init_r = vmag(init_pos) |
||||||
large_dt = period2 * 2.0 |
init_v = vmag(init_vel) |
||||||
prop_large = propagate(init_el, large_dt, earth_mass) |
|
||||||
pos_large, vel_large = orbital_to_cartesian(prop_large, earth_mass) |
# Large timestep: 2x period |
||||||
r_large = vmag(pos_large) |
large_dt = period2 * 2.0 |
||||||
v_large = vmag(vel_large) |
prop_large = propagate(init_el, large_dt, earth_mass) |
||||||
r_err_large = abs(r_large - init_r) |
pos_large, vel_large = orbital_to_cartesian(prop_large, earth_mass) |
||||||
v_err_large = abs(v_large - init_v) |
r_large = vmag(pos_large) |
||||||
rel_r_large = r_err_large / init_r * 100.0 |
v_large = vmag(vel_large) |
||||||
rel_v_large = v_err_large / init_v * 100.0 |
r_err_large = abs(r_large - init_r) |
||||||
print(f"// 2x period: r_err={r_err_large:.6f} m ({rel_r_large:.8f}%), v_err={v_err_large:.6f} m/s ({rel_v_large:.8f}%)") |
v_err_large = abs(v_large - init_v) |
||||||
|
rel_r_large = r_err_large / init_r * 100.0 |
||||||
# Small timestep: 0.1 s |
rel_v_large = v_err_large / init_v * 100.0 |
||||||
small_dt = 0.1 |
print(f"// 2x period: r_err={r_err_large:.6f} m ({rel_r_large:.8f}%), v_err={v_err_large:.6f} m/s ({rel_v_large:.8f}%)") |
||||||
prop_small = propagate(init_el, small_dt, earth_mass) |
|
||||||
pos_small, vel_small = orbital_to_cartesian(prop_small, earth_mass) |
# Small timestep: 0.1 s |
||||||
pos_change = math.sqrt((pos_small[0]-init_pos[0])**2 + (pos_small[1]-init_pos[1])**2 + (pos_small[2]-init_pos[2])**2) |
small_dt = 0.1 |
||||||
vel_change = math.sqrt((vel_small[0]-init_vel[0])**2 + (vel_small[1]-init_vel[1])**2 + (vel_small[2]-init_vel[2])**2) |
prop_small = propagate(init_el, small_dt, earth_mass) |
||||||
expected_pos_change = init_v * small_dt |
pos_small, vel_small = orbital_to_cartesian(prop_small, earth_mass) |
||||||
pos_error_small = abs(pos_change - expected_pos_change) |
pos_change = math.sqrt((pos_small[0]-init_pos[0])**2 + (pos_small[1]-init_pos[1])**2 + (pos_small[2]-init_pos[2])**2) |
||||||
print(f"// 0.1s dt: pos_change={pos_change:.6f} m, vel_change={vel_change:.10f} m/s") |
vel_change = math.sqrt((vel_small[0]-init_vel[0])**2 + (vel_small[1]-init_vel[1])**2 + (vel_small[2]-init_vel[2])**2) |
||||||
print(f"// expected_pos_change={expected_pos_change:.6f} m, pos_error={pos_error_small:.6f} m") |
expected_pos_change = init_v * small_dt |
||||||
|
pos_error_small = abs(pos_change - expected_pos_change) |
||||||
# ============================================================================= |
print(f"// 0.1s dt: pos_change={pos_change:.6f} m, vel_change={vel_change:.10f} m/s") |
||||||
# 5. Accuracy vs timestep size |
print(f"// expected_pos_change={expected_pos_change:.6f} m, pos_error={pos_error_small:.6f} m") |
||||||
# ============================================================================= |
|
||||||
|
# Accuracy at various multiples of period |
||||||
print_comment_block("Accuracy vs timestep size") |
dt_ratios = [1.0, 10.0] |
||||||
|
for ratio in dt_ratios: |
||||||
dt_ratios = [0.01, 0.1, 1.0, 10.0] |
dt = period2 * ratio |
||||||
for ratio in dt_ratios: |
prop = propagate(init_el, dt, earth_mass) |
||||||
dt = period2 * ratio |
pos_f, vel_f = orbital_to_cartesian(prop, earth_mass) |
||||||
prop = propagate(init_el, dt, earth_mass) |
pos_err = math.sqrt((pos_f[0]-init_pos[0])**2 + (pos_f[1]-init_pos[1])**2 + (pos_f[2]-init_pos[2])**2) |
||||||
pos_f, vel_f = orbital_to_cartesian(prop, earth_mass) |
vel_err = math.sqrt((vel_f[0]-init_vel[0])**2 + (vel_f[1]-init_vel[1])**2 + (vel_f[2]-init_vel[2])**2) |
||||||
pos_err = math.sqrt((pos_f[0]-init_pos[0])**2 + (pos_f[1]-init_pos[1])**2 + (pos_f[2]-init_pos[2])**2) |
print(f"// {ratio:.0f}x period: pos_err={pos_err:.6f} m, vel_err={vel_err:.10f} m/s") |
||||||
vel_err = math.sqrt((vel_f[0]-init_vel[0])**2 + (vel_f[1]-init_vel[1])**2 + (vel_f[2]-init_vel[2])**2) |
|
||||||
num_periods = dt / period2 |
# ============================================================================= |
||||||
expected_orbits = round(num_periods) |
# 5. Long-term stability (100 periods) |
||||||
fractional_phase = num_periods - expected_orbits |
# ============================================================================= |
||||||
expected_pos_err = abs(fractional_phase) * 2.0 * math.pi * a2 |
|
||||||
print(f"// dt={ratio:.2f}x period: pos_err={pos_err:.3f} m, vel_err={vel_err:.6f} m/s, " |
print_comment_block("Long-term stability (100 periods)") |
||||||
f"num_periods={num_periods:.4f}, expected_pos_err={expected_pos_err:.3f} m") |
|
||||||
if expected_orbits > 0 and expected_pos_err > 1e-6: |
prop_100 = propagate(init_el, period2 * 100.0, earth_mass) |
||||||
rel_err = pos_err / expected_pos_err |
final_nu = prop_100.nu |
||||||
print(f"// relative_error={rel_err:.6f}") |
expected_delta_nu = n2 * period2 * 100.0 |
||||||
|
expected_nu = init_el.nu + expected_delta_nu |
||||||
# ============================================================================= |
# Normalize both to [0, 2*pi) |
||||||
# 6. Long-term propagation (100 periods) |
while final_nu < 0: |
||||||
# ============================================================================= |
final_nu += 2.0 * math.pi |
||||||
|
while final_nu >= 2.0 * math.pi: |
||||||
print_comment_block("Long-term propagation (100 periods)") |
final_nu -= 2.0 * math.pi |
||||||
|
while expected_nu < 0: |
||||||
prop_100 = propagate(init_el, period2 * 100.0, earth_mass) |
expected_nu += 2.0 * math.pi |
||||||
final_nu = prop_100.nu |
while expected_nu >= 2.0 * math.pi: |
||||||
expected_delta_nu = n2 * period2 * 100.0 |
expected_nu -= 2.0 * math.pi |
||||||
expected_nu = init_el.nu + expected_delta_nu |
|
||||||
# Normalize both to [0, 2*pi) |
raw_error = abs(final_nu - expected_nu) |
||||||
while final_nu < 0: |
anomaly_error = min(raw_error, 2.0 * math.pi - raw_error) |
||||||
final_nu += 2.0 * math.pi |
print(f"// Propagation time: {period2*100.0:.2f} s ({100.0} periods)") |
||||||
while final_nu >= 2.0 * math.pi: |
print(f"// final_nu={final_nu:.15e} rad") |
||||||
final_nu -= 2.0 * math.pi |
print(f"// expected_nu={expected_nu:.15e} rad") |
||||||
while expected_nu < 0: |
print(f"// raw_error={raw_error:.15e} rad") |
||||||
expected_nu += 2.0 * math.pi |
print(f"// anomaly_error={anomaly_error:.15e} rad ({anomaly_error*180/math.pi:.10e} degrees)") |
||||||
while expected_nu >= 2.0 * math.pi: |
|
||||||
expected_nu -= 2.0 * math.pi |
# ============================================================================= |
||||||
|
# Output summary |
||||||
raw_error = abs(final_nu - expected_nu) |
# ============================================================================= |
||||||
anomaly_error = min(raw_error, 2.0 * math.pi - raw_error) |
|
||||||
print(f"// final_nu={final_nu:.15e} rad") |
print("\n// === SUMMARY ===") |
||||||
print(f"// expected_nu={expected_nu:.15e} rad") |
print(f"// Apsides spacecraft: a={a1:.0f}, e={e1}, period={period1:.2f}s") |
||||||
print(f"// raw_error={raw_error:.15e} rad") |
print(f"// Timestep spacecraft: a={a2:.0f}, e={e2}, period={period2:.2f}s") |
||||||
print(f"// anomaly_error={anomaly_error:.15e} rad ({anomaly_error*180/math.pi:.10e} degrees)") |
print(f"// Vis-viva relative errors are all < 0.01%") |
||||||
|
print(f"// Full orbit position/velocity errors are < 0.1%") |
||||||
# ============================================================================= |
print(f"// Long-term (100 periods) anomaly error: {anomaly_error:.15e} rad") |
||||||
# 7. Full orbit true anomaly accuracy |
|
||||||
# ============================================================================= |
if __name__ == "__main__": |
||||||
|
main() |
||||||
print_comment_block("Full orbit true anomaly accuracy") |
|
||||||
|
|
||||||
# Test with initial nu = 0 (craft_apsides starts at nu=0) |
|
||||||
full_prop = propagate(init_el, period2, earth_mass) |
|
||||||
print(f"// After 1 period: nu={full_prop.nu:.15e} rad") |
|
||||||
print(f"// Expected: nu=0 (same as initial)") |
|
||||||
print(f"// Error: {abs(full_prop.nu):.15e} rad") |
|
||||||
|
|
||||||
# ============================================================================= |
|
||||||
# Output summary |
|
||||||
# ============================================================================= |
|
||||||
|
|
||||||
print("\n// === SUMMARY ===") |
|
||||||
print(f"// Apsides spacecraft: a={a1:.0f}, e={e1}, period={period1:.2f}s") |
|
||||||
print(f"// Timestep spacecraft: a={a2:.0f}, e={e2}, period={period2:.2f}s") |
|
||||||
print(f"// Vis-viva relative errors are all < 0.01%") |
|
||||||
print(f"// Full orbit position/velocity errors are < 0.1%") |
|
||||||
print(f"// Long-term (100 periods) anomaly error: {anomaly_error:.15e} rad") |
|
||||||
|
|||||||
Loading…
Reference in new issue