示例#1
0
def _targetting(departure_body, target_body, t_launch, t_arrival):
    """This function returns the increment in departure and arrival velocities.

    """

    rr_dpt_body, vv_dpt_body = _get_state(departure_body, t_launch)
    rr_arr_body, vv_arr_body = _get_state(target_body, t_arrival)

    # Compute time of flight
    tof = t_arrival - t_launch

    if tof <= 0:
        return None, None, None, None, None

    try:
        (v_dpt, v_arr), = lambert(Sun.k, rr_dpt_body.xyz, rr_arr_body.xyz, tof)

        # Compute all the output variables
        dv_dpt = norm(v_dpt - vv_dpt_body.xyz)
        dv_arr = norm(v_arr - vv_arr_body.xyz)
        c3_launch = dv_dpt ** 2
        c3_arrival = dv_arr ** 2

        return (
            dv_dpt.to(u.km / u.s).value,
            dv_arr.to(u.km / u.s).value,
            c3_launch.to(u.km ** 2 / u.s ** 2).value,
            c3_arrival.to(u.km ** 2 / u.s ** 2).value,
            tof.jd,
        )

    except AssertionError:
        return None, None, None, None, None
示例#2
0
def test_propagating_to_certain_nu_is_correct():
    # take an elliptic orbit
    a = 1.0 * u.AU
    ecc = 1.0 / 3.0 * u.one
    _a = 0.0 * u.rad
    nu = 10 * u.deg
    elliptic = Orbit.from_classical(Sun, a, ecc, _a, _a, _a, nu)

    elliptic_at_perihelion = elliptic.propagate_to_anomaly(0.0 * u.rad)
    r_per, _ = elliptic_at_perihelion.rv()

    elliptic_at_aphelion = elliptic.propagate_to_anomaly(np.pi * u.rad)
    r_ap, _ = elliptic_at_aphelion.rv()

    assert_quantity_allclose(norm(r_per), a * (1.0 - ecc))
    assert_quantity_allclose(norm(r_ap), a * (1.0 + ecc))

    # TODO: Test specific values
    assert elliptic_at_perihelion.epoch > elliptic.epoch
    assert elliptic_at_aphelion.epoch > elliptic.epoch

    # test 10 random true anomaly values
    for _ in range(10):
        nu = np.random.uniform(low=0.0, high=2 * np.pi)
        elliptic = elliptic.propagate_to_anomaly(nu * u.rad)
        r, _ = elliptic.rv()
        assert_quantity_allclose(norm(r), a * (1.0 - ecc ** 2) / (1 + ecc * np.cos(nu)))
示例#3
0
def test_cowell_propagation_circle_to_circle():
    # From [Edelbaum, 1961]
    accel = 1e-7

    def constant_accel(t0, u, k):
        v = u[3:]
        norm_v = (v[0]**2 + v[1]**2 + v[2]**2)**.5
        return accel * v / norm_v

    ss = Orbit.circular(Earth, 500 * u.km)
    tof = 20 * ss.period

    r, v = cowell(ss,
                  tof.to(u.s).value,
                  ad=constant_accel)

    ss_final = Orbit.from_vectors(
        Earth, r * u.km, v * u.km / u.s)

    da_a0 = (ss_final.a - ss.a) / ss.a
    dv_v0 = abs(norm(ss_final.v) - norm(ss.v)) / norm(ss.v)
    assert_quantity_allclose(da_a0, 2 * dv_v0, rtol=1e-2)

    dv = abs(norm(ss_final.v) - norm(ss.v))
    accel_dt = accel * u.km / u.s**2 * tof
    assert_quantity_allclose(dv, accel_dt, rtol=1e-2)
