Monday, January 27, 2014

A Simple TLE Aggregator for Amateur Satellite Enthusiasts

Tonight I threw together a small two line element aggregator.  This is targeted for amateur satellite enthusiasts that are tired of downloading keplerian elements and post-filtering them.  This tool can be used to aggregate TLEs from multiple sources into a single file named 'all.tle'.

The first thing I had to decide was the config file format.  To decide this, I pinned down what the user currently does, which includes:
  • Read keps from multiple files
  • Read keps from multiple URLs
  • Dump them into a file somewhere
Our config file should allow us to specify a source (filename or url) and the names of the satellites whose keplerian elements should be derived from it.  

[downloaded.tles]
sats = AO-07, AO-27, AO-51

[http://www.amsat.org/amsat/ftp/keps/current/nasabare.txt]
sats = CP3, CO-65

This format will allow us to trivially parse the config file using Pythons configparser.  Now, we need to do something with this config file.  Let's start by reading each of the TLEs from the files and URLs.

# read the configuration file sections
for section in config.sections():

        print '[info] processing section: %s' % (section)

        if os.path.exists(section):
                print '[info] found keps file on disk, reading: %s' % (section)
                section_keps = open(section).readlines()
        else:
                print '[info] downloading keps from: %s' % (section)
                section_keps  = urllib2.urlopen(section).readlines()

        if not config.has_option(section, 'sats'):
                print '[error] section is missing sats listing'

        sats = config.get(section, 'sats').split(',')
        print '[info] reading sats: %s' % (sats)
        sats = [item.strip().lower() for item in sats]

        section_keps = [line.strip() for line in section_keps]
        section_keps = [ [section_keps[i], section_keps[i+1], section_keps[i+2]] for i in xrange(0, len(section_keps), 3)]

        print '[info] read %d keps from source' % (len(section_keps))

        keps[section] = [sats, section_keps]

We end up with a keps dictionary that is keyed off by each section name.  Each record of the dictionary points to a list containing the satellite names and keps for each individual section.  Next, we will obtain the keps of interest and sort them.

# grab the keps of interest
all_keps = {}
for source in keps:
        print 'processing source: %s' % (source)

        section = keps[source]

        section_sats = section[0]
        section_keps = section[1]
        for kep in section_keps:
        if kep[0].lower() in section_sats:
               all_keps[kep[0]] = [kep[0], kep[1], kep[2]]

# sort all the keps by satellite name
aggregate = ''
sorted_keys = all_keps.keys()
sorted_keys.sort()
for key in sorted_keys:
        kep = all_keps[key]
        aggregate += '%s\n%s\n%s\n' % (kep[0], kep[1], kep[2])
open('all.tle','w').write(aggregate)

In the end, we end up with a simple sorted list with the keps of interest:

AO-07
1 07530U 74089B   14023.21186782 -.00000018  00000-0  15670-3 0  8786
2 07530 101.4563 009.9104 0011721 216.9898 211.0530 12.53601524793353
AO-27
1 22825U 93061C   14022.83041429  .00000120  00000-0  64147-4 0  5791
2 22825 098.6462 329.8231 0008379 012.2119 040.8747 14.29644643 60017
AO-51
1 28375U 04025K   14023.12989737  .00000268  00000-0  92403-4 0  7549
2 28375 098.2335 335.1283 0084384 071.3658 355.3917 14.41252761503014
CO-65
1 32785U 08021C   14022.76465391  .00001360  00000-0  16702-3 0  8501
2 32785 097.7213 078.7425 0012770 309.4854 170.8434 14.84591857310440
CP3
1 31129U 07012N   14023.21383341  .00001013  00000-0  23392-3 0  6568
2 31129 097.9188 008.7173 0100785 102.4710 323.4198 14.54150988358968

You are welcome to check out the code here.

No comments: