def danby_coe(k, p, ecc, inc, raan, argp, nu, tof, numiter=20, rtol=1e-8): semi_axis_a = p / (1 - ecc**2) n = np.sqrt(k / np.abs(semi_axis_a)**3) if ecc == 0: # Solving for circular orbit M0 = E_to_M(nu_to_E(nu, ecc), ecc) M = M0 + n * tof nu = M - 2 * np.pi * np.floor(M / 2 / np.pi) return nu elif ecc < 1.0: # For elliptical orbit M0 = E_to_M(nu_to_E(nu, ecc), ecc) M = M0 + n * tof xma = M - 2 * np.pi * np.floor(M / 2 / np.pi) E = xma + 0.85 * np.sign(np.sin(xma)) * ecc else: # For parabolic and hyperbolic M0 = F_to_M(nu_to_F(nu, ecc), ecc) M = M0 + n * tof xma = M - 2 * np.pi * np.floor(M / 2 / np.pi) 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) break 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 else: raise ValueError("Maximum number of iterations has been reached.") return nu
def nu_from_delta_t(delta_t, ecc, k=1.0, q=1.0, delta=1e-2): """True anomaly for given elapsed time since periapsis. Parameters ---------- delta_t : float Time elapsed since periapsis. ecc : float Eccentricity. k : float Gravitational parameter. q : float Periapsis distance. delta : float Parameter that controls the size of the near parabolic region. Returns ------- nu : float True anomaly. """ if ecc < 1 - delta: # Strong elliptic n = np.sqrt(k * (1 - ecc)**3 / q**3) M = n * delta_t # This might represent several revolutions, # so we wrap the true anomaly E = M_to_E((M + np.pi) % (2 * np.pi) - np.pi, ecc) nu = E_to_nu(E, ecc) elif 1 - delta <= ecc < 1: E_delta = np.arccos((1 - delta) / ecc) # We compute M assuming we are in the strong elliptic case # and verify later n = np.sqrt(k * (1 - ecc)**3 / q**3) M = n * delta_t # We check against abs(M) because E_delta could also be negative if E_to_M(E_delta, ecc) <= abs(M): # Strong elliptic, proceed # This might represent several revolutions, # so we wrap the true anomaly E = M_to_E((M + np.pi) % (2 * np.pi) - np.pi, ecc) nu = E_to_nu(E, ecc) else: # Near parabolic, recompute M n = np.sqrt(k / (2 * q**3)) M = n * delta_t D = M_to_D_near_parabolic(M, ecc) nu = D_to_nu(D) elif ecc == 1: # Parabolic n = np.sqrt(k / (2 * q**3)) M = n * delta_t D = M_to_D(M) nu = D_to_nu(D) elif 1 < ecc <= 1 + delta: F_delta = np.arccosh((1 + delta) / ecc) # We compute M assuming we are in the strong hyperbolic case # and verify later n = np.sqrt(k * (ecc - 1)**3 / q**3) M = n * delta_t # We check against abs(M) because F_delta could also be negative if F_to_M(F_delta, ecc) <= abs(M): # Strong hyperbolic, proceed F = M_to_F(M, ecc) nu = F_to_nu(F, ecc) else: # Near parabolic, recompute M n = np.sqrt(k / (2 * q**3)) M = n * delta_t D = M_to_D_near_parabolic(M, ecc) nu = D_to_nu(D) # elif 1 + delta < ecc: else: # Strong hyperbolic n = np.sqrt(k * (ecc - 1)**3 / q**3) M = n * delta_t F = M_to_F(M, ecc) nu = F_to_nu(F, ecc) return nu
def mikkola_coe(k, p, ecc, inc, raan, argp, nu, tof): a = p / (1 - ecc**2) n = np.sqrt(k / np.abs(a)**3) # Solve for specific geometrical case if ecc < 1.0: # Equation (9a) alpha = (1 - ecc) / (4 * ecc + 1 / 2) M0 = E_to_M(nu_to_E(nu, ecc), ecc) else: alpha = (ecc - 1) / (4 * ecc + 1 / 2) M0 = F_to_M(nu_to_F(nu, ecc), ecc) M = M0 + n * tof 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 nu
def delta_t_from_nu(nu, ecc, k=1.0, q=1.0, delta=1e-2): """Time elapsed since periapsis for given true anomaly. Parameters ---------- nu : float True anomaly. ecc : float Eccentricity. k : float Gravitational parameter. q : float Periapsis distance. delta : float Parameter that controls the size of the near parabolic region. Returns ------- delta_t : float Time elapsed since periapsis. """ assert -np.pi <= nu < np.pi if ecc < 1 - delta: # Strong elliptic E = nu_to_E(nu, ecc) # (-pi, pi] M = E_to_M(E, ecc) # (-pi, pi] n = np.sqrt(k * (1 - ecc)**3 / q**3) elif 1 - delta <= ecc < 1: E = nu_to_E(nu, ecc) # (-pi, pi] if delta <= 1 - ecc * np.cos(E): # Strong elliptic M = E_to_M(E, ecc) # (-pi, pi] n = np.sqrt(k * (1 - ecc)**3 / q**3) else: # Near parabolic D = nu_to_D(nu) # (-∞, ∞) # If |nu| is far from pi this result is bounded # because the near parabolic region shrinks in its vicinity, # otherwise the eccentricity is very close to 1 # and we are really far away M = D_to_M_near_parabolic(D, ecc) n = np.sqrt(k / (2 * q**3)) elif ecc == 1: # Parabolic D = nu_to_D(nu) # (-∞, ∞) M = D_to_M(D) # (-∞, ∞) n = np.sqrt(k / (2 * q**3)) elif 1 + ecc * np.cos(nu) < 0: # Unfeasible region return np.nan elif 1 < ecc <= 1 + delta: # NOTE: Do we need to wrap nu here? # For hyperbolic orbits, it should anyway be in # (-arccos(-1 / ecc), +arccos(-1 / ecc)) F = nu_to_F(nu, ecc) # (-∞, ∞) if delta <= ecc * np.cosh(F) - 1: # Strong hyperbolic M = F_to_M(F, ecc) # (-∞, ∞) n = np.sqrt(k * (ecc - 1)**3 / q**3) else: # Near parabolic D = nu_to_D(nu) # (-∞, ∞) M = D_to_M_near_parabolic(D, ecc) # (-∞, ∞) n = np.sqrt(k / (2 * q**3)) elif 1 + delta < ecc: # Strong hyperbolic F = nu_to_F(nu, ecc) # (-∞, ∞) M = F_to_M(F, ecc) # (-∞, ∞) n = np.sqrt(k * (ecc - 1)**3 / q**3) else: raise RuntimeError return M / n