示例#4
0
def test_cowell_propagation_circle_to_circle():
    # From [Edelbaum, 1961]
    accel = 1e-7

    def constant_accel(t0, u, k):
        v = u[3:]
        norm_v = (v[0]**2 + v[1]**2 + v[2]**2)**.5
        return accel * v / norm_v

    ss = Orbit.circular(Earth, 500 * u.km)
    tof = 20 * ss.period

    r0, v0 = ss.rv()
    k = ss.attractor.k

    r, v = cowell(k.to(u.km**3 / u.s**2).value,
                  r0.to(u.km).value,
                  v0.to(u.km / u.s).value,
                  tof.to(u.s).value,
                  ad=constant_accel)

    ss_final = Orbit.from_vectors(Earth,
                       r * u.km,
                       v * u.km / u.s)

    da_a0 = (ss_final.a - ss.a) / ss.a
    dv_v0 = abs(norm(ss_final.v) - norm(ss.v)) / norm(ss.v)
    assert_almost_equal(da_a0.value, 2 * dv_v0.value, decimal=4)

    dv = abs(norm(ss_final.v) - norm(ss.v))
    accel_dt = accel * u.km / u.s**2 * tof
    assert_almost_equal(dv.value, accel_dt.value, decimal=4)
示例#5
0
def test_propagation_hyperbolic():
    # Data from Curtis, example 3.5
    r0 = [Earth.R.to(u.km).value + 300, 0, 0] * u.km
    v0 = [0, 15, 0] * u.km / u.s
    ss0 = Orbit.from_vectors(Earth, r0, v0)
    tof = 14941 * u.s
    ss1 = ss0.propagate(tof)
    r, v = ss1.rv()
    assert_almost_equal(norm(r).to(u.km).value, 163180, decimal=-1)
    assert_almost_equal(norm(v).to(u.km/u.s).value, 10.51, decimal=2)
示例#6
0
 def pqw(self):
     """Perifocal frame (PQW) vectors. """
     if self.ecc < 1e-8:
         if abs(self.inc.to(u.rad).value) > 1e-8:
             node = np.cross([0, 0, 1], self.h_vec) / norm(self.h_vec)
             p_vec = node / norm(node)  # Circular inclined
         else:
             p_vec = [1, 0, 0] * u.one  # Circular equatorial
     else:
         p_vec = self.e_vec / self.ecc
     w_vec = self.h_vec / norm(self.h_vec)
     q_vec = np.cross(w_vec, p_vec) * u.one
     return p_vec, q_vec, w_vec
示例#7
0
def test_propagation_hyperbolic():
    # Data from Curtis, example 3.5
    r0 = [Earth.R.to(u.km).value + 300, 0, 0] * u.km
    v0 = [0, 15, 0] * u.km / u.s
    expected_r_norm = 163180 * u.km
    expected_v_norm = 10.51 * u.km / u.s

    ss0 = Orbit.from_vectors(Earth, r0, v0)
    tof = 14941 * u.s
    ss1 = ss0.propagate(tof)
    r, v = ss1.rv()

    assert_quantity_allclose(norm(r), expected_r_norm, rtol=1e-4)
    assert_quantity_allclose(norm(v), expected_v_norm, rtol=1e-3)
示例#8
0
def guidance_law(ss_0, ecc_f, inc_f, f):
    """Guidance law from the model.

    Thrust is aligned with an inertially fixed direction perpendicular to the
    semimajor axis of the orbit.

    Parameters
    ----------
    ss_0 : Orbit
        Initial orbit, containing all the information.
    ecc_f : float
        Final eccentricity.
    inc_f : float
        Final inclination.
    f : float
        Magnitude of constant acceleration.

    """
    # We fix the inertial direction at the beginning
    ecc_0 = ss_0.ecc.value
    if ecc_0 > 0.001:  # Arbitrary tolerance
        ref_vec = ss_0.e_vec / ecc_0
    else:
        ref_vec = ss_0.r / norm(ss_0.r)

    h_unit = ss_0.h_vec / norm(ss_0.h_vec)
    thrust_unit = np.cross(h_unit, ref_vec) * np.sign(ecc_f - ecc_0)

    inc_0 = ss_0.inc.to(u.rad).value
    argp = ss_0.argp.to(u.rad).value

    beta_0_ = beta(ecc_0, ecc_f, inc_0, inc_f, argp)

    @state_from_vector
    def a_d(t0, ss):
        r = ss.r.value
        v = ss.v.value
        nu = ss.nu.value

        beta_ = beta_0_ * np.sign(np.cos(nu))  # The sign of ß reverses at minor axis crossings

        w_ = np.cross(r, v) / norm(np.cross(r, v))
        accel_v = f * (
            np.cos(beta_) * thrust_unit +
            np.sin(beta_) * w_
        )
        return accel_v

    return a_d
