def test_raan_from_ltan_sentinel5p(): # SENTINEL-5P LTAN: 13:30 # LTAN from https://sentinels.copernicus.eu/web/sentinel/missions/sentinel-5p/geographical-coverage # 1 42969U 17064A 20049.78099017 -.00000032 00000-0 54746-5 0 9991 # 2 42969 98.7249 350.5997 0001077 82.0109 278.1189 14.19549365121775 ltan = (13 + (30 / 60)) * u.hourangle epoch = astropy.time.Time( astropy.time.Time("2020-01-01 00:00").to_value("mjd") + 49.78099017 - 1, format="mjd", ) expected_raan = 350.5997 * u.deg raan = angles.raan_from_ltan(epoch, ltan) assert_allclose(raan.wrap_at(360 * u.deg).to(u.deg), expected_raan, atol=0.3)
def test_raan_from_ltan_metopb(): # MetOp-B LTAN: 21:31:45 # LTAN from https://www.ospo.noaa.gov/Operations/METOP/status.html # METOP-B # 1 38771U 12049A 20049.95408566 -.00000014 00000-0 13607-4 0 9997 # 2 38771 98.7092 110.9899 0001263 48.5458 295.8781 14.21485930385043 ltan = (21 + ((31 + 45 / 60) / 60)) * u.hourangle epoch = astropy.time.Time( astropy.time.Time("2020-01-01 00:00").to_value("mjd") + 49.95408566 - 1, format="mjd", ) expected_raan = 110.9899 * u.deg raan = angles.raan_from_ltan(epoch, ltan) assert_allclose(raan.wrap_at(360 * u.deg).to(u.deg), expected_raan, atol=0.3)
def heliosynchronous( cls, attractor, a=None, ecc=None, inc=None, ltan=10.0 * u.hourangle, argp=0 * u.deg, nu=0 * u.deg, epoch=J2000, plane=Planes.EARTH_EQUATOR, ): r""" Solves for a Sun-Synchronous orbit. These orbits make use of the J2 perturbation to precess in order to be always towards Sun. At least two parameters of the set {a, ecc, inc} are needed in order to solve for these kind of orbits. Relationships among them are given by: .. math:: \begin{align} a &= \left (\frac{-3R_{\bigoplus}J_{2}\sqrt{\mu}\cos(i)}{2\dot{\Omega}(1-e^2)^2} \right ) ^ {\frac{2}{7}}\\ e &= \sqrt{1 - \sqrt{\frac{-3R_{\bigoplus}J_{2}\sqrt{\mu}cos(i)}{2a^{\frac{7}{2}}\dot{\Omega}}}}\\ i &= \arccos{\left ( \frac{-2a^{\frac{7}{2}}\dot{\Omega}(1-e^2)^2}{3R_{\bigoplus}J_{2}\sqrt{\mu}} \right )}\\ \end{align} Parameters ---------- a: ~astropy.units.Quantity Semi-major axis. ecc: ~astropy.units.Quantity Eccentricity. inc: ~astropy.units.Quantity Inclination. ltan: ~astropy.units.Quantity Local time of the ascending node which will be translated to the Right ascension of the ascending node. argp : ~astropy.units.Quantity Argument of the pericenter. nu : ~astropy.units.Quantity True anomaly. epoch : ~astropy.time.Time, optional Epoch, default to J2000. plane : ~poliastro.frames.Planes Fundamental plane of the frame. """ mean_elements = get_mean_elements(attractor) n_sunsync = ( np.sqrt(mean_elements.attractor.k / abs(mean_elements.a**3)) * u.one).decompose() R_SSO = attractor.R k_SSO = attractor.k J2_SSO = attractor.J2 try: with np.errstate(invalid="raise"): if (a is None) and (ecc is None) and (inc is None): # We check sufficient number of parameters raise ValueError( "At least two parameters of the set {a, ecc, inc} are required." ) elif a is None and (ecc is not None) and (inc is not None): # Semi-major axis is the unknown variable a = (-3 * R_SSO**2 * J2_SSO * np.sqrt(k_SSO) / (2 * n_sunsync * (1 - ecc**2)**2) * np.cos(inc))**(2 / 7) elif ecc is None and (a is not None) and (inc is not None): # Eccentricity is the unknown variable _ecc_0 = np.sqrt(-3 * R_SSO**2 * J2_SSO * np.sqrt(k_SSO) * np.cos(inc.to(u.rad)) / (2 * a**(7 / 2) * n_sunsync)) ecc = np.sqrt(1 - _ecc_0) elif inc is None and (ecc is not None) and (a is not None): # Inclination is the unknown variable inc = np.arccos(-2 * a**(7 / 2) * n_sunsync * (1 - ecc**2)**2 / (3 * R_SSO**2 * J2_SSO * np.sqrt(k_SSO))) except FloatingPointError: raise ValueError( "No SSO orbit with given parameters can be found.") # Temporary fix: raan_from_ltan works only for Earth if attractor.name.lower() != "earth": raise NotImplementedError( "Attractors other than Earth not supported yet") raan = raan_from_ltan(epoch, ltan) ss = cls.from_classical(attractor, a, ecc, inc, raan, argp, nu, epoch=epoch.tdb, plane=plane) return ss