diff --git a/pyorbital/orbital.py b/pyorbital/orbital.py index 6100be3..a073677 100644 --- a/pyorbital/orbital.py +++ b/pyorbital/orbital.py @@ -576,7 +576,7 @@ class OrbitElements: def __init__(self, tle): """Initialize the class.""" self.epoch = tle.epoch - self.excentricity = tle.excentricity + self.eccentricity = tle.eccentricity self.inclination = np.deg2rad(tle.inclination) self.right_ascension = np.deg2rad(tle.right_ascension) self.arg_perigee = np.deg2rad(tle.arg_perigee) @@ -593,20 +593,26 @@ def __init__(self, tle): self.right_ascension_lon = self.right_ascension - astronomy.gmst(self.epoch) self.right_ascension_lon = np.fmod(self.right_ascension_lon + np.pi, 2 * np.pi) - np.pi + @property + def excentricity(self): + """Get 'eccentricity' using legacy 'excentricity' name.""" + warnings.warn("The 'eccentricity' property is deprecated in favor of 'eccentricity'", stacklevel=2) + return self.eccentricity + @property def apogee(self): """Compute apogee altitude in kilometers.""" - return ((self.semi_major_axis * (1 + self.excentricity)) / AE - AE) * XKMPER + return ((self.semi_major_axis * (1 + self.eccentricity)) / AE - AE) * XKMPER @property def perigee(self): """Compute perigee altitude in kilometers.""" - return ((self.semi_major_axis * (1 - self.excentricity)) / AE - AE) * XKMPER + return ((self.semi_major_axis * (1 - self.eccentricity)) / AE - AE) * XKMPER @property def is_circular(self): """Check if orbit is nearly circular.""" - return self.excentricity < 1e-3 + return self.eccentricity < 1e-3 @property def is_retrograde(self): @@ -616,7 +622,7 @@ def is_retrograde(self): def _get_true_anomaly(self): """Computes the True Anomaly (nu) from Mean Anomaly (M) and Eccentricity (e).""" M = self.mean_anomaly - e = self.excentricity + e = self.eccentricity E = M # Initial guess for Eccentric Anomaly (E) # Iteratively solve Kepler's Equation (M = E - e*sin(E)) @@ -644,8 +650,8 @@ def position_vector_in_orbital_plane(self): true_anomaly = self._get_true_anomaly() # Calculate radius (r) and coordinates using the True Anomaly - r = self.semi_major_axis * (1 - self.excentricity**2) / \ - (1 + self.excentricity * np.cos(true_anomaly)) + r = self.semi_major_axis * (1 - self.eccentricity**2) / \ + (1 + self.eccentricity * np.cos(true_anomaly)) x = r * np.cos(true_anomaly) y = r * np.sin(true_anomaly) @@ -671,11 +677,11 @@ def _get_velocity_at_apsis(self, e_1, e_2): def velocity_at_perigee(self): """Compute orbital velocity at perigee in km/s.""" - return self._get_velocity_at_apsis(1 - self.excentricity, 1 + self.excentricity) + return self._get_velocity_at_apsis(1 - self.eccentricity, 1 + self.eccentricity) def velocity_at_apogee(self): """Compute orbital velocity at apogee in km/s.""" - return self._get_velocity_at_apsis(1 + self.excentricity, 1 - self.excentricity) + return self._get_velocity_at_apsis(1 + self.eccentricity, 1 - self.eccentricity) def _calculate_mean_motion_and_semi_major_axis(self): """Apply SGP4 perturbation corrections to mean motion and semi-major axis. @@ -684,10 +690,10 @@ def _calculate_mean_motion_and_semi_major_axis(self): """ a_1 = (XKE / self.mean_motion) ** (2.0 / 3) delta_1 = (3 / 2.0) * (CK2 / a_1**2) * ((3 * np.cos(self.inclination)**2 - 1) / - (1 - self.excentricity**2)**(3 / 2)) + (1 - self.eccentricity**2)**(3 / 2)) a_0 = a_1 * (1 - delta_1 / 3 - delta_1**2 - (134.0 / 81) * delta_1**3) delta_0 = (3 / 2.0) * (CK2 / a_0**2) * ((3 * np.cos(self.inclination)**2 - 1) / - (1 - self.excentricity**2)**(3 / 2)) + (1 - self.eccentricity**2)**(3 / 2)) corrected_mean_motion = self.mean_motion / (1 + delta_0) corrected_semi_major_axis = a_0 / (1 - delta_0) @@ -704,7 +710,7 @@ def __init__(self, orbit_elements): _check_orbital_elements(orbit_elements) - self.eo = orbit_elements.excentricity + self.eo = orbit_elements.eccentricity self.xincl = orbit_elements.inclination self.xno = orbit_elements.original_mean_motion self.bstar = orbit_elements.bstar @@ -1290,8 +1296,8 @@ def _collect_return_values(self): def _check_orbital_elements(orbit_elements): - if not (0 < orbit_elements.excentricity < ECC_LIMIT_HIGH): - raise OrbitalError("Eccentricity out of range: %e" % orbit_elements.excentricity) + if not (0 < orbit_elements.eccentricity < ECC_LIMIT_HIGH): + raise OrbitalError("Eccentricity out of range: %e" % orbit_elements.eccentricity) if not ((0.0035 * 2 * np.pi / XMNPDA) < orbit_elements.original_mean_motion < (18 * 2 * np.pi / XMNPDA)): raise OrbitalError("Mean motion out of range: %e" % orbit_elements.original_mean_motion) if not (0 < orbit_elements.inclination < np.pi): diff --git a/pyorbital/tests/test_orbit_elements.py b/pyorbital/tests/test_orbit_elements.py index 68e86eb..95122e5 100644 --- a/pyorbital/tests/test_orbit_elements.py +++ b/pyorbital/tests/test_orbit_elements.py @@ -43,7 +43,7 @@ class MockTLE: def __init__(self): """Initialize mock TLE values for testing.""" self.epoch = datetime.datetime(2025, 9, 30, 12, 0, 0) - self.excentricity = 0.001 + self.eccentricity = 0.001 self.inclination = 98.7 self.right_ascension = 120.0 self.arg_perigee = 87.0 @@ -65,10 +65,10 @@ def test_orbit_elements_computation(): assert -np.pi <= orbit.right_ascension_lon <= np.pi -def test_zero_excentricity(): +def test_zero_eccentricity(): """Test perigee calculation with zero eccentricity.""" tle = MockTLE() - tle.excentricity = 0.0 + tle.eccentricity = 0.0 orbit = OrbitElements(tle) # Perigee == Apogee altitude when e=0 assert orbit.perigee == pytest.approx( @@ -134,7 +134,7 @@ def test_apogee_computation(): tle = MockTLE() orbit = OrbitElements(tle) expected_apogee = ( - (orbit.semi_major_axis * (1 + orbit.excentricity)) / AE - AE + (orbit.semi_major_axis * (1 + orbit.eccentricity)) / AE - AE ) * XKMPER assert orbit.apogee == pytest.approx(expected_apogee, abs=1e-3) @@ -178,7 +178,7 @@ def test_velocity_at_apogee_accuracy(): def test_position_vector_in_orbital_plane_perigee_accuracy(): """Test position vector at perigee (Mean Anomaly = 0°).""" tle = MockTLE() - tle.excentricity = 0.001 + tle.eccentricity = 0.001 tle.mean_anomaly = 0.0 orbit = OrbitElements(tle) pos = orbit.position_vector_in_orbital_plane() @@ -208,16 +208,16 @@ def test_position_vector_in_orbital_plane_varied(mean_anomaly_deg, expected_radi @pytest.mark.parametrize( - ("excentricity", "expected"), + ("eccentricity", "expected"), [ (0.0005, True), (0.01, False), ], ) -def test_is_circular_property(excentricity, expected): +def test_is_circular_property(eccentricity, expected): """Test circular orbit detection based on eccentricity.""" tle = MockTLE() - tle.excentricity = excentricity + tle.eccentricity = eccentricity orbit = OrbitElements(tle) assert orbit.is_circular == expected @@ -237,22 +237,22 @@ def test_is_retrograde_property(inclination_deg, expected): assert orbit.is_retrograde == expected -@pytest.mark.parametrize("excentricity", [0.001, 0.01, 0.1, 0.5]) -def test_velocity_perigee_greater_than_apogee(excentricity): +@pytest.mark.parametrize("eccentricity", [0.001, 0.01, 0.1, 0.5]) +def test_velocity_perigee_greater_than_apogee(eccentricity): """Ensure velocity at perigee is greater than at apogee for elliptical orbits.""" tle = MockTLE() - tle.excentricity = excentricity + tle.eccentricity = eccentricity orbit = OrbitElements(tle) v_perigee = orbit.velocity_at_perigee() v_apogee = orbit.velocity_at_apogee() assert v_perigee > v_apogee -@pytest.mark.parametrize("excentricity", [0.0, ECC_EPS / 10, ECC_EPS]) -def test_velocity_equal_for_circular_orbits(excentricity): +@pytest.mark.parametrize("eccentricity", [0.0, ECC_EPS / 10, ECC_EPS]) +def test_velocity_equal_for_circular_orbits(eccentricity): """Ensure velocity at perigee equals velocity at apogee for nearly circular orbits.""" tle = MockTLE() - tle.excentricity = excentricity + tle.eccentricity = eccentricity orbit = OrbitElements(tle) v_perigee = orbit.velocity_at_perigee() v_apogee = orbit.velocity_at_apogee() @@ -262,7 +262,7 @@ def test_velocity_equal_for_circular_orbits(excentricity): def test_high_eccentricity_limit(): """Test behavior near the upper eccentricity limit.""" tle = MockTLE() - tle.excentricity = ECC_LIMIT_HIGH + tle.eccentricity = ECC_LIMIT_HIGH orbit = OrbitElements(tle) assert orbit.semi_major_axis > 0 assert orbit.velocity_at_perigee() > orbit.velocity_at_apogee() @@ -281,7 +281,7 @@ def __init__(self): super().__init__() """Initialize reference TLE values for SGP4 unnormalization test.""" self.inclination = 98.7408 # degrees - self.excentricity = 0.001140 + self.eccentricity = 0.001140 self.mean_motion = 14.28634842 # rev/day self.epoch = datetime.datetime(2200, 1, 1, 0, 0, 0) diff --git a/pyorbital/tests/test_tlefile.py b/pyorbital/tests/test_tlefile.py index 0a94e04..2a118d0 100644 --- a/pyorbital/tests/test_tlefile.py +++ b/pyorbital/tests/test_tlefile.py @@ -437,7 +437,7 @@ def check_example(self, tle): # line 2 assert tle.inclination == 51.6416 assert tle.right_ascension == 247.4627 - assert tle.excentricity == 0.0006703 + assert tle.eccentricity == 0.0006703 assert tle.arg_perigee == 130.536 assert tle.mean_anomaly == 325.0288 assert tle.mean_motion == 15.72125391 @@ -877,6 +877,6 @@ def test_tle_instance_printing(): tle = Tle("ISS", line1=LINE1, line2=LINE2) - expected = "{'arg_perigee': 130.536,\n 'bstar': -1.1606e-05,\n 'classification': 'U',\n 'element_number': 292,\n 'ephemeris_type': 0,\n 'epoch': np.datetime64('2008-09-20T12:25:40.104192'),\n 'epoch_day': 264.51782528,\n 'epoch_year': '08',\n 'excentricity': 0.0006703,\n 'id_launch_number': '067',\n 'id_launch_piece': 'A ',\n 'id_launch_year': '98',\n 'inclination': 51.6416,\n 'mean_anomaly': 325.0288,\n 'mean_motion': 15.72125391,\n 'mean_motion_derivative': -2.182e-05,\n 'mean_motion_sec_derivative': 0.0,\n 'orbit': 56353,\n 'right_ascension': 247.4627,\n 'satnumber': '25544'}" # noqa + expected = "{'arg_perigee': 130.536,\n 'bstar': -1.1606e-05,\n 'classification': 'U',\n 'eccentricity': 0.0006703,\n 'element_number': 292,\n 'ephemeris_type': 0,\n 'epoch': np.datetime64('2008-09-20T12:25:40.104192'),\n 'epoch_day': 264.51782528,\n 'epoch_year': '08',\n 'id_launch_number': '067',\n 'id_launch_piece': 'A ',\n 'id_launch_year': '98',\n 'inclination': 51.6416,\n 'mean_anomaly': 325.0288,\n 'mean_motion': 15.72125391,\n 'mean_motion_derivative': -2.182e-05,\n 'mean_motion_sec_derivative': 0.0,\n 'orbit': 56353,\n 'right_ascension': 247.4627,\n 'satnumber': '25544'}" # noqa assert str(tle) == expected diff --git a/pyorbital/tlefile.py b/pyorbital/tlefile.py index 9b36f3a..2de7ef3 100644 --- a/pyorbital/tlefile.py +++ b/pyorbital/tlefile.py @@ -190,7 +190,7 @@ def __init__(self, platform, tle_file=None, line1=None, line2=None): self.element_number = None self.inclination = None self.right_ascension = None - self.excentricity = None + self.eccentricity = None self.arg_perigee = None self.mean_anomaly = None self.mean_motion = None @@ -200,6 +200,12 @@ def __init__(self, platform, tle_file=None, line1=None, line2=None): self._checksum() self._parse_tle() + @property + def excentricity(self): + """Get 'eccentricity' using legacy 'excentricity' name.""" + warnings.warn("The 'eccentricity' property is deprecated in favor of 'eccentricity'", stacklevel=2) + return self.eccentricity + @property def line1(self): """Return first TLE line.""" @@ -275,7 +281,7 @@ def _read_tle_decimal(rep): self.inclination = float(self._line2[8:16]) self.right_ascension = float(self._line2[17:25]) - self.excentricity = int(self._line2[26:33]) * 10 ** -7 + self.eccentricity = int(self._line2[26:33]) * 10 ** -7 self.arg_perigee = float(self._line2[34:42]) self.mean_anomaly = float(self._line2[43:51]) self.mean_motion = float(self._line2[52:63]) @@ -300,7 +306,7 @@ def to_dict(self): "element_number": self.element_number, "inclination": self.inclination, "right_ascension": self.right_ascension, - "excentricity": self.excentricity, + "eccentricity": self.eccentricity, "arg_perigee": self.arg_perigee, "mean_anomaly": self.mean_anomaly, "mean_motion": self.mean_motion,