示例#9
0
    def a_d(t0, ss):
        r = ss.r.value
        v = ss.v.value

        # Change sign of beta with the out-of-plane velocity
        beta_ = beta(t0, V_0=V_0, f=f, beta_0=beta_0_) * np.sign(r[0] * (inc_f - inc_0))

        t_ = v / norm(v)
        w_ = np.cross(r, v) / norm(np.cross(r, v))
        # n_ = np.cross(t_, w_)
        accel_v = f * (
            np.cos(beta_) * t_ +
            np.sin(beta_) * w_
        )
        return accel_v
示例#10
0
    def a_d(t0, ss):
        r = ss.r.value
        v = ss.v.value
        nu = ss.nu.value

        alpha_ = nu - np.pi / 2

        r_ = r / norm(r)
        w_ = np.cross(r, v) / norm(np.cross(r, v))
        s_ = np.cross(w_, r_)
        accel_v = f * (
            np.cos(alpha_) * s_ +
            np.sin(alpha_) * r_
        )
        return accel_v
示例#11
0
def _lambert(k, r1, r2, tof, M, numiter, rtol):
    # Check preconditions
    assert tof > 0
    assert k > 0

    # Chord
    c = r2 - r1
    c_norm, r1_norm, r2_norm = norm(c), norm(r1), norm(r2)

    # Semiperimeter
    s = (r1_norm + r2_norm + c_norm) * .5

    # Versors
    i_r1, i_r2 = r1 / r1_norm, r2 / r2_norm
    i_h = np.cross(i_r1, i_r2)
    i_h = i_h / norm(i_h)  # Fixed from paper

    # Geometry of the problem
    ll = np.sqrt(1 - c_norm / s)

    if i_h[2] < 0:
        ll = -ll
        i_h = -i_h

    i_t1, i_t2 = np.cross(i_h, i_r1), np.cross(i_h, i_r2)  # Fixed from paper

    # Non dimensional time of flight
    T = np.sqrt(2 * k / s ** 3) * tof

    # Find solutions
    xy = _find_xy(ll, T, M, numiter, rtol)

    # Reconstruct
    gamma = np.sqrt(k * s / 2)
    rho = (r1_norm - r2_norm) / c_norm
    sigma = np.sqrt(1 - rho ** 2)

    for x, y in xy:
        V_r1, V_r2, V_t1, V_t2 = _reconstruct(x, y, r1_norm, r2_norm, ll,
                                              gamma, rho, sigma)
        v1 = V_r1 * i_r1 + V_t1 * i_t1
        v2 = V_r2 * i_r2 + V_t2 * i_t2
        yield v1, v2
示例#12
0
    def a_d(t0, ss):
        r = ss.r.value
        v = ss.v.value
        nu = ss.nu.value

        beta_ = beta_0_ * np.sign(np.cos(nu))  # The sign of ß reverses at minor axis crossings

        w_ = np.cross(r, v) / norm(np.cross(r, v))
        accel_v = f * (
            np.cos(beta_) * thrust_unit +
            np.sin(beta_) * w_
        )
        return accel_v
示例#13
0
文件: rv.py 项目: muhtar05/poliastro
def rv2coe(k, r, v, tol=1e-8):
    """Converts from vectors to classical orbital elements.

    Parameters
    ----------
    k : float
        Standard gravitational parameter (km^3 / s^2).
    r : array
        Position vector (km).
    v : array
        Velocity vector (km / s).
    tol : float, optional
        Tolerance for eccentricity and inclination checks, default to 1e-8.

    """
    h = np.cross(r, v)
    n = np.cross([0, 0, 1], h) / norm(h)
    e = ((v.dot(v) - k / (norm(r))) * r - r.dot(v) * v) / k
    ecc = norm(e)
    p = h.dot(h) / k
    inc = np.arccos(h[2] / norm(h))

    circular = ecc < tol
    equatorial = abs(inc) < tol

    if equatorial and not circular:
        raan = 0
        argp = np.arctan2(e[1], e[0]) % (2 * np.pi)  # Longitude of periapsis
        nu = (np.arctan2(h.dot(np.cross(e, r)) / norm(h), r.dot(e)) %
              (2 * np.pi))
    elif not equatorial and circular:
        raan = np.arctan2(n[1], n[0]) % (2 * np.pi)
        argp = 0
        # Argument of latitude
        nu = (np.arctan2(r.dot(np.cross(h, n)) / norm(h), r.dot(n)) %
              (2 * np.pi))
    elif equatorial and circular:
        raan = 0
        argp = 0
        nu = np.arctan2(r[1], r[0]) % (2 * np.pi)  # True longitude
    else:
        raan = np.arctan2(n[1], n[0]) % (2 * np.pi)
        argp = (np.arctan2(e.dot(np.cross(h, n)) / norm(h), e.dot(n)) %
                (2 * np.pi))
        nu = (np.arctan2(r.dot(np.cross(h, e)) / norm(h), r.dot(e))
              % (2 * np.pi))

    return p, ecc, inc, raan, argp, nu
示例#14
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)
示例#15
0
    def set_frame(self, p_vec, q_vec, w_vec):
        """Sets perifocal frame.

        Raises
        ------
        ValueError
            If the vectors are not a set of mutually orthogonal unit vectors.
        """
        if not np.allclose([norm(v) for v in (p_vec, q_vec, w_vec)], 1):
            raise ValueError("Vectors must be unit.")
        elif not np.allclose([p_vec.dot(q_vec), q_vec.dot(w_vec), w_vec.dot(p_vec)], 0):
            raise ValueError("Vectors must be mutually orthogonal.")
        else:
            self._frame = p_vec, q_vec, w_vec

        if self._trajectories:
            self._redraw()
示例#16
0
    def set_frame(self, p_vec, q_vec, w_vec):
        """Sets perifocal frame.

        Raises
        ------
        ValueError
            If the vectors are not a set of mutually orthogonal unit vectors.

        """
        if self._frame and self.trajectories:
            raise NotImplementedError(
                "OrbitPlotter2D does not support reprojecting yet"
            )

        if not np.allclose([norm(v) for v in (p_vec, q_vec, w_vec)], 1):
            raise ValueError("Vectors must be unit.")
        elif not np.allclose([p_vec.dot(q_vec), q_vec.dot(w_vec), w_vec.dot(p_vec)], 0):
            raise ValueError("Vectors must be mutually orthogonal.")
        else:
            self._frame = p_vec, q_vec, w_vec
示例#17
0
    def set_frame(self, p_vec, q_vec, w_vec):
        """Sets perifocal frame if not existing.

        Raises
        ------
        ValueError
            If the vectors are not a set of mutually orthogonal unit vectors.
        NotImplementedError
            If the frame was already set up.

        """
        if not self._frame:
            if not np.allclose([norm(v) for v in (p_vec, q_vec, w_vec)], 1):
                raise ValueError("Vectors must be unit.")
            if not np.allclose([p_vec.dot(q_vec),
                                q_vec.dot(w_vec),
                                w_vec.dot(p_vec)], 0):
                raise ValueError("Vectors must be mutually orthogonal.")
            else:
                self._frame = p_vec, q_vec, w_vec
        else:
            raise NotImplementedError
示例#18
0
    def plot(self, orbit, *, label=None, color=None):
        """Plots state and osculating orbit in their plane.

        Parameters
        ----------
        orbit : ~poliastro.twobody.orbit.Orbit
            Orbit to plot.
        label : string, optional
            Label of the orbit.
        color : string, optional
            Color of the line and the position.

        """
        if color is None:
            color = next(self._color_cycle)

        self._set_attractor(orbit.attractor)

        label = generate_label(orbit, label)
        trajectory = orbit.sample()

        trace = self._plot_trajectory(trajectory, label, color, True)

        self._trajectories.append(
            Trajectory(trajectory, orbit.r, label, trace.line.color)
        )

        # Redraw the attractor now to compute the attractor radius
        self._redraw_attractor()

        # Plot required 2D/3D shape in the position of the body
        radius = min(
            self._attractor_radius * 0.5, (norm(orbit.r) - orbit.attractor.R) * 0.5
        )  # Arbitrary thresholds
        self._plot_point(radius, color, label, center=orbit.r)

        if not self._figure._in_batch_mode:
            return self.show()
