def test_bielliptic_maneuver(nu): # Data from Vallado, example 6.2 alt_i = 191.34411 * u.km alt_b = 503873.0 * u.km alt_f = 376310.0 * u.km _a = 0 * u.deg ss_i = Orbit.from_classical(Earth, Earth.R + alt_i, ecc=0 * u.one, inc=_a, raan=_a, argp=_a, nu=nu) # Expected output expected_dv = 3.904057 * u.km / u.s expected_t_pericenter = ss_i.time_to_anomaly(0 * u.deg) expected_t_trans = 593.919803 * u.h expected_total_time = expected_t_pericenter + expected_t_trans man = Maneuver.bielliptic(ss_i, Earth.R + alt_b, Earth.R + alt_f) assert_allclose(ss_i.apply_maneuver(man).ecc, 0 * u.one, atol=1e-12 * u.one) assert_quantity_allclose(man.get_total_cost(), expected_dv, rtol=1e-5) assert_quantity_allclose(man.get_total_time(), expected_total_time, rtol=1e-6)
def test_bielliptic_maneuver(): # Data from Vallado, example 6.2 alt_i = 191.34411 * u.km alt_b = 503873.0 * u.km alt_f = 376310.0 * u.km ss_i = Orbit.circular(Earth, alt_i) expected_dv = 3.904057 * u.km / u.s expected_t_trans = 593.919803 * u.h man = Maneuver.bielliptic(ss_i, Earth.R + alt_b, Earth.R + alt_f) assert_allclose(ss_i.apply_maneuver(man).ecc, 0 * u.one, atol=1e-12 * u.one) assert_quantity_allclose(man.get_total_cost(), expected_dv, rtol=1e-5) assert_quantity_allclose(man.get_total_time(), expected_t_trans, rtol=1e-6)
def test_repr_maneuver(): alt_f = 35781.34857 * u.km r = [-6045, -3490, 2500] * u.km v = [-3.457, 6.618, 2.533] * u.km / u.s alt_b = 503873.0 * u.km alt_fi = 376310.0 * u.km ss_i = Orbit.from_vectors(Earth, r, v) expected_hohmann_manuever = "Number of impulses: 2, Total cost: 3.060548 km / s" expected_bielliptic_maneuver = "Number of impulses: 3, Total cost: 3.122556 km / s" assert repr(Maneuver.hohmann(ss_i, Earth.R + alt_f)) == expected_hohmann_manuever assert (repr(Maneuver.bielliptic(ss_i, Earth.R + alt_b, Earth.R + alt_fi)) == expected_bielliptic_maneuver)
def test_bielliptic_maneuver(): # Data from Vallado, example 6.2 alt_i = 191.34411 * u.km alt_b = 503873.0 * u.km alt_f = 376310.0 * u.km ss_i = Orbit.circular(Earth, alt_i) expected_dv = 3.904057 * u.km / u.s expected_t_trans = 593.919803 * u.h man = Maneuver.bielliptic(ss_i, Earth.R + alt_b, Earth.R + alt_f) assert_almost_equal(ss_i.apply_maneuver(man).ecc, 0) assert_almost_equal(man.get_total_cost().to(u.km / u.s).value, expected_dv.value, decimal=5) assert_almost_equal(man.get_total_time().to(u.h).value, expected_t_trans.value, decimal=2)
R = np.linspace(2, 75, num=100) Rstar = [15.58, 40, 60, 100, 200, np.inf] hohmann_data = np.zeros_like(R) bielliptic_data = np.zeros((len(R), len(Rstar))) ss_i = Orbit.circular(Earth, 1.8 * u.km) r_i = ss_i.a v_i = np.sqrt(ss_i.v.dot(ss_i.v)) for ii, r in enumerate(R): r_f = r * r_i man = Maneuver.hohmann(ss_i, r_f) hohmann_data[ii] = (man.get_total_cost() / v_i).decompose().value for jj, rstar in enumerate(Rstar): r_b = rstar * r_i man = Maneuver.bielliptic(ss_i, r_b, r_f) bielliptic_data[ii, jj] = ( man.get_total_cost() / v_i).decompose().value idx_max = np.argmax(hohmann_data) ylims = (0.35, 0.6) # In[3]: fig, ax = plt.subplots(figsize=(8, 6)) l, = ax.plot(R, hohmann_data, lw=2) for jj in range(len(Rstar)):
def validate_3D_bielliptic(): # Initial orbit state vectors, final radius and time of flight # This orbit has an inclination of 22.30 [deg] so the maneuver will not be # applied on an equatorial orbit. See associated GMAT script: # gmat_validate_bielliptic3D.script rx_0, ry_0, rz_0 = 7200e3, -1000.0e3, 0.0 vx_0, vy_0, vz_0 = 0.0, 8.0e3, 3.25e3 rb_norm = 50000.00e3 rf_norm = 35781.35e3 tof_trans = 24500.00 # Impulse dVb is performed just a bit before this epoch tof = 72158.11 # Build the initial Orekit orbit k = C.IAU_2015_NOMINAL_EARTH_GM r0_vec = Vector3D(rx_0, ry_0, rz_0) v0_vec = Vector3D(vx_0, vy_0, vz_0) rv_0 = PVCoordinates(r0_vec, v0_vec) epoch_00 = AbsoluteDate.J2000_EPOCH gcrf_frame = FramesFactory.getGCRF() ss_0 = KeplerianOrbit(rv_0, gcrf_frame, epoch_00, k) # Final desired orbit radius and auxiliary variables a_0, ecc_0 = ss_0.getA(), ss_0.getE() rp_norm = a_0 * (1 - ecc_0) vp_norm = np.sqrt(2 * k / rp_norm - k / a_0) a_trans1 = (rp_norm + rb_norm) / 2 a_trans2 = (rb_norm + rf_norm) / 2 # Compute the magnitude of Bielliptic's deltaV dv_a = np.sqrt(2 * k / rp_norm - k / a_trans1) - vp_norm dv_b = np.sqrt(2 * k / rb_norm - k / a_trans2) - np.sqrt(2 * k / rb_norm - k / a_trans1) dv_c = np.sqrt(k / rf_norm) - np.sqrt(2 * k / rf_norm - k / a_trans2) # Convert previous magnitudes into vectors within the TNW frame, which # is aligned with local velocity vector dVa_vec, dVb_vec, dVc_vec = [ Vector3D(float(dV), float(0), float(0)) for dV in (dv_a, dv_b, dv_c) ] # Local orbit frame: X-axis aligned with velocity, Z-axis towards momentum attitude_provider = LofOffset(gcrf_frame, LOFType.TNW) # Setup impulse triggers; default apside detector stops on increasing g # function (aka at perigee) at_apoapsis = ApsideDetector(ss_0).withHandler(StopOnDecreasing()) at_periapsis = ApsideDetector(ss_0) # Build the impulsive maneuvers; ISP is only related to fuel mass cost ISP = float(300) imp_A = ImpulseManeuver(at_periapsis, attitude_provider, dVa_vec, ISP) imp_B = ImpulseManeuver(at_apoapsis, attitude_provider, dVb_vec, ISP) # Generate the propagator and add the first two impulses propagator = KeplerianPropagator(ss_0, attitude_provider) propagator.addEventDetector(imp_A) propagator.addEventDetector(imp_B) # Apply the maneuver by propagating the orbit epoch_trans = epoch_00.shiftedBy(tof_trans) ss_trans = propagator.propagate(epoch_trans).getOrbit() # Build a second propagator and add the last impulse so it is not confused # with the first one as both happen at perigee at_periapsis = ApsideDetector(ss_trans) imp_C = ImpulseManeuver(at_periapsis, attitude_provider, dVc_vec, ISP) propagator = KeplerianPropagator(ss_trans, attitude_provider) propagator.addEventDetector(imp_C) # Apply the last of the impulses tof_ff = float(tof - tof_trans) epoch_ff = epoch_trans.shiftedBy(tof_ff) rv_f = propagator.propagate(epoch_ff).getPVCoordinates(gcrf_frame) # Retrieve orekit final state vectors r_orekit, v_orekit = ( rv_f.getPosition().toArray() * u.m, rv_f.getVelocity().toArray() * u.m / u.s, ) # Build initial poliastro orbit and apply the maneuver r0_vec = [rx_0, ry_0, rz_0] * u.m v0_vec = [vx_0, vy_0, vz_0] * u.m / u.s ss0_poliastro = Orbit.from_vectors(Earth, r0_vec, v0_vec) man_poliastro = Maneuver.bielliptic(ss0_poliastro, rb_norm * u.m, rf_norm * u.m) # Retrieve propagation time after maneuver has been applied tof_prop = tof - man_poliastro.get_total_time().to(u.s).value ssf_poliastro = ss0_poliastro.apply_maneuver(man_poliastro).propagate( tof_prop * u.s, ) # Retrieve poliastro final state vectors r_poliastro, v_poliastro = ssf_poliastro.rv() # Assert final state vectors assert_quantity_allclose(r_poliastro, r_orekit, rtol=1e-6) assert_quantity_allclose(v_poliastro, v_orekit, rtol=1e-6)