Ejemplo n.º 1
0
def test_J2_propagation_Earth():
    # from Curtis example 12.2:
    r0 = np.array([-2384.46, 5729.01, 3050.46])  # km
    v0 = np.array([-7.36138, -2.98997, 1.64354])  # km/s

    orbit = Orbit.from_vectors(Earth, r0 * u.km, v0 * u.km / u.s)

    tofs = [48.0] * u.h
    rr, vv = cowell(
        Earth.k,
        orbit.r,
        orbit.v,
        tofs,
        ad=J2_perturbation,
        J2=Earth.J2.value,
        R=Earth.R.to(u.km).value,
    )

    k = Earth.k.to(u.km ** 3 / u.s ** 2).value

    _, _, _, raan0, argp0, _ = rv2coe(k, r0, v0)
    _, _, _, raan, argp, _ = rv2coe(k, rr[0].to(u.km).value, vv[0].to(u.km / u.s).value)

    raan_variation_rate = (raan - raan0) / tofs[0].to(u.s).value
    argp_variation_rate = (argp - argp0) / tofs[0].to(u.s).value

    raan_variation_rate = (raan_variation_rate * u.rad / u.s).to(u.deg / u.h)
    argp_variation_rate = (argp_variation_rate * u.rad / u.s).to(u.deg / u.h)

    assert_quantity_allclose(raan_variation_rate, -0.172 * u.deg / u.h, rtol=1e-2)
    assert_quantity_allclose(argp_variation_rate, 0.282 * u.deg / u.h, rtol=1e-2)
Ejemplo n.º 2
0
def test_J2_propagation_Earth():
    # From Curtis example 12.2:
    r0 = np.array([-2384.46, 5729.01, 3050.46])  # km
    v0 = np.array([-7.36138, -2.98997, 1.64354])  # km/s

    orbit = Orbit.from_vectors(Earth, r0 * u.km, v0 * u.km / u.s)

    tofs = [48.0] * u.h

    def f(t0, u_, k):
        du_kep = func_twobody(t0, u_, k)
        ax, ay, az = J2_perturbation(
            t0, u_, k, J2=Earth.J2.value, R=Earth.R.to(u.km).value
        )
        du_ad = np.array([0, 0, 0, ax, ay, az])
        return du_kep + du_ad

    rr, vv = cowell(Earth.k, orbit.r, orbit.v, tofs, f=f)

    k = Earth.k.to(u.km ** 3 / u.s ** 2).value

    _, _, _, raan0, argp0, _ = rv2coe(k, r0, v0)
    _, _, _, raan, argp, _ = rv2coe(k, rr[0].to(u.km).value, vv[0].to(u.km / u.s).value)

    raan_variation_rate = (raan - raan0) / tofs[0].to(u.s).value  # type: ignore
    argp_variation_rate = (argp - argp0) / tofs[0].to(u.s).value  # type: ignore

    raan_variation_rate = (raan_variation_rate * u.rad / u.s).to(u.deg / u.h)
    argp_variation_rate = (argp_variation_rate * u.rad / u.s).to(u.deg / u.h)

    assert_quantity_allclose(raan_variation_rate, -0.172 * u.deg / u.h, rtol=1e-2)
    assert_quantity_allclose(argp_variation_rate, 0.282 * u.deg / u.h, rtol=1e-2)
Ejemplo n.º 3
0
def test_J2_propagation_Earth():
    # from Curtis example 12.2:
    r0 = np.array([-2384.46, 5729.01, 3050.46])  # km
    v0 = np.array([-7.36138, -2.98997, 1.64354])  # km/s
    k = Earth.k.to(u.km**3 / u.s**2).value

    orbit = Orbit.from_vectors(Earth, r0 * u.km, v0 * u.km / u.s)

    tof = (48.0 * u.h).to(u.s).value
    r, v = cowell(orbit,
                  tof,
                  ad=J2_perturbation,
                  J2=Earth.J2.value,
                  R=Earth.R.to(u.km).value)

    _, _, _, raan0, argp0, _ = rv2coe(k, r0, v0)
    _, _, _, raan, argp, _ = rv2coe(k, r, v)

    raan_variation_rate = (raan - raan0) / tof
    argp_variation_rate = (argp - argp0) / tof

    raan_variation_rate = (raan_variation_rate * u.rad / u.s).to(u.deg / u.h)
    argp_variation_rate = (argp_variation_rate * u.rad / u.s).to(u.deg / u.h)

    assert_quantity_allclose(raan_variation_rate,
                             -0.172 * u.deg / u.h,
                             rtol=1e-2)
    assert_quantity_allclose(argp_variation_rate,
                             0.282 * u.deg / u.h,
                             rtol=1e-2)
Ejemplo n.º 4
0
def test_long_propagation_preserves_orbit_elements(method):
    tof = 100 * u.year
    r_halleys = np.array([-9018878.63569932, -94116054.79839276, 22619058.69943215])  # km
    v_halleys = np.array([-49.95092305, -12.94843055, -4.29251577])  # km/s
    halleys = Orbit.from_vectors(Sun, r_halleys * u.km, v_halleys * u.km / u.s)

    params_ini = rv2coe(Sun.k.to(u.km**3 / u.s**2).value, r_halleys, v_halleys)[:-1]
    r_new, v_new = halleys.propagate(tof, method=method).rv()
    params_final = rv2coe(Sun.k.to(u.km**3 / u.s**2).value,
                          r_new.to(u.km).value, v_new.to(u.km / u.s).value)[:-1]
    print(params_ini, params_final)
    assert_quantity_allclose(params_ini, params_final)
Ejemplo n.º 5
0
def test_3rd_body_Curtis(test_params):
    # based on example 12.11 from Howard Curtis
    body = test_params['body']
    solar_system_ephemeris.set('de432s')

    j_date = 2454283.0 * u.day
    tof = (test_params['tof']).to(u.s).value
    body_r = build_ephem_interpolant(body, test_params['period'], (j_date, j_date + test_params['tof']), rtol=1e-2)

    epoch = Time(j_date, format='jd', scale='tdb')
    initial = Orbit.from_classical(Earth, *test_params['orbit'], epoch=epoch)
    r, v = cowell(initial, np.linspace(0, tof, 400), rtol=1e-10, ad=third_body,
                  k_third=body.k.to(u.km**3 / u.s**2).value, third_body=body_r)

    incs, raans, argps = [], [], []
    for ri, vi in zip(r, v):
        angles = Angle(rv2coe(Earth.k.to(u.km**3 / u.s**2).value, ri, vi)[2:5] * u.rad)  # inc, raan, argp
        angles = angles.wrap_at(180 * u.deg)
        incs.append(angles[0].value)
        raans.append(angles[1].value)
        argps.append(angles[2].value)

    # averaging over 5 last values in the way Curtis does
    inc_f, raan_f, argp_f = np.mean(incs[-5:]), np.mean(raans[-5:]), np.mean(argps[-5:])

    assert_quantity_allclose([(raan_f * u.rad).to(u.deg) - test_params['orbit'][3],
                              (inc_f * u.rad).to(u.deg) - test_params['orbit'][2],
                              (argp_f * u.rad).to(u.deg) - test_params['orbit'][4]],
                             [test_params['raan'], test_params['inc'], test_params['argp']],
                             rtol=1e-1)
Ejemplo n.º 6
0
def inertial_body_centered_to_pqw(r, v, source_body):
    """Converts position and velocity from inertial body-centered frame to perifocal frame.

    Parameters
    ----------
    r : ~astropy.units.Quantity
        Position vector in a inertial body-centered reference frame.
    v : ~astropy.units.Quantity
        Velocity vector in a inertial body-centered reference frame.
    source_body : Body
        Source body.

    Returns
    -------
    r_pqw, v_pqw : tuple (~astropy.units.Quantity)
        Position and velocity vectors in ICRS.


    """
    r = r.to("km").value
    v = v.to("km/s").value
    k = source_body.k.to("km^3 / s^2").value

    p, ecc, inc, _, _, nu = rv2coe(k, r, v)

    r_pqw = (np.array([cos(nu), sin(nu), 0 * nu]) * p / (1 + ecc * cos(nu))).T * u.km
    v_pqw = (np.array([-sin(nu), (ecc + cos(nu)), 0]) * sqrt(k / p)).T * u.km / u.s

    return r_pqw, v_pqw
Ejemplo n.º 7
0
def inertial_body_centered_to_pqw(r, v, source_body):
    """Converts position and velocity from inertial body-centered frame to perifocal frame.

    Parameters
    ----------
    r : ~astropy.units.Quantity
        Position vector in a inertial body-centered reference frame.
    v : ~astropy.units.Quantity
        Velocity vector in a inertial body-centered reference frame.
    source_body : Body
        Source body.

    Returns
    -------
    r_pqw, v_pqw : tuple (~astropy.units.Quantity)
        Position and velocity vectors in ICRS.


    """
    r = r.to('km').value
    v = v.to('km/s').value
    k = source_body.k.to('km^3 / s^2').value

    p, ecc, inc, _, _, nu = rv2coe(k, r, v)

    r_pqw = (np.array([cos(nu), sin(nu), 0 * nu]) * p / (1 + ecc * cos(nu))).T * u.km
    v_pqw = (np.array([-sin(nu), (ecc + cos(nu)), 0]) * sqrt(k / p)).T * u.km / u.s

    return r_pqw, v_pqw
Ejemplo n.º 8
0
def test_solar_pressure(t_days, deltas_expected, sun_r):
    # based on example 12.9 from Howard Curtis
    with solar_system_ephemeris.set("builtin"):
        j_date = 2_438_400.5 * u.day
        tof = 600 * u.day
        epoch = Time(j_date, format="jd", scale="tdb")

        initial = Orbit.from_classical(
            Earth,
            10085.44 * u.km,
            0.025422 * u.one,
            88.3924 * u.deg,
            45.38124 * u.deg,
            227.493 * u.deg,
            343.4268 * u.deg,
            epoch=epoch,
        )
        # in Curtis, the mean distance to Sun is used. In order to validate against it, we have to do the same thing
        sun_normalized = functools.partial(normalize_to_Curtis, sun_r=sun_r)

        rr, vv = cowell(
            Earth.k,
            initial.r,
            initial.v,
            np.linspace(0, (tof).to(u.s).value, 4000) * u.s,
            rtol=1e-8,
            ad=radiation_pressure,
            R=Earth.R.to(u.km).value,
            C_R=2.0,
            A_over_m=2e-4 / 100,
            Wdivc_s=Wdivc_sun.value,
            star=sun_normalized,
        )

        delta_eccs, delta_incs, delta_raans, delta_argps = [], [], [], []
        for ri, vi in zip(rr.to(u.km).value, vv.to(u.km / u.s).value):
            orbit_params = rv2coe(Earth.k.to(u.km**3 / u.s**2).value, ri, vi)
            delta_eccs.append(orbit_params[1] - initial.ecc.value)
            delta_incs.append((orbit_params[2] * u.rad).to(u.deg).value -
                              initial.inc.value)
            delta_raans.append((orbit_params[3] * u.rad).to(u.deg).value -
                               initial.raan.value)
            delta_argps.append((orbit_params[4] * u.rad).to(u.deg).value -
                               initial.argp.value)

        # averaging over 5 last values in the way Curtis does
        index = int(1.0 * t_days / tof.to(u.day).value * 4000  # type: ignore
                    )
        delta_ecc, delta_inc, delta_raan, delta_argp = (
            np.mean(delta_eccs[index - 5:index]),
            np.mean(delta_incs[index - 5:index]),
            np.mean(delta_raans[index - 5:index]),
            np.mean(delta_argps[index - 5:index]),
        )
        assert_quantity_allclose(
            [delta_ecc, delta_inc, delta_raan, delta_argp],
            deltas_expected,
            rtol=1e0,  # TODO: Excessively low, rewrite test?
            atol=1e-4,
        )
Ejemplo n.º 9
0
def test_solar_pressure():
    # based on example 12.9 from Howard Curtis
    with solar_system_ephemeris.set('builtin'):
        j_date = 2438400.5 * u.day
        tof = 600 * u.day
        sun_r = build_ephem_interpolant(Sun, 365 * u.day, (j_date, j_date + tof), rtol=1e-2)
        epoch = Time(j_date, format='jd', scale='tdb')
        drag_force_orbit = [10085.44 * u.km, 0.025422 * u.one, 88.3924 * u.deg,
                            45.38124 * u.deg, 227.493 * u.deg, 343.4268 * u.deg]

        initial = Orbit.from_classical(Earth, *drag_force_orbit, epoch=epoch)
        # in Curtis, the mean distance to Sun is used. In order to validate against it, we have to do the same thing
        sun_normalized = functools.partial(normalize_to_Curtis, sun_r=sun_r)

        r, v = cowell(initial, np.linspace(0, (tof).to(u.s).value, 4000), rtol=1e-8, ad=radiation_pressure,
                      R=Earth.R.to(u.km).value, C_R=2.0, A=2e-4, m=100, Wdivc_s=Sun.Wdivc.value, star=sun_normalized)

        delta_eccs, delta_incs, delta_raans, delta_argps = [], [], [], []
        for ri, vi in zip(r, v):
            orbit_params = rv2coe(Earth.k.to(u.km**3 / u.s**2).value, ri, vi)
            delta_eccs.append(orbit_params[1] - drag_force_orbit[1].value)
            delta_incs.append((orbit_params[2] * u.rad).to(u.deg).value - drag_force_orbit[2].value)
            delta_raans.append((orbit_params[3] * u.rad).to(u.deg).value - drag_force_orbit[3].value)
            delta_argps.append((orbit_params[4] * u.rad).to(u.deg).value - drag_force_orbit[4].value)

        # averaging over 5 last values in the way Curtis does
        for check in solar_pressure_checks:
            index = int(1.0 * check['t_days'] / tof.to(u.day).value * 4000)
            delta_ecc, delta_inc, delta_raan, delta_argp = np.mean(delta_eccs[index - 5:index]), \
                np.mean(delta_incs[index - 5:index]), np.mean(delta_raans[index - 5:index]), \
                np.mean(delta_argps[index - 5:index])
            assert_quantity_allclose([delta_ecc, delta_inc, delta_raan, delta_argp],
                                     check['deltas_expected'], rtol=1e-1, atol=1e-4)
Ejemplo n.º 10
0
def test_3rd_body_Curtis(test_params):
    # based on example 12.11 from Howard Curtis
    body = test_params['body']
    with solar_system_ephemeris.set('builtin'):
        j_date = 2454283.0 * u.day
        tof = (test_params['tof']).to(u.s).value
        body_r = build_ephem_interpolant(body, test_params['period'], (j_date, j_date + test_params['tof']), rtol=1e-2)

        epoch = Time(j_date, format='jd', scale='tdb')
        initial = Orbit.from_classical(Earth, *test_params['orbit'], epoch=epoch)
        r, v = cowell(initial, np.linspace(0, tof, 400), rtol=1e-10, ad=third_body,
                      k_third=body.k.to(u.km**3 / u.s**2).value, third_body=body_r)

        incs, raans, argps = [], [], []
        for ri, vi in zip(r, v):
            angles = Angle(rv2coe(Earth.k.to(u.km**3 / u.s**2).value, ri, vi)[2:5] * u.rad)  # inc, raan, argp
            angles = angles.wrap_at(180 * u.deg)
            incs.append(angles[0].value)
            raans.append(angles[1].value)
            argps.append(angles[2].value)

        # averaging over 5 last values in the way Curtis does
        inc_f, raan_f, argp_f = np.mean(incs[-5:]), np.mean(raans[-5:]), np.mean(argps[-5:])

        assert_quantity_allclose([(raan_f * u.rad).to(u.deg) - test_params['orbit'][3],
                                  (inc_f * u.rad).to(u.deg) - test_params['orbit'][2],
                                  (argp_f * u.rad).to(u.deg) - test_params['orbit'][4]],
                                 [test_params['raan'], test_params['inc'], test_params['argp']],
                                 rtol=1e-1)