示例#19
0
    def plot(self, orbit, *, label=None, color=None):
        """Plots state and osculating orbit in their plane.

        Parameters
        ----------
        orbit : ~poliastro.twobody.orbit.Orbit
        label : string, optional
        color : string, optional
        """
        if color is None:
            color = next(self._color_cycle)

        self.set_attractor(orbit.attractor)
        self._redraw_attractor(orbit.r_p * 0.15)  # Arbitrary threshold

        label = _generate_label(orbit, label)
        trajectory = orbit.sample()

        self._plot_trajectory(trajectory, label, color, True)
        # Plot required 2D/3D shape in the position of the body
        radius = min(self._attractor_radius * 0.5, (norm(orbit.r) - orbit.attractor.R) * 0.3)  # Arbitrary thresholds
        shape = self._plot_sphere(radius, color, label, center=orbit.r)
        self._data.append(shape)
示例#20
0
 def e_vec(self):
     """Eccentricity vector. """
     r, v = self.rv()
     k = self.attractor.k
     e_vec = ((v.dot(v) - k / (norm(r))) * r - r.dot(v) * v) / k
     return e_vec.decompose()
示例#21
0
 def e_vec(self):
     """Eccentricity vector. """
     r, v = self.rv()
     k = self.attractor.k
     e_vec = ((v.dot(v) - k / (norm(r))) * r - r.dot(v) * v) / k
     return e_vec
示例#22
0
    def hohmann(cls, orbit_i, r_f):
        r"""Compute a Hohmann transfer between two circular orbits.

        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
        ----------
        orbit_i: poliastro.twobody.orbit.Orbit
            Initial orbit
        r_f: astropy.unit.Quantity
            Final altitude of the orbit
        """

        if orbit_i.nu is not 0 * u.deg:
            orbit_i = orbit_i.propagate_to_anomaly(0 * u.deg)

        # Initial orbit data
        k = orbit_i.attractor.k
        r_i = orbit_i.r
        v_i = orbit_i.v
        h_i = norm(cross(r_i.to(u.m).value, v_i.to(u.m / u.s).value) * u.m ** 2 / u.s)
        p_i = h_i ** 2 / k.to(u.m ** 3 / u.s ** 2)

        # Hohmann is defined always from the PQW frame, since it is the
        # natural plane of the orbit
        r_i, v_i = rv_pqw(
            k.to(u.m ** 3 / u.s ** 2).value,
            p_i.to(u.m).value,
            orbit_i.ecc.value,
            orbit_i.nu.to(u.rad).value,
        )

        # Now, we apply Hohmman maneuver
        r_i = norm(r_i * u.m)
        v_i = norm(v_i * u.m / u.s)
        a_trans = (r_i + r_f) / 2

        # This is the modulus of the velocities
        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)

        # Write them in PQW frame
        dv_a = np.array([0, dv_a.to(u.m / u.s).value, 0])
        dv_b = np.array([0, -dv_b.to(u.m / u.s).value, 0])

        # Transform to IJK frame
        rot_matrix = coe_rotation_matrix(
            orbit_i.inc.to(u.rad).value,
            orbit_i.raan.to(u.rad).value,
            orbit_i.argp.to(u.rad).value,
        )

        dv_a = (rot_matrix @ dv_a) * u.m / u.s
        dv_b = (rot_matrix @ dv_b) * u.m / u.s

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

        return cls((0 * u.s, dv_a), (t_trans, dv_b))
示例#23
0
    def get_total_cost(self):
        """Returns total cost of the maneuver.

        """
        dvs = [norm(dv) for dv in self._dvs]
        return sum(dvs, 0 * u.km / u.s)
