Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is there a way to get the TLE data of a satellite by ID, not by name? #167

Closed
tmamedzadeh opened this issue Mar 29, 2018 · 8 comments
Closed
Assignees

Comments

@tmamedzadeh
Copy link

There is a code, describing the retrieval of satellite data from the TLE:

stations_url = 'http://celestrak.com/NORAD/elements/stations.txt'
satellites = load.tle(stations_url)
satellite = satellites['ISS (ZARYA)']

However, the names sometimes are changed, but ID is constant. Is there a way to get the data by ID, something like satellites[40053]?
For now, my solution is just looping through the lines.

@tmamedzadeh
Copy link
Author

Solved, loading the TLE directly from URL celestrak.com/cgi-bin/TLE.pl?CATNR=40052 and parsing achieved 3 lines.

Closing this issue, however this functionality would be useful.

@brandon-rhodes brandon-rhodes self-assigned this Apr 11, 2018
@brandon-rhodes
Copy link
Member

I didn't know that names changed! I think this is a good idea, and I've re-opened this issue so that I remember to add that feature (or so that someone else can).

@murison
Copy link

murison commented Apr 19, 2018

FWIW, another user finds it essential to be able to do satellites[cat Id #] instead of satellites[name]. I know you have a gazillion things to do, but do know this is not just a "gee, it'd be nice if..." need for some of us.

@brandon-rhodes
Copy link
Member

brandon-rhodes commented Apr 19, 2018

If someone needs this quickly, I believe they can build their own dictionary with something like:

satcat = {sat.model.satnum: sat for sat in satellites.values()}

Then they could say satcat[...cat Id #...] — if someone could test this out and see if it works, a follow-up comment letting other users know will doubtless be appreciated!

@murison
Copy link

murison commented Apr 20, 2018

Oh, duh! Good idea. This, for example, works:

geosats = loader.tle(celestrak+'geo.txt')
[...]
satcats = [geosats, gpssats, sciencesats, stationsats, tdrss, visualsats]
sats = {}
for cat in satcats:
    for name in cat.keys():
        sat = cat[name]
        sats.update( {sat.model.satnum:sat, name:sat} )

Then one can access either way:

sats['GOES 16']
sats[41866]

@brandon-rhodes
Copy link
Member

brandon-rhodes commented Apr 20, 2018

Very nice! You might find it slightly more efficient to:

sats[sat.model.satnum] = sat
sats[name] = sat

instead of building and then disposing of an entire new dictionary — but, honestly, you'll never notice the difference since it's plenty fast either way for the number of satellites involved. :)

@murison
Copy link

murison commented Apr 20, 2018

For completeness, and in case someone else might find it useful at some point:

import urllib
from skyfield import api as sf
datadir = os.environ['HOME']+'/programming/python/astro/skyfield-data/'
loader = sf.Loader(datadir, expire=False)
# Satellite TLEs.
celestrak = 'http://celestrak.com/NORAD/elements/'
geosats = loader.tle(celestrak+'geo.txt')
gpssats = loader.tle(celestrak+'supplemental/gps.txt')
sciencesats = loader.tle(celestrak+'science.txt')
stationsats = loader.tle(celestrak+'stations.txt')
tdrss = loader.tle(celestrak+'tdrss.txt')
visualsats = loader.tle(celestrak+'visual.txt')
# Make catalogs indexable by either name or catalog number.
# Also create sats, a merge of the individual ones.
satcats = [geosats, gpssats, sciencesats, stationsats, tdrss, visualsats]
sats = {}
for cat in satcats:
    names = [key for key in cat.keys()]
    for name in names:
        sat = cat[name]
        satnum = sat.model.satnum
        cat[satnum]  = sat
        sats[satnum] = sat
        sats[name]   = sat
def getsat(satid):
    """
    Return a skyfield EarthSatellite.
    
    <satid> is case independent if it is a satellite name (str).
    
    Retrieve directly from CelesTrak by catalog number if not in local database.
    """
    if isinstance(satid, str):
        satid = satid.upper()
    if satid in sats.keys():
        return sats[satid]
    if not isinstance(satid, int):
        msg = 'satid must be an integer for satellites not in the local set'
        raise Exception(msg)
    base = 'http://celestrak.com/cgi-bin/TLE.pl?CATNR='
    url = base + str(satid)
    with urllib.request.urlopen(url) as fd:
        lines = fd.readlines()
    for k, line in enumerate(lines):
        if 'PRE' in line.decode():
            name = lines[k+1].decode().strip()
            if name == 'No TLE found':
                msg = '%i is not in the CelesTrak database!' % satid
                raise Exception(msg)
            tle1 = lines[k+2].decode().strip()
            tle2 = lines[k+3].decode().strip()
            break
    sat = sf.EarthSatellite(tle1, tle2, name)
    return sat

>> sat = getsat(28129)
...print(sat)
...sat = getsat('goes 16')
...print(sat)

EarthSatellite 'GPS BIIR-10 (PRN 22)' number=28129 epoch=2018-04-21T19:50:06Z
EarthSatellite 'GOES 16' number=41866 epoch=2018-04-18T15:14:01Z

@blast-inc
Copy link

For completeness, and in case someone else might find it useful at some point:

import urllib
from skyfield import api as sf
datadir = os.environ['HOME']+'/programming/python/astro/skyfield-data/'
loader = sf.Loader(datadir, expire=False)
# Satellite TLEs.
celestrak = 'http://celestrak.com/NORAD/elements/'
geosats = loader.tle(celestrak+'geo.txt')
gpssats = loader.tle(celestrak+'supplemental/gps.txt')
sciencesats = loader.tle(celestrak+'science.txt')
stationsats = loader.tle(celestrak+'stations.txt')
tdrss = loader.tle(celestrak+'tdrss.txt')
visualsats = loader.tle(celestrak+'visual.txt')
# Make catalogs indexable by either name or catalog number.
# Also create sats, a merge of the individual ones.
satcats = [geosats, gpssats, sciencesats, stationsats, tdrss, visualsats]
sats = {}
for cat in satcats:
    names = [key for key in cat.keys()]
    for name in names:
        sat = cat[name]
        satnum = sat.model.satnum
        cat[satnum]  = sat
        sats[satnum] = sat
        sats[name]   = sat
def getsat(satid):
    """
    Return a skyfield EarthSatellite.
    
    <satid> is case independent if it is a satellite name (str).
    
    Retrieve directly from CelesTrak by catalog number if not in local database.
    """
    if isinstance(satid, str):
        satid = satid.upper()
    if satid in sats.keys():
        return sats[satid]
    if not isinstance(satid, int):
        msg = 'satid must be an integer for satellites not in the local set'
        raise Exception(msg)
    base = 'http://celestrak.com/cgi-bin/TLE.pl?CATNR='
    url = base + str(satid)
    with urllib.request.urlopen(url) as fd:
        lines = fd.readlines()
    for k, line in enumerate(lines):
        if 'PRE' in line.decode():
            name = lines[k+1].decode().strip()
            if name == 'No TLE found':
                msg = '%i is not in the CelesTrak database!' % satid
                raise Exception(msg)
            tle1 = lines[k+2].decode().strip()
            tle2 = lines[k+3].decode().strip()
            break
    sat = sf.EarthSatellite(tle1, tle2, name)
    return sat

>> sat = getsat(28129)
...print(sat)
...sat = getsat('goes 16')
...print(sat)

EarthSatellite 'GPS BIIR-10 (PRN 22)' number=28129 epoch=2018-04-21T19:50:06Z
EarthSatellite 'GOES 16' number=41866 epoch=2018-04-18T15:14:01Z

thanks a lot for this code.
I am getting a strange error when i call the function with your example:

sat = getsat(28129)
...print(sat)

line 66, in getsat
sat = sf.EarthSatellite(tle1, tle2, name)

UnboundLocalError: local variable 'tle1' referenced before assignment

???

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants