Skip to content

Commit

Permalink
Add check_satrec() and document parameter ranges
Browse files Browse the repository at this point in the history
  • Loading branch information
brandon-rhodes committed Feb 13, 2025
1 parent 85a4c88 commit 7845f7b
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 27 deletions.
38 changes: 26 additions & 12 deletions sgp4/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,17 +406,26 @@
... 0.0, # ndot: ballistic coefficient (radians/minute^2)
... 0.0, # nddot: mean motion 2nd derivative (radians/minute^3)
... 0.0007417, # ecco: eccentricity
... 0.3083420829620822, # argpo: argument of perigee (radians)
... 0.9013560935706996, # inclo: inclination (radians)
... 1.4946964807494398, # mo: mean anomaly (radians)
... 0.3083420829620822, # argpo: argument of perigee (radians 0..2pi)
... 0.9013560935706996, # inclo: inclination (radians 0..pi)
... 1.4946964807494398, # mo: mean anomaly (radians 0..2pi)
... 0.06763602333248933, # no_kozai: mean motion (radians/minute)
... 3.686137125541276, # nodeo: R.A. of ascending node (radians)
... 3.686137125541276, # nodeo: R.A. of ascending node (radians 0..2pi)
... )
These numbers don’t look the same as the numbers in the TLE, because the
underlying ``sgp4init()`` routine uses different units: radians rather
than degrees. But this is the same orbit and will produce the same
positions.
You might notice that these numbers don’t look the same as the numbers
in the TLE above. For example, the inclination was 51.6439 in the TLE,
but is 0.901356 here. That’s because ``sgp4init()`` uses different
units than the TLE format: angles are in radians rather than degrees.
But this is the same orbit and will produce the same positions.
If you want to double-check that your elements are valid, you can run
the function ``check_satrec()``, which will raise a ``ValueError`` with
an informative error message if any of the angles are out of bounds. If
the satellite is fine, it simply returns, without raising an exception:
>>> from sgp4.conveniences import check_satrec
>>> check_satrec(satellite2)
Note that ``ndot`` and ``nddot`` are ignored by the SGP4 propagator, so
you can leave them ``0.0`` without any effect on the resulting satellite
Expand Down Expand Up @@ -472,11 +481,11 @@
| ``nddot`` — Second time derivative of the mean motion
(loaded from the TLE, but otherwise ignored).
| ``bstar`` — Ballistic drag coefficient B* (1/earth radii).
| ``inclo`` — Inclination (radians).
| ``nodeo`` — Right ascension of ascending node (radians).
| ``inclo`` — Inclination (radians 0 ≤ i < pi).
| ``nodeo`` — Right ascension of ascending node (radians 0 ≤ Ω < 2pi).
| ``ecco`` — Eccentricity.
| ``argpo`` — Argument of perigee (radians).
| ``mo`` — Mean anomaly (radians).
| ``argpo`` — Argument of perigee (radians 0 ≤ ω < 2pi).
| ``mo`` — Mean anomaly (radians 0 ≤ M < 2pi).
| ``no_kozai`` — Mean motion (radians/minute).
| ``no`` — Alias for ``no_kozai``, for compatibility with old code.
Expand Down Expand Up @@ -634,6 +643,11 @@
Unreleased — 2.24
* The documentation now specifies the acceptable range for orbital
element angles like inclination and mean anomaly, and a new function
``check_satrec(sat)`` will tell the caller if any of the angles are
out of bounds.
* Tweaked the fallback Python code to accept TLE lines without a final
checksum character in the 69th column, to match the C++ code.
Expand Down
57 changes: 42 additions & 15 deletions sgp4/conveniences.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"""
import datetime as dt
import sgp4
from math import pi
from .functions import days2mdhms, jday

class _UTC(dt.tzinfo):
Expand Down Expand Up @@ -73,25 +74,51 @@ def sat_epoch_datetime(sat):
micro = int(fraction * 1e6)
return dt.datetime(year, month, day, hour, minute, second, micro, UTC)

_ATTRIBUTES = None
_ATTRIBUTES = []
_ATTR_MAXES = {}
_MAX_VALUES = {'2pi': 2*pi, 'pi': pi}

def _load_attributes():
for line in sgp4.__doc__.splitlines():
if line.endswith('*'):
title = line.strip('*')
_ATTRIBUTES.append(title)
elif line.startswith('| ``'):
pieces = line.split('``')
name = pieces[1]
_ATTRIBUTES.append(name)
i = 2
while pieces[i] == ', ':
another_name = pieces[i+1]
_ATTRIBUTES.append(another_name)
i += 2
if '<' in line:
_ATTR_MAXES[name] = '2pi' if ('2pi' in line) else 'pi'

def check_satrec(sat):
"""Check whether satellite orbital elements are within range."""

if not _ATTRIBUTES:
_load_attributes()

e = []

for name, max_name in sorted(_ATTR_MAXES.items()):
value = getattr(sat, name)
if 0.0 <= value < _MAX_VALUES[max_name]:
continue
e.append(' {0} = {1:f} is outside the range 0 <= {0} < {2}\n'
.format(name, value, max_name))

if e:
raise ValueError('satellite parameters out of range:\n' + '\n'.join(e))


def dump_satrec(sat, sat2=None):
"""Yield lines that list the attributes of one or two satellites."""

global _ATTRIBUTES
if _ATTRIBUTES is None:
_ATTRIBUTES = []
for line in sgp4.__doc__.splitlines():
if line.endswith('*'):
title = line.strip('*')
_ATTRIBUTES.append(title)
elif line.startswith('| ``'):
pieces = line.split('``')
_ATTRIBUTES.append(pieces[1])
i = 2
while pieces[i] == ', ':
_ATTRIBUTES.append(pieces[i+1])
i += 2
if not _ATTRIBUTES:
_load_attributes()

for item in _ATTRIBUTES:
if item[0].isupper():
Expand Down
10 changes: 10 additions & 0 deletions sgp4/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,16 @@ def test_sat_epoch_datetime():
zone = conveniences.UTC
assertEqual(datetime, dt.datetime(2000, 6, 27, 18, 50, 19, 733568, zone))

def test_check_satrec():
sat = Satrec.twoline2rv(LINE1, LINE2)
conveniences.check_satrec(sat) # should succeed with no exception

sat = Satrec.twoline2rv(LINE1, LINE2.replace('34.2682', '-4.2682'))
msg = ('satellite parameters out of range:\n'
' inclo = -0.074494 is outside the range 0 <= inclo < pi')
with assertRaisesRegex(ValueError, msg):
conveniences.check_satrec(sat)

def test_good_tle_checksum():
for line in LINE1, LINE2:
checksum = int(line[-1])
Expand Down

0 comments on commit 7845f7b

Please sign in to comment.