Ejemplo n.º 11
0
def ORBel_frm_SV(svr, svv):
    from astropy import units as u
    from poliastro.bodies import Earth
    from poliastro.twobody import Orbit
    from poliastro.core.elements import rv2coe

    state_vector_r = svr
    state_vector_v = svv

    # unit vector conversion

    r = np.array(state_vector_r) * u.km
    v = np.array(state_vector_v) * u.km / u.s

    ss = Orbit.from_vectors(Earth, r, v)

    #standard_grav_param_earth
    k = 3.986004418 * 100000
    coe = rv2coe(k, r, v)
    #a,e,i,raom,apom,truean=coe[0,1,2,3,4,5]

    # print('\n')
    # print('---------- ORBIT CALCULATIONS ---------------------- \n\n')
    # print(' SEMI MAJOR AXIS IS -- : '+str(coe[0])+' Meters \n')
    # print(' ECCENTRICITY IS -- : '+str(coe[1])+' \n')
    # print(' INCLINATION IS -- : '+str(coe[2])+' radians \n') #unit confirmation?
    # print(' ARGUMENT OF RA IS -- : '+str(coe[3])+' radians \n')
    # print(' ARGUMENT OF PERIGEE IS -- : '+str(coe[4])+' radians \n')
    # print(' TRUE ANAMOLY IS -- : '+str(coe[5])+' radians \n')
    # print('------------------------------------')
    # print('\n')

    return coe
Ejemplo n.º 12
0
def mikkola(k, r0, v0, tof, rtol=None):
    """Raw algorithm for Mikkola's Kepler solver.

    Parameters
    ----------
    k : float
        Standard gravitational parameter of the attractor.
    r : ~np.array
        Position vector.
    v : ~np.array
        Velocity vector.
    tof : float
        Time of flight.
    rtol: float
        This method does not require tolerance since it is non-iterative.

    Returns
    -------
    rr : ~np.array
        Final velocity vector.
    vv : ~np.array
        Final velocity vector.
    Note
    ----
    Original paper: https://doi.org/10.1007/BF01235850
    """

    # Solving for the classical elements
    p, ecc, inc, raan, argp, nu = rv2coe(k, r0, v0)
    nu = mikkola_coe(k, p, ecc, inc, raan, argp, nu, tof)

    return coe2rv(k, p, ecc, inc, raan, argp, nu)
Ejemplo n.º 13
0
def markley(k, r0, v0, tof):
    """Solves the kepler problem by a non-iterative method. Relative error is
    around 1e-18, only limited by machine double-precision errors.

    Parameters
    ----------
    k : float
        Standar Gravitational parameter.
    r0 : ~np.array
        Initial position vector wrt attractor center.
    v0 : ~np.array
        Initial velocity vector.
    tof : float
        Time of flight.

    Returns
    -------
    rr: ~np.array
        Final position vector.
    vv: ~np.array
        Final velocity vector.

    Note
    ----
    The following algorithm was taken from http://dx.doi.org/10.1007/BF00691917.

    """
    # Solve first for eccentricity and mean anomaly
    p, ecc, inc, raan, argp, nu = rv2coe(k, r0, v0)
    nu = markley_coe(k, p, ecc, inc, raan, argp, nu, tof)

    return coe2rv(k, p, ecc, inc, raan, argp, nu)
Ejemplo n.º 14
0
def test_solar_pressure():
    # based on example 12.9 from Howard Curtis
    solar_system_ephemeris.set('de432s')

    j_date = 2438400.5 * u.day
    tof = 600 * u.day
    sun_r = build_ephem_interpolant(Sun, 365 * u.day, (j_date, j_date + tof), rtol=1e-2)
    epoch = Time(j_date, format='jd', scale='tdb')
    drag_force_orbit = [10085.44 * u.km, 0.025422 * u.one, 88.3924 * u.deg,
                        45.38124 * u.deg, 227.493 * u.deg, 343.4268 * u.deg]

    initial = Orbit.from_classical(Earth, *drag_force_orbit, epoch=epoch)
    # in Curtis, the mean distance to Sun is used. In order to validate against it, we have to do the same thing
    sun_normalized = functools.partial(normalize_to_Curtis, sun_r=sun_r)

    r, v = cowell(initial, np.linspace(0, (tof).to(u.s).value, 4000), rtol=1e-8, ad=radiation_pressure,
                  R=Earth.R.to(u.km).value, C_R=2.0, A=2e-4, m=100, Wdivc_s=Sun.Wdivc.value, star=sun_normalized)

    delta_as, delta_eccs, delta_incs, delta_raans, delta_argps, delta_hs = [], [], [], [], [], []
    for ri, vi in zip(r, v):
        orbit_params = rv2coe(Earth.k.to(u.km**3 / u.s**2).value, ri, vi)
        delta_eccs.append(orbit_params[1] - drag_force_orbit[1].value)
        delta_incs.append((orbit_params[2] * u.rad).to(u.deg).value - drag_force_orbit[2].value)
        delta_raans.append((orbit_params[3] * u.rad).to(u.deg).value - drag_force_orbit[3].value)
        delta_argps.append((orbit_params[4] * u.rad).to(u.deg).value - drag_force_orbit[4].value)

    # averaging over 5 last values in the way Curtis does
    for check in solar_pressure_checks:
        index = int(1.0 * check['t_days'] / tof.to(u.day).value * 4000)
        delta_ecc, delta_inc, delta_raan, delta_argp = np.mean(delta_eccs[index - 5:index]), \
            np.mean(delta_incs[index - 5:index]), np.mean(delta_raans[index - 5:index]), \
            np.mean(delta_argps[index - 5:index])
        assert_quantity_allclose([delta_ecc, delta_inc, delta_raan, delta_argp],
                                 check['deltas_expected'], rtol=1e-1, atol=1e-4)
Ejemplo n.º 15
0
    def a_d(t0, u_, k):
        r = u_[:3]
        v = u_[3:]
        nu = rv2coe(k, r, v)[-1]
        beta_ = beta_0_ * np.sign(
            np.cos(nu))  # The sign of ß reverses at minor axis crossings

        w_ = cross(r, v) / norm(cross(r, v))
        accel_v = f * (np.cos(beta_) * thrust_unit + np.sin(beta_) * w_)
        return accel_v
Ejemplo n.º 16
0
    def a_d(t0, u_, k_):
        r_ = u_[:3]
        v_ = u_[3:]
        nu = rv2coe(k_, r_, v_)[-1]
        beta_ = beta_0 * np.sign(
            np.cos(nu))  # The sign of ß reverses at minor axis crossings

        w_ = cross(r_, v_) / norm(cross(r_, v_))
        accel_v = f * (np.cos(beta_) * thrust_unit + np.sin(beta_) * w_)
        return accel_v
