Exemple #1
0
def test_true_to_mean():
    for row in ELLIPTIC_ANGLES_DATA:
        ecc, expected_M, nu = row
        ecc = ecc * u.one
        expected_M = expected_M * u.deg
        nu = nu * u.deg

        M = E_to_M(nu_to_E(nu, ecc), ecc)

        assert_quantity_allclose(M, expected_M, rtol=1e-4)
Exemple #2
0
def test_expected_mean_anomaly():
    # Example from Curtis
    expected_mean_anomaly = 77.93 * u.deg

    attractor = Earth

    _a = 1.0 * u.deg  # Unused angle
    a = 15_300 * u.km
    ecc = 0.37255 * u.one
    nu = 120 * u.deg

    orbit = Orbit.from_classical(attractor, a, ecc, _a, _a, _a, nu)
    orbit_M = E_to_M(nu_to_E(orbit.nu, orbit.ecc), orbit.ecc)

    assert_quantity_allclose(orbit_M, expected_mean_anomaly, rtol=1e-2)
Exemple #3
0
    def propagate(self, duration=43200, step=60):
        """
        Core method for relative trajectory generation. Inputs duration and
        time step (integers), and updates the six state arrays of the object
        (XYZ position and XYZ velocity) for N samples.

        Input:
        - duration - integer number of seconds (optional, default = 12 hours)
        - step - integer time step size (optional, default = 1 minute)
        
        Returns two numpy arrays
        - Nx3 matrix of all position vectors
        - Nx3 matrix of all velocity vector
        """

        # Check if the orbit is an ellipse (closed)
        if self.satC.ecc < 1 and self.satD.ecc < 1:

            # Get the initial mean anomaly of the chief.
            mC = E_to_M(nu_to_E(self.satC.nu, self.satC.ecc), self.satC.ecc)

            # Get the mean anomaly of the deputy.
            mD = E_to_M(nu_to_E(self.satD.nu, self.satD.ecc), self.satD.ecc)

            # Get the mean motion of the chief.
            nC = self.satC.n

            # Get the mean motion of the deputy.
            nD = self.satD.n

            # Initialise relative position and velocity component arrays.
            relPosArrayX, relPosArrayY, relPosArrayZ = [], [], []
            relVelArrayX, relVelArrayY, relVelArrayZ = [], [], []

            # Get the gravitational constant in u.km**3 / u.s**2
            mu = self.satC.attractor.k.to(u.km**3 / u.s**2)

            # Initialise pi in terms of astropy units
            pi = np.pi * 1 * u.rad

            # Get a time step in Astropy units (seconds)
            ts = step * u.s

            # For each sample...
            for t in range(0, duration, step):

                # Update the mean anomaly of the chief (loop over pi).
                mC = ((mC + pi + (nC * ts)) % (2 * pi)) - pi

                # Update the mean anomaly of the deputy (loop over pi).
                mD = ((mD + pi + (nD * ts)) % (2 * pi)) - pi

                # Compute the chief position, velocity and true anomaly.
                pC, vC, nuC = self._solve_posn(self.satC.a, self.satC.ecc,
                                               self.satC.inc, self.satC.argp,
                                               self.satC.raan, mC, mu)

                # Compute the deputy position, velocity and true anomaly.
                pD, vD, nuD = self._solve_posn(self.satD.a, self.satD.ecc,
                                               self.satD.inc, self.satD.argp,
                                               self.satD.raan, mD, mu)

                # Get the argument of latitude of the chief.
                uC = nuC + self.satC.argp
                uC = (uC + pi) % (2 * pi) - pi  # Loop over pi

                # Get the argument of latitude of the deputy.
                uD = nuD + self.satD.argp
                uD = (uD + pi) % (2 * pi) - pi  # Loop over pi

                # Get the relative argument of latitude.
                du = uD - uC
                du = (du + pi) % (2 * pi) - pi  # Loop over pi

                # Save the chief initial argument of latitude.
                if t == 0:
                    uC0 = uC

                # Compute the deputy elapsed argument of latitude.
                uD_elapsed = uD - uC0
                uD_elapsed = (uD_elapsed + pi) % (2 * pi) - pi
                uD_elapsed = uD_elapsed.to_value(u.rad)

                # Compute the velocity magnitude
                vCMag = np.sqrt(vC[0]**2 + vC[1]**2 + vC[2]**2)

                # Initialize the time-dependent input vector
                uVect = np.array([1.0, uD_elapsed, np.cos(uC), np.sin(uC)])

                # Update Row 1 Column 0 of the state transition matrix.
                self.M[1][0] = du.to_value(u.rad) + self.dR

                # We may now compute the normalized relative state vectors
                relPos = np.matmul(self.M[:3], uVect)
                relVel = np.matmul(self.M[3:], uVect)

                # Un-normalize the relative position vectors
                relPosArrayX.append((relPos[0] * self.satC.a).to_value(u.km))
                relPosArrayY.append((relPos[1] * self.satC.a).to_value(u.km))
                relPosArrayZ.append((relPos[2] * self.satC.a).to_value(u.km))

                # Un-normalize the relative velocity vectors
                relVelArrayX.append((relVel[0] * vCMag).to_value(u.km / u.s))
                relVelArrayY.append((relVel[1] * vCMag).to_value(u.km / u.s))
                relVelArrayZ.append((relVel[2] * vCMag).to_value(u.km / u.s))

            # Save the entire relativeEphem matrix.
            self.relPosArrayX = np.array(relPosArrayX) * (1 * u.km)
            self.relPosArrayY = np.array(relPosArrayY) * (1 * u.km)
            self.relPosArrayZ = np.array(relPosArrayZ) * (-1 * u.km)
            self.relVelArrayX = np.array(relVelArrayX) * (1 * u.km / u.s)
            self.relVelArrayY = np.array(relVelArrayY) * (1 * u.km / u.s)
            self.relVelArrayZ = np.array(relVelArrayZ) * (-1 * u.km / u.s)

            # To allow for chaining...
            return self

        # Or if the orbit is a para/hyperbola...
        else:
            print('Error! Formations for non-closed orbits not supported!')
            return self