示例#24
0
    def bielliptic(cls, orbit_i, r_b, r_f):
        r"""Compute a bielliptic transfer between two circular orbits.

        The bielliptic maneuver employs two Hohmann transfers, therefore two
        intermediate orbits are established. We define the different radius
        relationships as follows:

        .. math::
            \begin{align}
                a_{trans1} &= \frac{r_{i} + r_{b}}{2}\\
                a_{trans2} &= \frac{r_{b} + r_{f}}{2}\\
            \end{align}

        The increments in the velocity are:

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

        The time of flight for this maneuver is the addition of the time needed for both transition orbits, following the same formula as
        Hohmann:

        .. math::
            \begin{align}
                \tau_{trans1} &= \pi \sqrt{\frac{a_{trans1}^{3}}{\mu}}\\
                \tau_{trans2} &= \pi \sqrt{\frac{a_{trans2}^{3}}{\mu}}\\
            \end{align}

        Parameters
        ----------
        orbit_i: poliastro.twobody.orbit.Orbit
            Initial orbit
        r_b: astropy.unit.Quantity
            Altitude of the intermediate orbit
        r_f: astropy.unit.Quantity
            Final altitude of the orbit
        """
        if orbit_i.nu is not 0 * u.deg:
            orbit_i = orbit_i.propagate_to_anomaly(0 * u.deg)

        # Initial orbit data
        k = orbit_i.attractor.k
        r_i = orbit_i.r
        v_i = orbit_i.v
        h_i = norm(cross(r_i.to(u.m).value, v_i.to(u.m / u.s).value) * u.m ** 2 / u.s)
        p_i = h_i ** 2 / k.to(u.m ** 3 / u.s ** 2)

        # Bielliptic is defined always from the PQW frame, since it is the
        # natural plane of the orbit
        r_i, v_i = rv_pqw(
            k.to(u.m ** 3 / u.s ** 2).value,
            p_i.to(u.m).value,
            orbit_i.ecc.value,
            orbit_i.nu.to(u.rad).value,
        )

        # Define the transfer radius
        r_i = norm(r_i * u.m)
        v_i = norm(v_i * u.m / u.s)
        a_trans1 = (r_i + r_b) / 2
        a_trans2 = (r_b + r_f) / 2

        # Compute impulses
        dv_a = np.sqrt(2 * k / r_i - k / a_trans1) - v_i
        dv_b = np.sqrt(2 * k / r_b - k / a_trans2) - np.sqrt(2 * k / r_b - k / a_trans1)
        dv_c = np.sqrt(k / r_f) - np.sqrt(2 * k / r_f - k / a_trans2)

        # Write impulses in PQW frame
        dv_a = np.array([0, dv_a.to(u.m / u.s).value, 0])
        dv_b = np.array([0, -dv_b.to(u.m / u.s).value, 0])
        dv_c = np.array([0, dv_c.to(u.m / u.s).value, 0])

        rot_matrix = coe_rotation_matrix(
            orbit_i.inc.to(u.rad).value,
            orbit_i.raan.to(u.rad).value,
            orbit_i.argp.to(u.rad).value,
        )

        # Transform to IJK frame
        dv_a = (rot_matrix @ dv_a) * u.m / u.s
        dv_b = (rot_matrix @ dv_b) * u.m / u.s
        dv_c = (rot_matrix @ dv_c) * u.m / u.s

        # Compute time for maneuver
        t_trans1 = np.pi * np.sqrt(a_trans1 ** 3 / k)
        t_trans2 = np.pi * np.sqrt(a_trans2 ** 3 / k)

        return cls((0 * u.s, dv_a), (t_trans1, dv_b), (t_trans2, dv_c))