Ejemplo n.º 17
0
def hohmann(k, rv, r_f):
    r"""Calculate the Hohmann maneuver velocities and the duration of the maneuver.

    By defining the relationship between orbit radius:

    .. math::
        a_{trans} = \frac{r_{i} + r_{f}}{2}

    The Hohmann maneuver velocities can be expressed as:

    .. math::
        \begin{align}
            \Delta v_{a} &= \sqrt{\frac{2\mu}{r_{i}} - \frac{\mu}{a_{trans}}} - v_{i}\\
            \Delta v_{b} &= \sqrt{\frac{\mu}{r_{f}}} - \sqrt{\frac{2\mu}{r_{f}} - \frac{\mu}{a_{trans}}}
        \end{align}

    The time that takes to complete the maneuver can be computed as:

    .. math::
        \tau_{trans} = \pi \sqrt{\frac{(a_{trans})^{3}}{\mu}}

    Parameters
    ----------
    k : float
        Standard Gravitational parameter
    rv : numpy.ndarray, numpy.ndarray
        Position and velocity vectors
    r_f : float
        Final orbital radius

    """
    _, ecc, inc, raan, argp, nu = rv2coe(k, *rv)
    h_i = norm(cross(*rv))
    p_i = h_i**2 / k

    r_i, v_i = rv_pqw(k, p_i, ecc, nu)

    r_i = norm(r_i)
    v_i = norm(v_i)
    a_trans = (r_i + r_f) / 2

    dv_a = np.sqrt(2 * k / r_i - k / a_trans) - v_i
    dv_b = np.sqrt(k / r_f) - np.sqrt(2 * k / r_f - k / a_trans)

    dv_a = np.array([0, dv_a, 0])
    dv_b = np.array([0, -dv_b, 0])

    rot_matrix = coe_rotation_matrix(inc, raan, argp)

    dv_a = rot_matrix @ dv_a
    dv_b = rot_matrix @ dv_b

    t_trans = np.pi * np.sqrt(a_trans**3 / k)

    return dv_a, dv_b, t_trans
Ejemplo n.º 18
0
    def a_d(t0, u_, k):
        r = u_[:3]
        v = u_[3:]
        nu = rv2coe(k, r, v)[-1]
        beta_ = beta_0_ * np.sign(
            np.cos(nu)
        )  # The sign of ß reverses at minor axis crossings

        w_ = cross(r, v) / norm(cross(r, v))
        accel_v = f * (np.cos(beta_) * thrust_unit + np.sin(beta_) * w_)
        return accel_v
Ejemplo n.º 19
0
def test_3rd_body_Curtis(test_params):
    # based on example 12.11 from Howard Curtis
    body = test_params["body"]
    with solar_system_ephemeris.set("builtin"):
        j_date = 2454283.0 * u.day
        tof = (test_params["tof"]).to(u.s).value
        body_r = build_ephem_interpolant(
            body,
            test_params["period"],
            (j_date, j_date + test_params["tof"]),
            rtol=1e-2,
        )

        epoch = Time(j_date, format="jd", scale="tdb")
        initial = Orbit.from_classical(Earth,
                                       *test_params["orbit"],
                                       epoch=epoch)
        rr, vv = cowell(
            Earth.k,
            initial.r,
            initial.v,
            np.linspace(0, tof, 400) * u.s,
            rtol=1e-10,
            ad=third_body,
            k_third=body.k.to(u.km**3 / u.s**2).value,
            perturbation_body=body_r,
        )

        incs, raans, argps = [], [], []
        for ri, vi in zip(rr.to(u.km).value, vv.to(u.km / u.s).value):
            angles = Angle(
                rv2coe(Earth.k.to(u.km**3 / u.s**2).value, ri, vi)[2:5] *
                u.rad)  # inc, raan, argp
            angles = angles.wrap_at(180 * u.deg)
            incs.append(angles[0].value)
            raans.append(angles[1].value)
            argps.append(angles[2].value)

        # averaging over 5 last values in the way Curtis does
        inc_f, raan_f, argp_f = (
            np.mean(incs[-5:]),
            np.mean(raans[-5:]),
            np.mean(argps[-5:]),
        )

        assert_quantity_allclose(
            [
                (raan_f * u.rad).to(u.deg) - test_params["orbit"][3],
                (inc_f * u.rad).to(u.deg) - test_params["orbit"][2],
                (argp_f * u.rad).to(u.deg) - test_params["orbit"][4],
            ],
            [test_params["raan"], test_params["inc"], test_params["argp"]],
            rtol=1e-1,
        )
Ejemplo n.º 20
0
    def to_classical(self):
        """Converts to classical orbital elements representation.

        """
        (p, ecc, inc, raan, argp, nu) = rv2coe(
            self.attractor.k.to(u.km**3 / u.s**2).value,
            self.r.to(u.km).value,
            self.v.to(u.km / u.s).value)

        return classical.ClassicalState(self.attractor, p * u.km, ecc * u.one,
                                        inc * u.rad, raan * u.rad,
                                        argp * u.rad, nu * u.rad)
Ejemplo n.º 21
0
    def propagate(self, value, method=mean_motion, rtol=1e-10, **kwargs):
        """Propagates an orbit.

        If value is true anomaly, propagate orbit to this anomaly and return the result.
        Otherwise, if time is provided, propagate this `Orbit` some `time` and return the result.

        Parameters
        ----------
        value : Multiple options
            True anomaly values or time values. If given an angle, it will always propagate forward.
        rtol : float, optional
            Relative tolerance for the propagation algorithm, default to 1e-10.
        method : function, optional
            Method used for propagation
        **kwargs
            parameters used in perturbation models

        """
        if hasattr(value, "unit") and value.unit in ("rad", "deg"):
            p, ecc, inc, raan, argp, _ = rv2coe(
                self.attractor.k.to(u.km**3 / u.s**2).value,
                self.r.to(u.km).value,
                self.v.to(u.km / u.s).value,
            )

            # Compute time of flight for correct epoch
            M = nu_to_M(self.nu, self.ecc)
            new_M = nu_to_M(value, self.ecc)
            time_of_flight = Angle(new_M - M).wrap_at(360 * u.deg) / self.n

            return self.from_classical(
                self.attractor,
                p / (1.0 - ecc**2) * u.km,
                ecc * u.one,
                inc * u.rad,
                raan * u.rad,
                argp * u.rad,
                value,
                epoch=self.epoch + time_of_flight,
                plane=self._plane,
            )
        else:
            if isinstance(value,
                          time.Time) and not isinstance(value, time.TimeDelta):
                time_of_flight = value - self.epoch
            else:
                time_of_flight = time.TimeDelta(value)

            return propagate(self,
                             time_of_flight,
                             method=method,
                             rtol=rtol,
                             **kwargs)
Ejemplo n.º 22
0
    def a_d(t0, u_, k):
        r = u_[:3]
        v = u_[3:]
        nu = rv2coe(k, r, v)[-1]

        alpha_ = nu - np.pi / 2

        r_ = r / norm(r)
        w_ = cross(r, v) / norm(cross(r, v))
        s_ = cross(w_, r_)
        accel_v = f * (np.cos(alpha_) * s_ + np.sin(alpha_) * r_)
        return accel_v
