def test_true_to_eccentric(): # Data from NASA-TR-R-158 data = [ # ecc,E (deg), nu(deg) (0.0, 0.0, 0.0), (0.05, 10.52321, 11.05994), (0.10, 54.67466, 59.49810), (0.35, 142.27123, 153.32411), (0.61, 161.87359, 171.02189) ] for row in data: ecc, expected_E, nu = row E = angles.nu_to_E(np.deg2rad(nu), ecc) assert_almost_equal(np.rad2deg(E), expected_E, decimal=4)
def test_true_to_eccentric(): # Data from NASA-TR-R-158 data = [ # ecc,E (deg), nu(deg) (0.0, 0.0, 0.0), (0.05, 10.52321, 11.05994), (0.10, 54.67466, 59.49810), (0.35, 142.27123, 153.32411), (0.61, 161.87359, 171.02189) ] for row in data: ecc, expected_E, nu = row ecc = ecc * u.one expected_E = expected_E * u.deg nu = nu * u.deg E = angles.nu_to_E(nu, ecc) assert_quantity_allclose(E, expected_E, rtol=1e-6)
def test_true_to_eccentric(): # Data from NASA-TR-R-158 data = [ # ecc,E (deg), nu(deg) (0.0, 0.0, 0.0), (0.05, 10.52321, 11.05994), (0.10, 54.67466, 59.49810), (0.35, 142.27123, 153.32411), (0.61, 161.87359, 171.02189) ] for row in data: ecc, expected_E, nu = row ecc = ecc * u.one expected_E = expected_E * u.deg nu = nu * u.deg E = angles.nu_to_E(nu, ecc) assert_almost_equal(E.to(u.rad).value, expected_E.to(u.rad).value, decimal=4)
def test_eccentric_to_true_range2pi(E, ecc): nu = angles.E_to_nu(E, ecc) print(E.value, ecc.value, nu.value) E1 = angles.nu_to_E(nu, ecc) assert_quantity_allclose(E1, E, rtol=1e-8)
def test_eccentric_to_true_range(E, ecc): nu = angles.E_to_nu(E, ecc) E1 = angles.nu_to_E(nu, ecc) assert_quantity_allclose(E1, E, rtol=1e-8)
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
if __name__ == "__main__": # Display some initial data print(f" Orbit: {iss}") print(" State vector [poliastro]") print(f" r = {iss.r}") print(f" v = {iss.v}") print() # Reference epoch epoch = Time(iss.epoch, format="datetime", scale="utc") # Store poliastro orbital elements (osculating) ecc_anomaly = angles.nu_to_E(iss.nu, iss.ecc) mean_anomaly = angles.E_to_M(ecc_anomaly, iss.ecc) # Compute orbital elements required by spg4 (mean) inc, raan, ecc, argp, m_ano, m_mot = rv2el(iss.r, iss.v, iss.epoch) # Display differences print(" Poliastro(osc) rv2el(mean)") print(f"Ecc : {iss.ecc:10.5f}{'':15}{ecc:10.5f}") print( f"Incl [deg] : {math.degrees(iss.inc.value):10.5f}{'':15}{math.degrees(inc):10.5f}" ) print( f"n [deg/min] : {math.degrees(iss.n.to(u.rad/u.minute).value):10.5f}{'':15}{math.degrees(m_mot):10.5f}" ) print(
def test_eccentric_to_true_range(E, ecc): nu = E_to_nu(E, ecc) E_result = nu_to_E(nu, ecc) assert_quantity_allclose(E_result, E, rtol=1e-8)