示例#25
0
def lagrange_points_vec(m1, r1, m2, r2, n):
    """Computes the five Lagrange points in the CR3BP.

    Returns the positions in the same frame of reference as `r1` and `r2`
    for the five Lagrangian points.

    Parameters
    ----------
    m1 : ~astropy.units.Quantity
        Mass of the main body. This body is the one with the biggest mass.
    r1 : ~astropy.units.Quantity
        Position of the main body.
    m2 : ~astropy.units.Quantity
        Mass of the secondary body.
    r2 : ~astropy.units.Quantity
        Position of the secondary body.
    n : ~astropy.units.Quantity
        Normal vector to the orbital plane.
    Returns
    -------
    list:
        Position of the Lagrange points: [L1, L2, L3, L4, L5]
        The positions are of type ~astropy.units.Quantity
    """

    # Check Body 1 is the main body
    assert (
        m1 > m2
    ), "Body 1 is not the main body: it has less mass than the 'secondary' body"

    # Define local frame of reference:
    # Center: main body, NOT the barycenter
    # x-axis: points to the secondary body
    ux = r2 - r1
    r12 = norm(ux)
    ux = ux / r12

    # y-axis: contained in the orbital plane, perpendicular to x-axis

    def cross(x, y):
        return np.cross(x, y) * x.unit * y.unit

    uy = cross(n, ux)
    uy = uy / norm(uy)

    # position in x-axis
    x1, x2, x3, x4, x5 = lagrange_points(r12, m1, m2)

    # position in y-axis
    # L1, L2, L3 are located in the x-axis, so y123 = 0

    # L4 and L5 are points in the plane of rotation which form an equilateral
    # triangle with the two masses (Battin)
    # sqrt(3)/2 = sin(60 deg)
    y4 = np.sqrt(3) / 2 * r12
    y5 = -y4

    # Convert L points coordinates (x,y) to original vectorial base [r1 r2]
    L1 = r1 + ux * x1
    L2 = r1 + ux * x2
    L3 = r1 + ux * x3
    L4 = r1 + ux * x4 + uy * y4
    L5 = r1 + ux * x5 + uy * y5

    return [L1, L2, L3, L4, L5]
示例#26
0
 def _plot_position(self, position, label, colors):
     radius = min(
         self._attractor_radius * 0.5, (norm(position) - self._attractor.R) * 0.5
     )  # Arbitrary thresholds
     self._draw_point(radius, colors[0], label, center=position)
示例#27
0
def compute_flyby(v_spacecraft, v_body, k, r_p, theta=0 * u.deg):
    """Computes outbound velocity after a flyby.

    Parameters
    ----------
    v_spacecraft : ~astropy.units.Quantity
        Velocity of the spacecraft, relative to the attractor of the body.
    v_body : ~astropy.units.Quantity
        Velocity of the body, relative to its attractor.
    k : ~astropy.units.Quantity
        Standard gravitational parameter of the body.
    r_p : ~astropy.units.Quantity
        Radius of periapsis, measured from the center of the body.
    theta : ~astropy.units.Quantity, optional
        Aim angle of the B vector, default to 0.

    Returns
    -------
    v_spacecraft_out : ~astropy.units.Quantity
        Outbound velocity of the spacecraft.
    delta : ~astropy.units.Quantity
        Turn angle.

    """
    v_inf_1 = v_spacecraft - v_body  # Hyperbolic excess velocity
    v_inf = norm(v_inf_1)

    ecc = 1 + r_p * v_inf ** 2 / k  # Eccentricity of the entry hyperbola
    delta = 2 * np.arcsin(1 / ecc)  # Turn angle

    b = k / v_inf ** 2 * np.sqrt(ecc ** 2 - 1)  # Magnitude of the B vector

    # Now we compute the unit vectors in which to return the outbound hyperbolic excess velocity:
    # * S goes along the hyperbolic excess velocity and is perpendicular to the B-Plane,
    # * T goes along the B-Plane and is parallel to _some_ fundamental plane - in this case, the plane in which
    #   the velocities are computed
    # * R completes the orthonormal set
    S_vec = v_inf_1 / v_inf
    c_vec = np.array([0, 0, 1]) * u.one
    T_vec = np.cross(S_vec, c_vec) * u.one
    T_vec = T_vec / norm(T_vec)
    R_vec = np.cross(S_vec, T_vec) * u.one

    # This vector defines the B-Plane
    B_vec = b * (np.cos(theta) * T_vec + np.sin(theta) * R_vec)

    # We have to rotate the inbound hyperbolic excess velocity
    # an angle delta (turn angle) around a vector that is orthogonal to
    # the B-Plane and trajectory plane
    rot_v = np.cross(B_vec / b, S_vec) * u.one

    # And now we rotate the outbound hyperbolic excess velocity
    # u_vec = v_inf_1 / norm(v_inf) = S_vec
    v_vec = np.cross(rot_v, v_inf_1) * u.one
    v_vec = v_vec / norm(v_vec)

    v_inf_2 = v_inf * (np.cos(delta) * S_vec + np.sin(delta) * v_vec)

    v_spacecraft_out = v_inf_2 + v_body

    return v_spacecraft_out, delta