Ejemplo n.º 23
0
def mean_motion(k, r0, v0, tof):
    r"""Propagates orbit using mean motion. This algorithm depends on the geometric shape of the
    orbit.

    For the case of the strong elliptic or strong hyperbolic orbits:

    ..  math::

        M = M_{0} + \frac{\mu^{2}}{h^{3}}\left ( 1 -e^{2}\right )^{\frac{3}{2}}t

    .. versionadded:: 0.9.0


    Parameters
    ----------
    k : float
        Standar Gravitational parameter
    r0 : ~astropy.units.Quantity
        Initial position vector wrt attractor center.
    v0 : ~astropy.units.Quantity
        Initial velocity vector.
    tof : float
        Time of flight (s).

    Note
    ----
    This method takes initial :math:`\vec{r}, \vec{v}`, calculates classical orbit parameters,
    increases mean anomaly and performs inverse transformation to get final :math:`\vec{r}, \vec{v}`
    The logic is based on formulae (4), (6) and (7) from http://dx.doi.org/10.1007/s10569-013-9476-9

    """

    # get the initial true anomaly and orbit parameters that are constant over time
    p, ecc, inc, raan, argp, nu0 = rv2coe(k, r0, v0)

    # get the initial mean anomaly
    M0 = nu_to_M(nu0, ecc)
    # strong elliptic or strong hyperbolic orbits
    if np.abs(ecc - 1.0) > 1e-2:
        a = p / (1.0 - ecc ** 2)
        # given the initial mean anomaly, calculate mean anomaly
        # at the end, mean motion (n) equals sqrt(mu / |a^3|)
        M = M0 + tof * np.sqrt(k / np.abs(a ** 3))
        nu = M_to_nu(M, ecc)

    # near-parabolic orbit
    else:
        q = p * np.abs(1.0 - ecc) / np.abs(1.0 - ecc ** 2)
        # mean motion n = sqrt(mu / 2 q^3) for parabolic orbit
        M = M0 + tof * np.sqrt(k / 2.0 / (q ** 3))
        nu = M_to_nu(M, ecc)

    return coe2rv(k, p, ecc, inc, raan, argp, nu)
Ejemplo n.º 24
0
def mean_motion(k, r0, v0, tof):
    r"""Propagates orbit using mean motion. This algorithm depends on the geometric shape of the
    orbit.

    For the case of the strong elliptic or strong hyperbolic orbits:

    ..  math::

        M = M_{0} + \frac{\mu^{2}}{h^{3}}\left ( 1 -e^{2}\right )^{\frac{3}{2}}t

    .. versionadded:: 0.9.0


    Parameters
    ----------
    k : float
        Standar Gravitational parameter
    r0 : ~astropy.units.Quantity
        Initial position vector wrt attractor center.
    v0 : ~astropy.units.Quantity
        Initial velocity vector.
    tof : float
        Time of flight (s).

    Note
    ----
    This method takes initial :math:`\vec{r}, \vec{v}`, calculates classical orbit parameters,
    increases mean anomaly and performs inverse transformation to get final :math:`\vec{r}, \vec{v}`
    The logic is based on formulae (4), (6) and (7) from http://dx.doi.org/10.1007/s10569-013-9476-9

    """

    # get the initial true anomaly and orbit parameters that are constant over time
    p, ecc, inc, raan, argp, nu0 = rv2coe(k, r0, v0)

    # get the initial mean anomaly
    M0 = nu_to_M(nu0, ecc)
    # strong elliptic or strong hyperbolic orbits
    if np.abs(ecc - 1.0) > 1e-2:
        a = p / (1.0 - ecc**2)
        # given the initial mean anomaly, calculate mean anomaly
        # at the end, mean motion (n) equals sqrt(mu / |a^3|)
        M = M0 + tof * np.sqrt(k / np.abs(a**3))
        nu = M_to_nu(M, ecc)

    # near-parabolic orbit
    else:
        q = p * np.abs(1.0 - ecc) / np.abs(1.0 - ecc**2)
        # mean motion n = sqrt(mu / 2 q^3) for parabolic orbit
        M = M0 + tof * np.sqrt(k / 2.0 / (q**3))
        nu = M_to_nu(M, ecc)

    return coe2rv(k, p, ecc, inc, raan, argp, nu)
Ejemplo n.º 25
0
    def a_d(t0, u_, k):
        r = u_[:3]
        v = u_[3:]
        nu = rv2coe(k, r, v)[-1]

        alpha_ = nu - np.pi / 2

        r_ = r / norm(r)
        w_ = cross(r, v) / norm(cross(r, v))
        s_ = cross(w_, r_)
        accel_v = f * (np.cos(alpha_) * s_ + np.sin(alpha_) * r_)
        return accel_v
Ejemplo n.º 26
0
    def plot_regimes(self, save_path=None, display=True):
        s = time.time()
        lla = np.array([ecef2lla(x[:3] @ self.trans_matrix[0]) for x in self.x_true[0]])

        coes = np.array(rv2coe(k=Earth.k.to_value(u.km ** 3 / u.s ** 2), r=x[:3] / 1000, v=x[3:] / 1000) for x in self.x_true[0])

        x = lla[:, 2] / 1000
        y = coes[:, 1]

        plot_regimes(np.column_stack((x, y)), save_path=save_path, display=display)
        e = time.time()
        self.runtime['plot_visibility'] += e - s
Ejemplo n.º 27
0
def test_3rd_body_Curtis(test_params):
    # based on example 12.11 from Howard Curtis
    body = test_params["body"]
    with solar_system_ephemeris.set("builtin"):
        j_date = 2454283.0 * u.day
        tof = (test_params["tof"]).to(u.s).value
        body_r = build_ephem_interpolant(
            body,
            test_params["period"],
            (j_date, j_date + test_params["tof"]),
            rtol=1e-2,
        )

        epoch = Time(j_date, format="jd", scale="tdb")
        initial = Orbit.from_classical(Earth, *test_params["orbit"], epoch=epoch)
        rr, vv = cowell(
            Earth.k,
            initial.r,
            initial.v,
            np.linspace(0, tof, 400) * u.s,
            rtol=1e-10,
            ad=third_body,
            k_third=body.k.to(u.km ** 3 / u.s ** 2).value,
            third_body=body_r,
        )

        incs, raans, argps = [], [], []
        for ri, vi in zip(rr.to(u.km).value, vv.to(u.km / u.s).value):
            angles = Angle(
                rv2coe(Earth.k.to(u.km ** 3 / u.s ** 2).value, ri, vi)[2:5] * u.rad
            )  # inc, raan, argp
            angles = angles.wrap_at(180 * u.deg)
            incs.append(angles[0].value)
            raans.append(angles[1].value)
            argps.append(angles[2].value)

        # averaging over 5 last values in the way Curtis does
        inc_f, raan_f, argp_f = (
            np.mean(incs[-5:]),
            np.mean(raans[-5:]),
            np.mean(argps[-5:]),
        )

        assert_quantity_allclose(
            [
                (raan_f * u.rad).to(u.deg) - test_params["orbit"][3],
                (inc_f * u.rad).to(u.deg) - test_params["orbit"][2],
                (argp_f * u.rad).to(u.deg) - test_params["orbit"][4],
            ],
            [test_params["raan"], test_params["inc"], test_params["argp"]],
            rtol=1e-1,
        )
Ejemplo n.º 28
0
def test_convert_between_coe_and_rv_is_transitive():
    k = Earth.k.to(u.km**3 / u.s**2).value  # u.km**3 / u.s**2
    p = 11067.790  # u.km
    ecc = 0.83285  # u.one
    inc = np.deg2rad(87.87)  # u.rad
    raan = np.deg2rad(227.89)  # u.rad
    argp = np.deg2rad(53.38)  # u.rad
    nu = np.deg2rad(92.335)  # u.rad

    expected_res = (p, ecc, inc, raan, argp, nu)

    res = rv2coe(k, *coe2rv(k, *expected_res))

    assert_allclose(res, expected_res)
Ejemplo n.º 29
0
def test_propagation_mean_motion_parabolic():
    # example from Howard Curtis (3rd edition), section 3.5, problem 3.15
    p = 2.0 * 6600 * u.km
    _a = 0.0 * u.deg
    orbit = Orbit.parabolic(Earth, p, _a, _a, _a, _a)
    orbit = orbit.propagate(0.8897 / 2.0 * u.h, method=mean_motion)

    _, _, _, _, _, nu0 = rv2coe(Earth.k.to(u.km**3 / u.s**2).value,
                                orbit.r.to(u.km).value,
                                orbit.v.to(u.km / u.s).value)
    assert_quantity_allclose(nu0, np.deg2rad(90.0), rtol=1e-4)

    orbit = Orbit.parabolic(Earth, p, _a, _a, _a, _a)
    orbit = orbit.propagate(36.0 * u.h, method=mean_motion)
    assert_quantity_allclose(norm(orbit.r), 304700.0 * u.km, rtol=1e-4)
Ejemplo n.º 30
0
def eclipse_function(k, u_, r_sec, R_sec, R_primary, umbra=True):
    """Calculates a continuous shadow function.

    Parameters
    ----------
    k: float
        Standard gravitational parameter (km^3 / s^2).
    u_: ~np.array
        Satellite position and velocity vector with respect to the primary body.
    r_sec: ~np.array
        Position vector of the secondary body with respect to the primary body.
    R_sec: float
        Equatorial radius of the secondary body.
    R_primary: float
        Equatorial radius of the primary body.
    umbra: bool
        Whether to calculate the shadow function for umbra or penumbra, defaults to True
        i.e. calculates for umbra.

    Note
    ----
    The shadow function is taken from Escobal, P. (1985). Methods of orbit determination.
    The current implementation assumes circular bodies and doesn't account for flattening.

    """
    # Plus or minus condition
    pm = 1 if umbra else -1
    p, ecc, inc, raan, argp, nu = rv2coe(k, u_[:3], u_[3:])

    PQW = coe_rotation_matrix(inc, raan, argp)
    # Make arrays contiguous for faster dot product with numba.
    P_, Q_ = np.ascontiguousarray(PQW[:, 0]), np.ascontiguousarray(PQW[:, 1])

    r_sec_norm = norm(r_sec)
    beta = np.dot(P_, r_sec) / r_sec_norm
    zeta = np.dot(Q_, r_sec) / r_sec_norm

    sin_delta_shadow = np.sin((R_sec - pm * R_primary) / r_sec_norm)

    cos_psi = beta * np.cos(nu) + zeta * np.sin(nu)
    shadow_function = (((R_primary**2) *
                        (1 + ecc * np.cos(nu))**2) + (p**2) * (cos_psi**2) -
                       p**2 + pm * (2 * p * R_primary * cos_psi) *
                       (1 + ecc * np.cos(nu)) * sin_delta_shadow)

    return shadow_function
Ejemplo n.º 31
0
    def to_classical(self):
        """Converts to classical orbital elements representation.

        """
        (p, ecc, inc, raan, argp, nu
         ) = rv2coe(self.attractor.k.to(u.km ** 3 / u.s ** 2).value,
                    self.r.to(u.km).value,
                    self.v.to(u.km / u.s).value)

        return classical.ClassicalState(
            self.attractor,
            p * u.km,
            ecc * u.one,
            inc * u.rad,
            raan * u.rad,
            argp * u.rad,
            nu * u.rad)
Ejemplo n.º 32
0
def recseries(k, r0, v0, tof, method="rtol", order=8, numiter=100, rtol=1e-8):
    """Kepler solver for elliptical orbits with recursive series approximation
    method. The order of the series is a user defined parameter.

    Parameters
    ----------
    k : float
        Standard gravitational parameter of the attractor.
    r0 : numpy.ndarray
        Position vector.
    v0 : numpy.ndarray
        Velocity vector.
    tof : float
        Time of flight.
    method : str
        Type of termination method ('rtol','order')
    order : int, optional
        Order of recursion, defaults to 8.
    numiter : int, optional
        Number of iterations, defaults to 100.
    rtol : float, optional
        Relative error for accuracy of the method, defaults to 1e-8.

    Returns
    -------
    rr : numpy.ndarray
        Final position vector.
    vv : numpy.ndarray
        Final velocity vector.

    Notes
    -----
    This algorithm uses series discussed in the paper *Recursive solution to
    Kepler’s problem for elliptical orbits - application in robust
    Newton-Raphson and co-planar closest approach estimation*
    with DOI: http://dx.doi.org/10.13140/RG.2.2.18578.58563/1
    """

    # Solve first for eccentricity and mean anomaly
    p, ecc, inc, raan, argp, nu = rv2coe(k, r0, v0)
    nu = recseries_coe(k, p, ecc, inc, raan, argp, nu, tof, method, order,
                       numiter, rtol)

    return coe2rv(k, p, ecc, inc, raan, argp, nu)
Ejemplo n.º 33
0
def test_J3_propagation_Earth(test_params):
    # Nai-ming Qi, Qilong Sun, Yong Yang, (2018) "Effect of J3 perturbation on satellite position in LEO",
    # Aircraft Engineering and  Aerospace Technology, Vol. 90 Issue: 1,
    # pp.74-86, https://doi.org/10.1108/AEAT-03-2015-0092
    a_ini = 8970.667 * u.km
    ecc_ini = 0.25 * u.one
    raan_ini = 1.047 * u.rad
    nu_ini = 0.0 * u.rad
    argp_ini = 1.0 * u.rad
    inc_ini = test_params['inc']

    k = Earth.k.to(u.km**3 / u.s**2).value

    orbit = Orbit.from_classical(Earth, a_ini, ecc_ini, inc_ini, raan_ini, argp_ini, nu_ini)

    tof = (10.0 * u.day).to(u.s).value
    r_J2, v_J2 = cowell(orbit, np.linspace(0, tof, int(1e+3)), ad=J2_perturbation,
                        J2=Earth.J2.value, R=Earth.R.to(u.km).value, rtol=1e-8)
    a_J2J3 = lambda t0, u_, k_: J2_perturbation(t0, u_, k_, J2=Earth.J2.value, R=Earth.R.to(u.km).value) + \
        J3_perturbation(t0, u_, k_, J3=Earth.J3.value, R=Earth.R.to(u.km).value)

    r_J3, v_J3 = cowell(orbit, np.linspace(0, tof, int(1e+3)), ad=a_J2J3, rtol=1e-8)

    a_values_J2 = np.array([rv2coe(k, ri, vi)[0] / (1.0 - rv2coe(k, ri, vi)[1] ** 2) for ri, vi in zip(r_J2, v_J2)])
    a_values_J3 = np.array([rv2coe(k, ri, vi)[0] / (1.0 - rv2coe(k, ri, vi)[1] ** 2) for ri, vi in zip(r_J3, v_J3)])
    da_max = np.max(np.abs(a_values_J2 - a_values_J3))

    ecc_values_J2 = np.array([rv2coe(k, ri, vi)[1] for ri, vi in zip(r_J2, v_J2)])
    ecc_values_J3 = np.array([rv2coe(k, ri, vi)[1] for ri, vi in zip(r_J3, v_J3)])
    decc_max = np.max(np.abs(ecc_values_J2 - ecc_values_J3))

    inc_values_J2 = np.array([rv2coe(k, ri, vi)[2] for ri, vi in zip(r_J2, v_J2)])
    inc_values_J3 = np.array([rv2coe(k, ri, vi)[2] for ri, vi in zip(r_J3, v_J3)])
    dinc_max = np.max(np.abs(inc_values_J2 - inc_values_J3))

    assert_quantity_allclose(dinc_max, test_params['dinc_max'], rtol=1e-1, atol=1e-7)
    assert_quantity_allclose(decc_max, test_params['decc_max'], rtol=1e-1, atol=1e-7)
    try:
        assert_quantity_allclose(da_max * u.km, test_params['da_max'])
    except AssertionError as exc:
        pytest.xfail('this assertion disagrees with the paper')
Ejemplo n.º 34
0
def test_J3_propagation_Earth(test_params):
    # Nai-ming Qi, Qilong Sun, Yong Yang, (2018) "Effect of J3 perturbation on satellite position in LEO",
    # Aircraft Engineering and  Aerospace Technology, Vol. 90 Issue: 1,
    # pp.74-86, https://doi.org/10.1108/AEAT-03-2015-0092
    a_ini = 8970.667 * u.km
    ecc_ini = 0.25 * u.one
    raan_ini = 1.047 * u.rad
    nu_ini = 0.0 * u.rad
    argp_ini = 1.0 * u.rad
    inc_ini = test_params['inc']

    k = Earth.k.to(u.km**3 / u.s**2).value

    orbit = Orbit.from_classical(Earth, a_ini, ecc_ini, inc_ini, raan_ini, argp_ini, nu_ini)

    tof = (10.0 * u.day).to(u.s).value
    r_J2, v_J2 = cowell(orbit, np.linspace(0, tof, int(1e+3)), ad=J2_perturbation,
                        J2=Earth.J2.value, R=Earth.R.to(u.km).value, rtol=1e-8)
    a_J2J3 = lambda t0, u_, k_: J2_perturbation(t0, u_, k_, J2=Earth.J2.value, R=Earth.R.to(u.km).value) + \
        J3_perturbation(t0, u_, k_, J3=Earth.J3.value, R=Earth.R.to(u.km).value)

    r_J3, v_J3 = cowell(orbit, np.linspace(0, tof, int(1e+3)), ad=a_J2J3, rtol=1e-8)

    a_values_J2 = np.array([rv2coe(k, ri, vi)[0] / (1.0 - rv2coe(k, ri, vi)[1] ** 2) for ri, vi in zip(r_J2, v_J2)])
    a_values_J3 = np.array([rv2coe(k, ri, vi)[0] / (1.0 - rv2coe(k, ri, vi)[1] ** 2) for ri, vi in zip(r_J3, v_J3)])
    da_max = np.max(np.abs(a_values_J2 - a_values_J3))

    ecc_values_J2 = np.array([rv2coe(k, ri, vi)[1] for ri, vi in zip(r_J2, v_J2)])
    ecc_values_J3 = np.array([rv2coe(k, ri, vi)[1] for ri, vi in zip(r_J3, v_J3)])
    decc_max = np.max(np.abs(ecc_values_J2 - ecc_values_J3))

    inc_values_J2 = np.array([rv2coe(k, ri, vi)[2] for ri, vi in zip(r_J2, v_J2)])
    inc_values_J3 = np.array([rv2coe(k, ri, vi)[2] for ri, vi in zip(r_J3, v_J3)])
    dinc_max = np.max(np.abs(inc_values_J2 - inc_values_J3))

    assert_quantity_allclose(dinc_max, test_params['dinc_max'], rtol=1e-1, atol=1e-7)
    assert_quantity_allclose(decc_max, test_params['decc_max'], rtol=1e-1, atol=1e-7)
    try:
        assert_quantity_allclose(da_max * u.km, test_params['da_max'])
    except AssertionError as exc:
        pytest.xfail('this assertion disagrees with the paper')
Ejemplo n.º 35
0
def test_rv2coe_iss_exact(engine, earth_elliptic):
    r, v = earth_elliptic

    expected_result = engine.rv2coe(matlab.double(r),
                                    matlab.double(v),
                                    nargout=11)
    p, _, ecc, inc, omega, argp, nu, *_ = expected_result

    # Load constants to make all computations equivalent
    engine.constastro(nargout=0)
    k = engine.workspace["mu"]

    result = rv2coe(k, np.array(r), np.array(v))

    assert result[0] == p
    assert result[1] == ecc
    assert result[2] == inc
    assert result[3] == omega
    assert result[4] == argp
    assert result[5] == nu
Ejemplo n.º 36
0
def mean_motion(k, r0, v0, tof):
    r"""Propagates orbit using mean motion

    .. versionadded:: 0.9.0

    Parameters
    ----------
    orbit : ~poliastro.twobody.orbit.Orbit
        the Orbit object to propagate.
    tof : float
        Time of flight (s).

    Notes
    -----
    This method takes initial :math:`\vec{r}, \vec{v}`, calculates classical orbit parameters,
    increases mean anomaly and performs inverse transformation to get final :math:`\vec{r}, \vec{v}`
    The logic is based on formulae (4), (6) and (7) from http://dx.doi.org/10.1007/s10569-013-9476-9

    """

    # get the initial true anomaly and orbit parameters that are constant over time
    p, ecc, inc, raan, argp, nu0 = rv2coe(k, r0, v0)

    # get the initial mean anomaly
    M0 = nu_to_M(nu0, ecc)
    # strong elliptic or strong hyperbolic orbits
    if np.abs(ecc - 1.0) > 1e-2:
        a = p / (1.0 - ecc**2)
        # given the initial mean anomaly, calculate mean anomaly
        # at the end, mean motion (n) equals sqrt(mu / |a^3|)
        M = M0 + tof * np.sqrt(k / np.abs(a**3))
        nu = M_to_nu(M, ecc)

    # near-parabolic orbit
    else:
        q = p * np.abs(1.0 - ecc) / np.abs(1.0 - ecc**2)
        # mean motion n = sqrt(mu / 2 q^3) for parabolic orbit
        M = M0 + tof * np.sqrt(k / 2.0 / (q**3))
        nu = M_to_nu(M, ecc)

    return coe2rv(k, p, ecc, inc, raan, argp, nu)
Ejemplo n.º 37
0
    def propagate(self, value, method=mean_motion, rtol=1e-10, **kwargs):
        """Propagates an orbit.

        If value is true anomaly, propagate orbit to this anomaly and return the result.
        Otherwise, if time is provided, propagate this `Orbit` some `time` and return the result.

        Parameters
        ----------
        value : Multiple options
            True anomaly values,
            Time values.
        rtol : float, optional
            Relative tolerance for the propagation algorithm, default to 1e-10.
        method : function, optional
            Method used for propagation
        **kwargs
            parameters used in perturbation models

        """
        if hasattr(value, "unit") and value.unit in ('rad', 'deg'):
            p, ecc, inc, raan, argp, _ = rv2coe(
                self.attractor.k.to(u.km**3 / u.s**2).value,
                self.r.to(u.km).value,
                self.v.to(u.km / u.s).value)

            return self.from_classical(self.attractor,
                                       p / (1.0 - ecc**2) * u.km, ecc * u.one,
                                       inc * u.rad, raan * u.rad, argp * u.rad,
                                       value)
        else:
            if isinstance(value,
                          time.Time) and not isinstance(value, time.TimeDelta):
                time_of_flight = value - self.epoch
            else:
                time_of_flight = time.TimeDelta(value)

            return propagate(self,
                             time_of_flight,
                             method=method,
                             rtol=rtol,
                             **kwargs)
Ejemplo n.º 38
0
def mean_motion(k, r0, v0, tof):
    r"""Propagates orbit using mean motion

    .. versionadded:: 0.9.0

    Parameters
    ----------
    orbit : ~poliastro.twobody.orbit.Orbit
        the Orbit object to propagate.
    tof : float
        Time of flight (s).

    Notes
    -----
    This method takes initial :math:`\vec{r}, \vec{v}`, calculates classical orbit parameters,
    increases mean anomaly and performs inverse transformation to get final :math:`\vec{r}, \vec{v}`
    The logic is based on formulae (4), (6) and (7) from http://dx.doi.org/10.1007/s10569-013-9476-9

    """

    # get the initial true anomaly and orbit parameters that are constant over time
    p, ecc, inc, raan, argp, nu0 = rv2coe(k, r0, v0)

    # get the initial mean anomaly
    M0 = nu_to_M(nu0, ecc)
    # strong elliptic or strong hyperbolic orbits
    if np.abs(ecc - 1.0) > 1e-2:
        a = p / (1.0 - ecc ** 2)
        # given the initial mean anomaly, calculate mean anomaly
        # at the end, mean motion (n) equals sqrt(mu / |a^3|)
        M = M0 + tof * np.sqrt(k / np.abs(a ** 3))
        nu = M_to_nu(M, ecc)

    # near-parabolic orbit
    else:
        q = p * np.abs(1.0 - ecc) / np.abs(1.0 - ecc ** 2)
        # mean motion n = sqrt(mu / 2 q^3) for parabolic orbit
        M = M0 + tof * np.sqrt(k / 2.0 / (q ** 3))
        nu = M_to_nu(M, ecc)

    return coe2rv(k, p, ecc, inc, raan, argp, nu)
