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)
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)
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)
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)
def to_vectors(self): """Converts to position and velocity vector representation.""" r, v = coe2rv( self.attractor.k.to_value(u.km ** 3 / u.s ** 2), self.p.to_value(u.km), self.ecc.value, self.inc.to_value(u.rad), self.raan.to_value(u.rad), self.argp.to_value(u.rad), self.nu.to_value(u.rad), ) return RVState(self.attractor, r * u.km, v * u.km / u.s, self.plane)
def init_state_vec(orbits='Default', random_state=np.random.RandomState()): if orbits == 'Default': orbits = [ 'LEO', 'MEO', 'GEO', 'LEO', 'MEO', 'GEO', 'Tundra', 'Molniya' ] random_state.shuffle(orbits) exo_atmospheric = False inc = np.radians(random_state.uniform(0, 180)) # (rad) – Inclination raan = np.radians(random_state.uniform( 0, 360)) # (rad) – Right ascension of the ascending node. argp = np.radians(random_state.uniform( low=0, high=360)) # (rad) – Argument of the pericenter. nu = np.radians(random_state.uniform(0, 360)) # (rad) – True anomaly. if orbits[-1] == 'LEO': while not exo_atmospheric: a = random_state.uniform(RE_eq + 300 * 1000, RE_eq + 2000 * 1000) # (m) – Semi-major axis. ecc = random_state.uniform(0, .25) # (Unit-less) – Eccentricity. b = a * np.sqrt(1 - ecc**2) if b > RE_eq + 300 * 1000: exo_atmospheric = True if orbits[-1] == 'MEO': while not exo_atmospheric: a = random_state.uniform(RE_eq + 2000 * 1000, RE_eq + 35786 * 1000) # (m) – Semi-major axis. ecc = random_state.uniform(0, .25) # (Unit-less) – Eccentricity. b = a * np.sqrt(1 - ecc**2) if b > RE_eq + 300 * 1000: exo_atmospheric = True if orbits[-1] == 'GEO': a = 42164 * 1000 # (m) – Semi-major axis. stationary = random_state.randint(0, 2) ecc = stationary * random_state.uniform( 0, .25) # (Unit-less) – Eccentricity. inc = stationary * random_state.uniform( 0, np.radians(0)) # (Unit-less) – Eccentricity. if orbits[-1] == 'Molniya': a = 26600 * 1000 # (m) – Semi-major axis. inc = np.radians(63.4) # (rad) – Inclination ecc = 0.737 # (Unit-less) – Eccentricity. argp = np.radians(270) # (rad) – Argument of the pericenter. if orbits[-1] == 'Tundra': a = 42164 * 1000 # (m) – Semi-major axis. inc = np.radians(63.4) # (rad) – Inclination ecc = 0.2 # (Unit-less) – Eccentricity. argp = np.radians(270) # (rad) – Argument of the pericenter. p = a * (1 - ecc**2) # (km) - Semi-latus rectum or parameter return np.concatenate(coe2rv(k, p, ecc, inc, raan, argp, nu))
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)
def init_state_vec(seed=None): if not (seed == None): np.random.seed(seed) k = 398600.4418 # (km^3 / s^2) - Standard gravitational parameter a = np.random.uniform((6378.1366 + 200), 42164) # (km) – Semi-major axis. ecc = np.random.uniform(0.001, 0.3) # (Unitless) – Eccentricity. inc = np.radians(np.random.uniform(0, 180)) # (rad) – Inclination raan = np.radians(np.random.uniform( 0, 360)) # (rad) – Right ascension of the ascending node. argp = np.radians(np.random.uniform( 0, 360)) # (rad) – Argument of the pericenter. nu = np.radians(np.random.uniform(0, 360)) # (rad) – True anomaly. p = a * (1 - ecc**2) # (km) - Semi-latus rectum or parameter return np.concatenate(coe2rv(k, p, ecc, inc, raan, argp, nu))
def to_vectors(self): """Converts to position and velocity vector representation. """ r, v = coe2rv( self.attractor.k.to(u.km ** 3 / u.s ** 2).value, self.p.to(u.km).value, self.ecc.value, self.inc.to(u.rad).value, self.raan.to(u.rad).value, self.argp.to(u.rad).value, self.nu.to(u.rad).value, ) return RVState(self.attractor, r * u.km, v * u.km / u.s)
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)
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)
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)
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)
def gooding(k, r0, v0, tof, numiter=150, rtol=1e-8): """Solves the Elliptic Kepler Equation with a cubic convergence and accuracy better than 10e-12 rad is normally achieved. It is not valid for eccentricities equal or higher than 1.0. 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 150. 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 ---- Original paper for the algorithm: https://doi.org/10.1007/BF01238923 """ # Solve first for eccentricity and mean anomaly p, ecc, inc, raan, argp, nu = rv2coe(k, r0, v0) nu = gooding_coe(k, p, ecc, inc, raan, argp, nu, tof, numiter, rtol) return coe2rv(k, p, ecc, inc, raan, argp, nu)
def farnocchia(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 : ~np.array Initial position vector wrt attractor center. v0 : ~np.array 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) nu = farnocchia_coe(k, p, ecc, inc, raan, argp, nu0, tof) return coe2rv(k, p, ecc, inc, raan, argp, nu)
def pimienta(k, r0, v0, tof): """Raw algorithm for Adonis' Pimienta and John L. Crassidis 15th order polynomial Kepler solver. 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 ---- This algorithm was derived from the original paper: Pimienta-Peñalver, A. & Crassidis, John. (2013). Accurate Kepler equation solver without transcendental function evaluations. Advances in the Astronautical Sciences. 147. 233-247. """ # TODO: implement hyperbolic case # Solve first for eccentricity and mean anomaly p, ecc, inc, raan, argp, nu = rv2coe(k, r0, v0) nu = pimienta_coe(k, p, ecc, inc, raan, argp, nu, tof) return coe2rv(k, p, ecc, inc, raan, argp, nu)
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)
def mikkola(k, r0, v0, tof, rtol=None): """ Raw algorithm for Mikkola's Kepler solver. Parameters ---------- k : ~astropy.units.Quantity Standard gravitational parameter of the attractor. r : ~astropy.units.Quantity Position vector. v : ~astropy.units.Quantity Velocity vector. tofs : ~astropy.units.Quantity Array of times to propagate. rtol: float This method does not require of tolerance since it is non iterative. Returns ------- rr : ~astropy.units.Quantity Propagated position vectors. vv : ~astropy.units.Quantity Note ---- Original paper: https://doi.org/10.1007/BF01235850 """ # Solving for the classical elements p, ecc, inc, raan, argp, nu = rv2coe(k, r0, v0) M0 = nu_to_M(nu, ecc, delta=0) a = p / (1 - ecc**2) n = np.sqrt(k / np.abs(a)**3) M = M0 + n * tof # Solve for specific geometrical case if ecc < 1.0: # Equation (9a) alpha = (1 - ecc) / (4 * ecc + 1 / 2) else: alpha = (ecc - 1) / (4 * ecc + 1 / 2) beta = M / 2 / (4 * ecc + 1 / 2) # Equation (9b) if beta >= 0: z = (beta + np.sqrt(beta**2 + alpha**3))**(1 / 3) else: z = (beta - np.sqrt(beta**2 + alpha**3))**(1 / 3) s = z - alpha / z # Apply initial correction if ecc < 1.0: ds = -0.078 * s**5 / (1 + ecc) else: ds = 0.071 * s**5 / (1 + 0.45 * s**2) / (1 + 4 * s**2) / ecc s += ds # Solving for the true anomaly if ecc < 1.0: E = M + ecc * (3 * s - 4 * s**3) f = E - ecc * np.sin(E) - M f1 = 1.0 - ecc * np.cos(E) f2 = ecc * np.sin(E) f3 = ecc * np.cos(E) f4 = -f2 f5 = -f3 else: E = 3 * np.log(s + np.sqrt(1 + s**2)) f = -E + ecc * np.sinh(E) - M f1 = -1.0 + ecc * np.cosh(E) f2 = ecc * np.sinh(E) f3 = ecc * np.cosh(E) f4 = f2 f5 = f3 # Apply Taylor expansion u1 = -f / f1 u2 = -f / (f1 + 0.5 * f2 * u1) u3 = -f / (f1 + 0.5 * f2 * u2 + (1.0 / 6.0) * f3 * u2**2) u4 = -f / (f1 + 0.5 * f2 * u3 + (1.0 / 6.0) * f3 * u3**2 + (1.0 / 24.0) * f4 * (u3**3)) u5 = -f / (f1 + f2 * u4 / 2 + f3 * (u4 * u4) / 6.0 + f4 * (u4 * u4 * u4) / 24.0 + f5 * (u4 * u4 * u4 * u4) / 120.0) E += u5 if ecc < 1.0: nu = E_to_nu(E, ecc) else: if ecc == 1.0: # Parabolic nu = D_to_nu(E) else: # Hyperbolic nu = F_to_nu(E, ecc) return coe2rv(k, p, ecc, inc, raan, argp, nu)
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-precission errors. Parameters ---------- k : float Standar Gravitational parameter r0 : array Initial position vector wrt attractor center. v0 : array Initial velocity vector. tof : float Time of flight. Returns ------- rf: array Final position vector vf: 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) M0 = nu_to_M(nu, ecc, delta=0) a = p / (1 - ecc**2) n = np.sqrt(k / a**3) M = M0 + n * tof # Range between -pi and pi M = M % (2 * np.pi) if M > np.pi: M = -(2 * np.pi - M) # Equation (20) alpha = (3 * np.pi**2 + 1.6 * (np.pi - np.abs(M)) / (1 + ecc)) / (np.pi**2 - 6) # Equation (5) d = 3 * (1 - ecc) + alpha * ecc # Equation (9) q = 2 * alpha * d * (1 - ecc) - M**2 # Equation (10) r = 3 * alpha * d * (d - 1 + ecc) * M + M**3 # Equation (14) w = (np.abs(r) + np.sqrt(q**3 + r**2))**(2 / 3) # Equation (15) E = (2 * r * w / (w**2 + w * q + q**2) + M) / d # Equation (26) f0 = _kepler_equation(E, M, ecc) f1 = _kepler_equation_prime(E, M, ecc) f2 = ecc * np.sin(E) f3 = ecc * np.cos(E) f4 = -f2 # Equation (22) delta3 = -f0 / (f1 - 0.5 * f0 * f2 / f1) delta4 = -f0 / (f1 + 0.5 * delta3 * f2 + 1 / 6 * delta3**2 * f3) delta5 = -f0 / (f1 + 0.5 * delta4 * f2 + 1 / 6 * delta4**2 * f3 + 1 / 24 * delta4**3 * f4) E += delta5 nu = E_to_nu(E, ecc) return coe2rv(k, p, ecc, inc, raan, argp, nu)
def pimienta(k, r0, v0, tof): """ Raw algorithm for Adonis' Pimienta and John L. Crassidis 15th order polynomial Kepler solver. Parameters ---------- k : float Standar Gravitational parameter r0 : array Initial position vector wrt attractor center. v0 : array Initial velocity vector. tof : float Time of flight. Returns ------- rf: array Final position vector vf: array Final velocity vector Note ---- This algorithm was drived from the original paper: http://hdl.handle.net/10477/50522 """ # TODO: implement hyperbolic case # Solve first for eccentricity and mean anomaly p, ecc, inc, raan, argp, nu = rv2coe(k, r0, v0) M0 = nu_to_M(nu, ecc, delta=0) semi_axis_a = p / (1 - ecc**2) n = np.sqrt(k / np.abs(semi_axis_a)**3) M = M0 + n * tof # Equation (32a), (32b), (32c) and (32d) c3 = 5 / 2 + 560 * ecc a = 15 * (1 - ecc) / c3 b = -M / c3 y = np.sqrt(b**2 / 4 + a**3 / 27) # Equation (33) x_bar = (-b / 2 + y)**(1 / 3) - (b / 2 + y)**(1 / 3) # Coefficients from equations (34a) and (34b) c15 = 3003 / 14336 + 16384 * ecc c13 = 3465 / 13312 - 61440 * ecc c11 = 945 / 2816 + 92160 * ecc c9 = 175 / 384 - 70400 * ecc c7 = 75 / 112 + 28800 * ecc c5 = 9 / 8 - 6048 * ecc # Precompute x_bar powers, equations (35a) to (35d) x_bar2 = x_bar**2 x_bar3 = x_bar2 * x_bar x_bar4 = x_bar3 * x_bar x_bar5 = x_bar4 * x_bar x_bar6 = x_bar5 * x_bar x_bar7 = x_bar6 * x_bar x_bar8 = x_bar7 * x_bar x_bar9 = x_bar8 * x_bar x_bar10 = x_bar9 * x_bar x_bar11 = x_bar10 * x_bar x_bar12 = x_bar11 * x_bar x_bar13 = x_bar12 * x_bar x_bar14 = x_bar13 * x_bar x_bar15 = x_bar14 * x_bar # Function f and its derivatives are given by all the (36) equation set f = (c15 * x_bar15 + c13 * x_bar13 + c11 * x_bar11 + c9 * x_bar9 + c7 * x_bar7 + c5 * x_bar5 + c3 * x_bar3 + 15 * (1 - ecc) * x_bar - M) f1 = (15 * c15 * x_bar14 + 13 * c13 * x_bar12 + 11 * c11 * x_bar10 + 9 * c9 * x_bar8 + 7 * c7 * x_bar6 + 5 * c5 * x_bar4 + 3 * c3 * x_bar2 + 15 * (1 - ecc)) f2 = (210 * c15 * x_bar13 + 156 * c13 * x_bar11 + 110 * c11 * x_bar9 + 72 * c9 * x_bar7 + 42 * c7 * x_bar5 + 20 * c5 * x_bar3 + 6 * c3 * x_bar) f3 = (2730 * c15 * x_bar12 + 1716 * c13 * x_bar10 + 990 * c11 * x_bar8 + 504 * c9 * x_bar6 + 210 * c7 * x_bar4 + 60 * c5 * x_bar2 + 6 * c3) f4 = (32760 * c15 * x_bar11 + 17160 * c13 * x_bar9 + 7920 * c11 * x_bar7 + 3024 * c9 * x_bar5 + 840 * c7 * x_bar3 + 120 * c5 * x_bar) f5 = (360360 * c15 * x_bar10 + 154440 * c13 * x_bar8 + 55440 * c11 * x_bar6 + 15120 * c9 * x_bar4 + 2520 * c7 * x_bar2 + 120 * c5) f6 = (3603600 * c15 * x_bar9 + 1235520 * c13 * x_bar7 + 332640 * c11 * x_bar5 + 60480 * c9 * x_bar3 + 5040 * c7 * x_bar) f7 = (32432400 * c15 * x_bar8 + 8648640 * c13 * x_bar6 + 1663200 * c11 * x_bar4 + 181440 * c9 * x_bar2 + 5040 * c7) f8 = (259459200 * c15 * x_bar7 + 51891840 * c13 * x_bar5 + 6652800 * c11 * x_bar3 + 362880 * c9 * x_bar) f9 = (1.8162144e9 * c15 * x_bar6 + 259459200 * c13 * x_bar4 + 19958400 * c11 * x_bar2 + 362880 * c9) f10 = (1.08972864e10 * c15 * x_bar5 + 1.0378368e9 * c13 * x_bar3 + 39916800 * c11 * x_bar) f11 = 5.4486432e10 * c15 * x_bar4 + 3.1135104e9 * c13 * x_bar2 + 39916800 * c11 f12 = 2.17945728e11 * c15 * x_bar3 + 6.2270208e9 * c13 * x_bar f13 = 6.53837184 * c15 * x_bar2 + 6.2270208e9 * c13 f14 = 1.307674368e13 * c15 * x_bar f15 = 1.307674368e13 * c15 # Solving g parameters defined by equations (37a), (37b), (37c) and (37d) g1 = 1 / 2 g2 = 1 / 6 g3 = 1 / 24 g4 = 1 / 120 g5 = 1 / 720 g6 = 1 / 5040 g7 = 1 / 40320 g8 = 1 / 362880 g9 = 1 / 3628800 g10 = 1 / 39916800 g11 = 1 / 479001600 g12 = 1 / 6.2270208e9 g13 = 1 / 8.71782912e10 g14 = 1 / 1.307674368e12 # Solving for the u_{i} and h_{i} variables defined by equation (38) u1 = -f / f1 h2 = f1 + g1 * u1 * f2 u2 = -f / h2 h3 = f1 + g1 * u2 * f2 + g2 * u2**2 * f3 u3 = -f / h3 h4 = f1 + g1 * u3 * f2 + g2 * u3**2 * f3 + g3 * u3**3 * f4 u4 = -f / h4 h5 = f1 + g1 * u4 * f2 + g2 * u4**2 * f3 + g3 * u4**3 * f4 + g4 * u4**4 * f5 u5 = -f / h5 h6 = (f1 + g1 * u5 * f2 + g2 * u5**2 * f3 + g3 * u5**3 * f4 + g4 * u5**4 * f5 + g5 * u5**5 * f6) u6 = -f / h6 h7 = (f1 + g1 * u6 * f2 + g2 * u6**2 * f3 + g3 * u6**3 * f4 + g4 * u6**4 * f5 + g5 * u6**5 * f6 + g6 * u6**6 * f7) u7 = -f / h7 h8 = (f1 + g1 * u7 * f2 + g2 * u7**2 * f3 + g3 * u7**3 * f4 + g4 * u7**4 * f5 + g5 * u7**5 * f6 + g6 * u7**6 * f7 + g7 * u7**7 * f8) u8 = -f / h8 h9 = (f1 + g1 * u8 * f2 + g2 * u8**2 * f3 + g3 * u8**3 * f4 + g4 * u8**4 * f5 + g5 * u8**5 * f6 + g6 * u8**6 * f7 + g7 * u8**7 * f8 + g8 * u8**8 * f9) u9 = -f / h9 h10 = (f1 + g1 * u9 * f2 + g2 * u9**2 * f3 + g3 * u9**3 * f4 + g4 * u9**4 * f5 + g5 * u9**5 * f6 + g6 * u9**6 * f7 + g7 * u9**7 * f8 + g8 * u9**8 * f9 + g9 * u9**9 * f10) u10 = -f / h10 h11 = (f1 + g1 * u10 * f2 + g2 * u10**2 * f3 + g3 * u10**3 * f4 + g4 * u10**4 * f5 + g5 * u10**5 * f6 + g6 * u10**6 * f7 + g7 * u10**7 * f8 + g8 * u10**8 * f9 + g9 * u10**9 * f10 + g10 * u10**10 * f11) u11 = -f / h11 h12 = (f1 + g1 * u11 * f2 + g2 * u11**2 * f3 + g3 * u11**3 * f4 + g4 * u11**4 * f5 + g5 * u11**5 * f6 + g6 * u11**6 * f7 + g7 * u11**7 * f8 + g8 * u11**8 * f9 + g9 * u11**9 * f10 + g10 * u11**10 * f11 + g11 * u11**11 * f12) u12 = -f / h12 h13 = (f1 + g1 * u12 * f2 + g2 * u12**2 * f3 + g3 * u12**3 * f4 + g4 * u12**4 * f5 + g5 * u12**5 * f6 + g6 * u12**6 * f7 + g7 * u12**7 * f8 + g8 * u12**8 * f9 + g9 * u12**9 * f10 + g10 * u12**10 * f11 + g11 * u12**11 * f12 + g12 * u12**12 * f13) u13 = -f / h13 h14 = (f1 + g1 * u13 * f2 + g2 * u13**2 * f3 + g3 * u13**3 * f4 + g4 * u13**4 * f5 + g5 * u13**5 * f6 + g6 * u13**6 * f7 + g7 * u13**7 * f8 + g8 * u13**8 * f9 + g9 * u13**9 * f10 + g10 * u13**10 * f11 + g11 * u13**11 * f12 + g12 * u13**12 * f13 + g13 * u13**13 * f14) u14 = -f / h14 h15 = (f1 + g1 * u14 * f2 + g2 * u14**2 * f3 + g3 * u14**3 * f4 + g4 * u14**4 * f5 + g5 * u14**5 * f6 + g6 * u14**6 * f7 + g7 * u14**7 * f8 + g8 * u14**8 * f9 + g9 * u14**9 * f10 + g10 * u14**10 * f11 + g11 * u14**11 * f12 + g12 * u14**12 * f13 + g13 * u14**13 * f14 + g14 * u14**14 * f15) u15 = -f / h15 # Solving for x x = x_bar + u15 w = x - 0.01171875 * x**17 / (1 + ecc) # Solving for the true anomaly from eccentricity anomaly E = M + ecc * (-16384 * w**15 + 61440 * w**13 - 92160 * w**11 + 70400 * w**9 - 28800 * w**7 + 6048 * w**5 - 560 * w**3 + 15 * w) nu = E_to_nu(E, ecc) return coe2rv(k, p, ecc, inc, raan, argp, nu)
def gooding(k, r0, v0, tof, numiter=150, rtol=1e-8): """ Solves the Elliptic Kepler Equation with a cubic convergence and accuracy better than 10e-12 rad is normally achieved. It is not valid for eccentricities equal or higher than 1.0. Parameters ---------- k : float Standard gravitational parameter of the attractor. r : 1x3 vector Position vector. v : 1x3 vector Velocity vector. tof : float Time of flight. rtol: float Relative error for accuracy of the method. Returns ------- rr : 1x3 vector Propagated position vectors. vv : 1x3 vector Note ---- Original paper for the algorithm: https://doi.org/10.1007/BF01238923 """ # Solve first for eccentricity and mean anomaly p, ecc, inc, raan, argp, nu = rv2coe(k, r0, v0) # TODO: parabolic and hyperbolic not implemented cases if ecc >= 1.0: raise NotImplementedError( "Parabolic/Hyperbolic cases still not implemented in gooding.") M0 = nu_to_M(nu, ecc, delta=0) semi_axis_a = p / (1 - ecc**2) n = np.sqrt(k / np.abs(semi_axis_a)**3) M = M0 + n * tof # Start the computation n = 0 c = ecc * np.cos(M) s = ecc * np.sin(M) psi = s / np.sqrt(1 - 2 * c + ecc**2) f = 1.0 while f**2 >= rtol and n <= numiter: xi = np.cos(psi) eta = np.sin(psi) fd = (1 - c * xi) + s * eta fdd = c * eta + s * xi f = psi - fdd psi = psi - f * fd / (fd**2 - 0.5 * f * fdd) n += 1 E = M + psi nu = E_to_nu(E, ecc) return coe2rv(k, p, ecc, inc, raan, argp, nu)
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. r : 1x3 vector Position vector. v : 1x3 vector Velocity vector. tof : float Time of flight. rtol: float Relative error for accuracy of the method. Returns ------- rr : 1x3 vector Propagated position vectors. vv : 1x3 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) M0 = nu_to_M(nu, ecc, delta=0) semi_axis_a = p / (1 - ecc**2) n = np.sqrt(k / np.abs(semi_axis_a)**3) M = M0 + n * tof # Range mean anomaly xma = M - 2 * np.pi * np.floor(M / 2 / np.pi) if ecc == 0: # Solving for circular orbit nu = xma return coe2rv(k, p, ecc, inc, raan, argp, nu) elif ecc < 1.0: # For elliptical orbit E = xma + 0.85 * np.sign(np.sin(xma)) * ecc else: # For parabolic and hyperbolic E = np.log(2 * xma / ecc + 1.8) # Iterations begin n = 0 while n <= numiter: if ecc < 1.0: s = ecc * np.sin(E) c = ecc * np.cos(E) f = E - s - xma fp = 1 - c fpp = s fppp = c else: s = ecc * np.sinh(E) c = ecc * np.cosh(E) f = s - E - xma fp = c - 1 fpp = s fppp = c if np.abs(f) <= rtol: if ecc < 1.0: sta = np.sqrt(1 - ecc**2) * np.sin(E) cta = np.cos(E) - ecc else: sta = np.sqrt(ecc**2 - 1) * np.sinh(E) cta = ecc - np.cosh(E) nu = np.arctan2(sta, cta) return coe2rv(k, p, ecc, inc, raan, argp, nu) else: delta = -f / fp delta_star = -f / (fp + 0.5 * delta * fpp) deltak = -f / (fp + 0.5 * delta_star * fpp + delta_star**2 * fppp / 6) E = E + deltak n += 1 raise ValueError("Maximum number of iterations has been reached.")
def updateDf(df, runHPOP, varyCols, setSunAreaEqualToDragArea): if any(col in ['a', 'e', 'i', 'AoP', 'RAAN', 'TA', 'Rp', 'Ra'] for col in varyCols): # absolute e df['e'] = abs(df['e']) # Update dependant values, based on selection of Rp,Ra,a,e # If 2 are varied if 'Rp' in varyCols and 'e' in varyCols: df['a'] = df['Rp'] / (1 - df['e']) df['Ra'] = df['a'] * (1 + df['e']) df['p'] = df['a'] * (1 - df['e']**2) elif 'Ra' in varyCols and 'e' in varyCols: df['a'] = df['Ra'] / (1 + df['e']) df['Rp'] = df['a'] * (1 - df['e']) df['p'] = df['a'] * (1 - df['e']**2) elif 'Rp' in varyCols and 'a' in varyCols: df['e'] = 1 - df['Rp'] / df['a'] df['Ra'] = df['a'] * (1 + df['e']) df['p'] = df['a'] * (1 - df['e']**2) elif 'Ra' in varyCols and 'a' in varyCols: df['e'] = df['Ra'] / df['a'] - 1 df['Rp'] = df['a'] * (1 - df['e']) df['p'] = df['a'] * (1 - df['e']**2) # If a,e pair or Rp,Ra pair or just 1 is varied else: if any(col in ['a', 'e'] for col in varyCols): df['Rp'] = df['a'] * (1 - df['e']) df['Ra'] = df['a'] * (1 + df['e']) df['p'] = df['a'] * (1 - df['e']**2) if any(col in ['Rp', 'Ra'] for col in varyCols): switchRaRp = df['Ra'] < df['Rp'] tempRp = df.loc[switchRaRp, 'Rp'] df.loc[switchRaRp, 'Rp'] = df.loc[switchRaRp, 'Ra'] df.loc[switchRaRp, 'Ra'] = tempRp df['a'] = (df['Rp'] + df['Ra']) / 2 df['e'] = (df['Ra'] - df['Rp']) / (df['Ra'] + df['Rp']) df['p'] = df['a'] * (1 - df['e']**2) # Wrap values between 0 and 180 or 0 and 360 if any(col in ['i', 'AoP', 'RAAN', 'TA'] for col in varyCols): df.loc[df['i'] > 180, 'i'] = 180 - ( df.loc[df['i'] > 180, 'i'] - 180 ) # if > 180, subtract the amount over from 180 df.loc[df['i'] < 180, 'i'] = abs(df.loc[df['i'] < 180, 'i']) df.loc[df['RAAN'] < 360, 'RAAN'] = df.loc[df['RAAN'] < 360, 'RAAN'] + 360 df.loc[df['RAAN'] > 360, 'RAAN'] = df.loc[df['RAAN'] > 360, 'RAAN'] - 360 df.loc[df['AoP'] < 360, 'AoP'] = df.loc[df['AoP'] < 360, 'AoP'] + 360 df.loc[df['AoP'] > 360, 'AoP'] = df.loc[df['AoP'] > 360, 'AoP'] - 360 df.loc[df['TA'] < 360, 'TA'] = df.loc[df['TA'] < 360, 'TA'] + 360 df.loc[df['TA'] > 360, 'TA'] = df.loc[df['TA'] > 360, 'TA'] - 360 # Update Cartesian rs = np.zeros((len(df), 3)) vs = np.zeros((len(df), 3)) for ii in range(len(df)): rs[ii], vs[ii] = coe2rv(GM_earth * 1e-9, df['p'].iloc[ii], df['e'].iloc[ii], df['i'].iloc[ii] * np.pi / 180, df['RAAN'].iloc[ii] * np.pi / 180, df['AoP'].iloc[ii] * np.pi / 180, df['TA'].iloc[ii] * np.pi / 180) df['x'] = rs[:, 0] df['y'] = rs[:, 1] df['z'] = rs[:, 2] df['Vx'] = vs[:, 0] df['Vy'] = vs[:, 1] df['Vz'] = vs[:, 2] # Update Cartesian and dependant variables if any(col in ['x', 'y', 'z', 'Vx', 'Vy', 'Vz'] for col in varyCols): # Convert back to classical rs = df[['x', 'y', 'z']].to_numpy() vs = df[['Vx', 'Vy', 'Vz']].to_numpy() oes = np.zeros((len(df), 6)) for ii in range(len(df)): oes[ii, 0], oes[ii, 1], oes[ii, 2], oes[ii, 3], oes[ii, 4], oes[ii, 5] = rv2coe( GM_earth * 1e-9, rs[ii], vs[ii]) df['p'] = oes[:, 0] df['e'] = oes[:, 1] df['i'] = oes[:, 2] * 180 / np.pi df['RAAN'] = oes[:, 3] * 180 / np.pi df['AoP'] = oes[:, 4] * 180 / np.pi df['TA'] = oes[:, 5] * 180 / np.pi df['a'] = df['p'] / (1 - df['e']**2) df['Rp'] = df['a'] * (1 - df['e']) df['Ra'] = df['a'] * (1 + df['e']) df.loc[df['TA'] < 0, 'TA'] = df.loc[df['TA'] < 0, 'TA'] + 360 df.loc[df['TA'] > 360, 'TA'] = df.loc[df['TA'] > 360, 'TA'] - 360 # Update satellite characteristics if any(col in ['Cd', 'Cr', 'Drag Area', 'Sun Area', 'Mass'] for col in varyCols): if setSunAreaEqualToDragArea == True: df['Sun Area'] = df['Drag Area'] df['Cd*Drag Area/Mass'] = df['Cd'] * df['Drag Area'] / df['Mass'] df['Cr*Sun Area/Mass'] = df['Cr'] * df['Sun Area'] / df['Mass'] # Replace flux sigma with 0 df.loc[df['SolarFluxFile'] == 'SpaceWeather-v1.2.txt', 'Flux Sigma Level'] = 0 # Get rid of -0 values df.loc[df['Flux Sigma Level'] == -0, 'Flux Sigma Level'] = 0 return df
def generateTradeStudy(tradeStudy): fileName = os.getcwd() + '\\Results\\' + tradeStudy.fileName runHPOP = tradeStudy.runHPOP epoch = tradeStudy.epoch a = tradeStudy.a e = tradeStudy.e i = tradeStudy.i RAAN = tradeStudy.RAAN AoP = tradeStudy.AoP TA = tradeStudy.TA Cd = tradeStudy.Cd Cr = tradeStudy.Cr DragArea = tradeStudy.DragArea SunArea = tradeStudy.SunArea Mass = tradeStudy.Mass OrbPerCal = tradeStudy.OrbPerCal GaussQuad = tradeStudy.GaussQuad SigLvl = tradeStudy.SigLvl SolFlxFile = tradeStudy.SolFlxFile AtmDen = tradeStudy.AtmDen SecondOrderOblateness = tradeStudy.SecondOrderOblateness numberOfRuns = tradeStudy.numberOfRuns howToVary = tradeStudy.howToVary varyCols = tradeStudy.varyCols varyValues = tradeStudy.varyValues setSunAreaEqualToDragArea = tradeStudy.setSunAreaEqualToDragArea np.random.seed(seed=1) # Generate Additional Columns Rp = a * (1 - e) Ra = a * (1 + e) p = a * (1 - e**2) rs, vs = coe2rv(GM_earth * 1e-9, p, e, i * np.pi / 180, RAAN * np.pi / 180, AoP * np.pi / 180, TA * np.pi / 180) x = rs[0] y = rs[1] z = rs[2] Vx = vs[0] Vy = vs[1] Vz = vs[2] CdAM = Cd * DragArea / Mass CrAM = Cr * SunArea / Mass # Generate Dataframe to store all of the runs data = [ epoch, a, e, i, RAAN, AoP, TA, Rp, Ra, p, x, y, z, Vx, Vy, Vz, Cd, Cr, DragArea, SunArea, Mass, CdAM, CrAM, OrbPerCal, GaussQuad, SigLvl, SolFlxFile, AtmDen, SecondOrderOblateness ] columns = [ 'epoch', 'a', 'e', 'i', 'RAAN', 'AoP', 'TA', 'Rp', 'Ra', 'p', 'x', 'y', 'z', 'Vx', 'Vy', 'Vz', 'Cd', 'Cr', 'Drag Area', 'Sun Area', 'Mass', 'Cd*Drag Area/Mass', 'Cr*Sun Area/Mass', 'Orb Per Calc', 'Gaussian Quad', 'Flux Sigma Level', 'SolarFluxFile', 'Density Model', '2nd Order Oblateness' ] df = pd.DataFrame(data=data, index=columns).T df[df.columns[:-3]] = df[df.columns[:-3]].astype(float) # Grid Search if howToVary.lower() == 'gridsearch': # Create grid search of parameters and update the Dataframe numOfLevels = [len(val) for val in varyValues] runs = fullfact(numOfLevels).astype(int) paramdf = pd.DataFrame() for ii in range(len(runs.T)): paramdf[varyCols[ii]] = varyValues[ii][runs[:, ii]] df = pd.concat([df] * len(runs), ignore_index=True) for col in paramdf.columns: df[col] = paramdf[col] # Latin Hypercube elif howToVary.lower() == 'latinhypercube': # Generate runs lhd = lhs(len(varyCols), samples=numberOfRuns) # lhd = stats.norm(loc=0, scale=1).ppf(lhd) # Convert to a normal distribution lhd = pd.DataFrame(lhd) adjustEpoch = False if 'epoch' in varyCols: date1 = yydddToDatetime(varyValues[0][0]) date2 = yydddToDatetime(varyValues[0][1]) deltaDays = lhd[0] * (date2 - date1).days minDate = [varyValues[0][0] for i in range(numberOfRuns)] varyCols.remove('epoch') varyValues = varyValues[1:] lhd = lhd.drop(0, axis=1) adjustEpoch = True lhd.columns = varyCols # Replace string columns with categories strii = [ ii for ii in range(len(varyValues)) if isinstance(varyValues[ii][0], str) ] for ii in strii: lhd.iloc[:, ii] = pd.cut(lhd.iloc[:, ii], len(varyValues[ii]), labels=varyValues[ii]) # Replace float columns with values in range varyValues = np.array(varyValues) floatii = lhd.dtypes == float lhsMinMax = varyValues[floatii] lhsMinMax = np.concatenate(lhsMinMax, axis=0).reshape(-1, 2) lhd.loc[:, floatii] = lhd.loc[:, floatii] * ( lhsMinMax[:, 1] - lhsMinMax[:, 0]) + lhsMinMax[:, 0] indxs = [ ii for ii in range(len(varyCols)) if varyCols[ii] in ['Orb Per Calc', 'Gaussian Quad', 'Flux Sigma Level'] ] lhd.iloc[:, indxs] = lhd.iloc[:, indxs].round() # Create df df = pd.concat([df] * len(lhd), ignore_index=True) if adjustEpoch == True: df['epoch'] = [ adjustDate(yyddd, deltaDay) for yyddd, deltaDay in zip(minDate, deltaDays) ] for col in lhd.columns: df[col] = lhd[col] # Perturb elif howToVary.lower() == 'perturb': # Sample from a normal distribution rv = stats.norm() rvVals = rv.rvs((numberOfRuns, len(varyCols))) # Create perturbation df pertdf = pd.DataFrame(rvVals) * varyValues pertdf.columns = varyCols # Duplicate original df by the number of runs df = pd.concat([df] * numberOfRuns, ignore_index=True) if 'epoch' in varyCols: df['epoch'] = [ adjustDate(yyddd, deltaDay) for yyddd, deltaDay in zip(df['epoch'], pertdf['epoch']) ] varyCols.remove('epoch') varyValues = varyValues[1:] for col in varyCols: df[col] = df[col] + pertdf[col] # Update dependant values df = updateDf(df, runHPOP, varyCols, setSunAreaEqualToDragArea) # Convert all columns to floats and round to the 10th decimal, otherwise there are some numerical rounding issues. cols = [ col for col in df.columns if col not in ['SolarFluxFile', 'Density Model', '2nd Order Oblateness'] ] df[cols] = df[cols].astype(float) for col in cols: df[col] = np.round(df[col], 10) # Add results df['LT Orbits'] = np.nan df['LT Years'] = np.nan df['LT Runtime'] = np.nan if runHPOP == True: df['HPOP Years'] = np.nan df['HPOP Runtime'] = np.nan df.index.name = 'Run ID' df = df.reset_index() df.to_csv(fileName) # Create a new csv to store the results return df
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)
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)
def test_convert_between_coe_and_rv_is_transitive(expected_res): k = Earth.k.to(u.km**3 / u.s**2).value # u.km**3 / u.s**2 res = rv2coe(k, *coe2rv(k, *expected_res)) assert_allclose(res, expected_res)