Ejemplo n.º 39
0
def test_rv2coe_iss_approximate(engine, earth_elliptic):
    r, v = earth_elliptic
    rtol = atol = 1e-13

    expected_result = engine.rv2coe(matlab.double(r),
                                    matlab.double(v),
                                    nargout=11)
    p, _, ecc, inc, omega, argp, nu, *_ = expected_result

    # Load constants to make all computations equivalent
    engine.constastro(nargout=0)
    k = engine.workspace["mu"]

    result = rv2coe(k, np.array(r), np.array(v))

    assert result[0] == approx(p, rel=rtol, abs=atol)
    assert result[1] == approx(ecc, rel=rtol, abs=atol)
    assert result[2] == approx(inc, rel=rtol, abs=atol)
    assert result[3] == approx(omega, rel=rtol, abs=atol)
    assert result[4] == approx(argp, rel=rtol, abs=atol)
    assert result[5] == approx(nu, rel=rtol, abs=atol)
Ejemplo n.º 40
0
def test_propagation_parabolic(propagator):
    # example from Howard Curtis (3rd edition), section 3.5, problem 3.15
    # TODO: add parabolic solver in some parabolic propagators, refer to #417
    if propagator in [mikkola, gooding]:
        pytest.skip()

    p = 2.0 * 6600 * u.km
    _a = 0.0 * u.deg
    orbit = Orbit.parabolic(Earth, p, _a, _a, _a, _a)
    orbit = orbit.propagate(0.8897 / 2.0 * u.h, method=propagator)

    _, _, _, _, _, nu0 = rv2coe(
        Earth.k.to(u.km ** 3 / u.s ** 2).value,
        orbit.r.to(u.km).value,
        orbit.v.to(u.km / u.s).value,
    )
    assert_quantity_allclose(nu0, np.deg2rad(90.0), rtol=1e-4)

    orbit = Orbit.parabolic(Earth, p, _a, _a, _a, _a)
    orbit = orbit.propagate(36.0 * u.h, method=propagator)
    assert_quantity_allclose(norm(orbit.r), 304700.0 * u.km, rtol=1e-4)
Ejemplo n.º 41
0
    def propagate(self, value, method=mean_motion, rtol=1e-10, **kwargs):
        """Propagates an orbit.

        If value is true anomaly, propagate orbit to this anomaly and return the result.
        Otherwise, if time is provided, propagate this `Orbit` some `time` and return the result.

        Parameters
        ----------
        value : Multiple options
            True anomaly values or time values. If given an angle, it will always propagate forward.
        rtol : float, optional
            Relative tolerance for the propagation algorithm, default to 1e-10.
        method : function, optional
            Method used for propagation
        **kwargs
            parameters used in perturbation models

        """
        if hasattr(value, "unit") and value.unit in ('rad', 'deg'):
            p, ecc, inc, raan, argp, _ = rv2coe(self.attractor.k.to(u.km ** 3 / u.s ** 2).value,
                                                self.r.to(u.km).value,
                                                self.v.to(u.km / u.s).value)

            # Compute time of flight for correct epoch
            M = nu_to_M(self.nu, self.ecc)
            new_M = nu_to_M(value, self.ecc)
            time_of_flight = Angle(new_M - M).wrap_at(360 * u.deg) / self.n

            return self.from_classical(self.attractor, p / (1.0 - ecc ** 2) * u.km,
                                       ecc * u.one, inc * u.rad, raan * u.rad,
                                       argp * u.rad, value,
                                       epoch=self.epoch + time_of_flight, plane=self._plane)
        else:
            if isinstance(value, time.Time) and not isinstance(value, time.TimeDelta):
                time_of_flight = value - self.epoch
            else:
                time_of_flight = time.TimeDelta(value)

            return propagate(self, time_of_flight, method=method, rtol=rtol, **kwargs)
Ejemplo n.º 42
0
def danby(k, r0, v0, tof, numiter=20, rtol=1e-8):
    """Kepler solver for both elliptic and parabolic orbits based on Danby's
    algorithm.

    Parameters
    ----------
    k : float
        Standard gravitational parameter of the attractor.
    r0 : ~np.array
        Position vector.
    v0 : ~np.array
        Velocity vector.
    tof : float
        Time of flight.
    numiter: int, optional
        Number of iterations, defaults to 20.
    rtol: float, optional
        Relative error for accuracy of the method, defaults to 1e-8.

    Returns
    -------
    rr : ~np.array
        Final position vector.
    vv : ~np.array
        Final velocity vector.

    Note
    ----
    This algorithm was developed by Danby in his paper *The solution of Kepler
    Equation* with DOI: https://doi.org/10.1007/BF01686811
    """

    # Solve first for eccentricity and mean anomaly
    p, ecc, inc, raan, argp, nu = rv2coe(k, r0, v0)
    nu = danby_coe(k, p, ecc, inc, raan, argp, nu, tof, numiter, rtol)

    return coe2rv(k, p, ecc, inc, raan, argp, nu)
Ejemplo n.º 43
0
    def propagate_to_anomaly(self, value):
        """Propagates an orbit to a specific true anomaly.

        Parameters
        ----------
        value : ~astropy.units.Quantity

        Returns
        -------
        Orbit
            Resulting orbit after propagation.

        """
        # TODO: Avoid repeating logic with mean_motion?
        p, ecc, inc, raan, argp, _ = rv2coe(
            self.attractor.k.to(u.km ** 3 / u.s ** 2).value,
            self.r.to(u.km).value,
            self.v.to(u.km / u.s).value,
        )

        # Compute time of flight for correct epoch
        M = nu_to_M(self.nu, self.ecc)
        new_M = nu_to_M(value, self.ecc)
        time_of_flight = Angle(new_M - M).wrap_at(360 * u.deg) / self.n

        return self.from_classical(
            self.attractor,
            p / (1.0 - ecc ** 2) * u.km,
            ecc * u.one,
            inc * u.rad,
            raan * u.rad,
            argp * u.rad,
            value,
            epoch=self.epoch + time_of_flight,
            plane=self.plane,
        )
Ejemplo n.º 44
0
def test_convert_between_coe_and_rv_is_transitive(classical):
    k = Earth.k.to(u.km ** 3 / u.s ** 2).value  # u.km**3 / u.s**2
    res = rv2coe(k, *coe2rv(k, *classical))
    assert_allclose(res, classical)
Ejemplo n.º 45
0
def test_convert_coe_and_rv_hyperbolic(hyperbolic):
    k, expected_res = hyperbolic
    res = rv2coe(k, *coe2rv(k, *expected_res))
    assert_allclose(res, expected_res, atol=1e-8)
Ejemplo n.º 46
0
def test_convert_coe_and_rv_circular_equatorial(circular_equatorial):
    k, expected_res = circular_equatorial
    res = rv2coe(k, *coe2rv(k, *expected_res))
    assert_allclose(res, expected_res, atol=1e-8)