- Joined
- Aug 5, 2011
- Messages
- 1,667
- Reaction score
- 3
- Points
- 38
Dropping in here after quite a long time away. I have been looking at StarLink orbits on www.space-track.org, using Python 3.x.
Here's my SLTrack.py:
Sample output (of thousands of lines):
Tested under Windows 10, VS Code, and Python 3.x.
Ask me anything!
Here's my SLTrack.py:
Code:
##
## SLTrack.py
## (c) 2019 Andrew Stokes All Rights Reserved
##
##
## Simple Python app to extract Starlink satellite history data from www.space-track.org into a spreadsheet
## (Note action for you in the code below, to set up a config file with your access and output details)
##
##
## Copyright Notice:
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## For full licencing terms, pleaserefer to the GNU General Public License
## (gpl-3_0.txt) distributed with this release, or see
## http://www.gnu.org/licenses/.
##
import requests
import json
import configparser
import xlsxwriter
import time
from datetime import datetime
class MyError(Exception):
def __init___(self,args):
Exception.__init__(self,"my exception was raised with arguments {0}".format(args))
self.args = args
# See https://www.space-track.org/documentation for details on REST queries
# the "Find Starlinks" query searches all satellites with NORAD_CAT_ID > 40000, with OBJECT_NAME matching STARLINK*, 1 line per sat
# the "OMM Starlink" query gets all Orbital Mean-Elements Messages (OMM) for a specific NORAD_CAT_ID in JSON format
uriBase = "https://www.space-track.org"
requestLogin = "/ajaxauth/login"
requestCmdAction = "/basicspacedata/query"
requestFindStarlinks = "/class/tle_latest/NORAD_CAT_ID/>40000/ORDINAL/1/OBJECT_NAME/STARLINK~~/format/json/orderby/NORAD_CAT_ID%20asc"
requestOMMStarlink1 = "/class/omm/NORAD_CAT_ID/"
requestOMMStarlink2 = "/orderby/EPOCH%20asc/format/json"
# Parameters to derive apoapsis and periapsis from mean motion (see https://en.wikipedia.org/wiki/Mean_motion)
GM = 398600441800000.0
GM13 = GM ** (1.0/3.0)
MRAD = 6378.137
PI = 3.14159265358979
TPI86 = 2.0 * PI / 86400.0
# ACTION REQUIRED FOR YOU:
#=========================
# Provide a config file in the same directory as this file, called SLTrack.ini, with this format (without the # signs)
# [configuration]
# username = XXX
# password = YYY
# output = ZZZ
#
# ... where XXX and YYY are your www.space-track.org credentials (https://www.space-track.org/auth/createAccount for free account)
# ... and ZZZ is your Excel Output file - e.g. starlink-track.xlsx (note: make it an .xlsx file)
# Use configparser package to pull in the ini file (pip install configparser)
config = configparser.ConfigParser()
config.read("./SLTrack.ini")
configUsr = config.get("configuration","username")
configPwd = config.get("configuration","password")
configOut = config.get("configuration","output")
siteCred = {'identity': configUsr, 'password': configPwd}
# User xlsxwriter package to write the xlsx file (pip install xlsxwriter)
workbook = xlsxwriter.Workbook(configOut)
worksheet = workbook.add_worksheet()
z0_format = workbook.add_format({'num_format': '#,##0'})
z1_format = workbook.add_format({'num_format': '#,##0.0'})
z2_format = workbook.add_format({'num_format': '#,##0.00'})
z3_format = workbook.add_format({'num_format': '#,##0.000'})
# write the headers on the spreadsheet
now = datetime.now()
nowStr = now.strftime("%m/%d/%Y %H:%M:%S")
worksheet.write('A1', 'Starlink data from' + uriBase + " on " + nowStr)
worksheet.write('A3','NORAD_CAT_ID')
worksheet.write('B3','SATNAME')
worksheet.write('C3','EPOCH')
worksheet.write('D3','Orb')
worksheet.write('E3','Inc')
worksheet.write('F3','Ecc')
worksheet.write('G3','MnM')
worksheet.write('H3','ApA')
worksheet.write('I3','PeA')
worksheet.write('J3','AvA')
worksheet.write('K3','LAN')
worksheet.write('L3','AgP')
worksheet.write('M3','MnA')
worksheet.write('N3','SMa')
worksheet.write('O3','T')
worksheet.write('P3','Vel')
wsline = 3
# use requests package to drive the RESTful session with space-track.org
with requests.Session() as session:
# run the session in a with block to force session to close if we exit
# need to log in first. note that we get a 200 to say the web site got the data, not that we are logged in
resp = session.post(uriBase + requestLogin, data = siteCred)
if resp.status_code != 200:
raise MyError(resp, "POST fail on login")
# this query picks up all Starlink satellites from the catalog. Note - a 401 failure shows you have bad credentials
resp = session.get(uriBase + requestCmdAction + requestFindStarlinks)
if resp.status_code != 200:
print(resp)
raise MyError(resp, "GET fail on request for Starlink satellites")
# use the json package to break the json formatted response text into a Python structure (a list of dictionaries)
retData = json.loads(resp.text)
satCount = len(retData)
satIds = []
for e in retData:
# each e describes the latest elements for one Starlink satellite. We just need the NORAD_CAT_ID
catId = e['NORAD_CAT_ID']
satIds.append(catId)
# using our new list of Starlink satellite NORAD_CAT_IDs, we can now get the OMM message
maxs = 1
for s in satIds:
resp = session.get(uriBase + requestCmdAction + requestOMMStarlink1 + s + requestOMMStarlink2)
if resp.status_code != 200:
# If you are getting error 500's here, its probably the rate throttle on the site (20/min and 200/hr)
# wait a while and retry
print(resp)
raise MyError(resp, "GET fail on request for Starlink satellite " + s)
# the data here can be quite large, as it's all the elements for every entry for one Starlink satellite
retData = json.loads(resp.text)
for e in retData:
# each element is one reading of the orbital elements for one Starlink
print("Scanning satellite " + e['OBJECT_NAME'] + " at epoch " + e['EPOCH'])
mmoti = float(e['MEAN_MOTION'])
ecc = float(e['ECCENTRICITY'])
worksheet.write(wsline, 0, int(e['NORAD_CAT_ID']))
worksheet.write(wsline, 1, e['OBJECT_NAME'])
worksheet.write(wsline, 2, e['EPOCH'])
worksheet.write(wsline, 3, float(e['REV_AT_EPOCH']))
worksheet.write(wsline, 4, float(e['INCLINATION']),z1_format)
worksheet.write(wsline, 5, ecc,z3_format)
worksheet.write(wsline, 6, mmoti,z1_format)
# do some ninja-fu to flip Mean Motion into Apoapsis and Periapsis, and to get the orbital period and velocity
sma = GM13 / ((TPI86 * mmoti) ** (2.0 / 3.0)) / 1000.0
apo = sma * (1.0 + ecc) - MRAD
per = sma * (1.0 - ecc) - MRAD
smak = sma * 1000.0
orbT = 2.0 * PI * ((smak ** 3.0) / GM) ** (0.5)
orbV = (GM / smak) ** (0.5)
worksheet.write(wsline, 7, apo,z1_format)
worksheet.write(wsline, 8, per,z1_format)
worksheet.write(wsline, 9, (apo+per)/2.0,z1_format)
worksheet.write(wsline, 10, float(e['RA_OF_ASC_NODE']),z1_format)
worksheet.write(wsline, 11, float(e['ARG_OF_PERICENTER']),z1_format)
worksheet.write(wsline, 12, float(e['MEAN_ANOMALY']),z1_format)
worksheet.write(wsline, 13, sma,z1_format)
worksheet.write(wsline, 14, orbT,z0_format)
worksheet.write(wsline, 15, orbV,z0_format)
wsline = wsline + 1
maxs = maxs + 1
if maxs > 18:
print("Snoozing for 60 secs for rate limit reasons (max 20/min and 200/hr)...")
time.sleep(60)
maxs = 1
session.close()
workbook.close()
print("Completed session")
Sample output (of thousands of lines):
Code:
Starlink data fromhttps://www.space-track.org on 11/26/2019 20:59:55
NORAD_CAT_ID SATNAME EPOCH Orb Inc Ecc MnM ApA PeA AvA LAN AgP MnA SMa T Vel
44235 STARLINK-31 2019-05-25T17:16:34 147 53.0 0.001 15.4 442.5 433.0 437.7 167.6 280.5 79.6 6,815.8 5,600 7,647
44235 STARLINK-31 2019-05-26T05:42:53 155 53.0 0.001 15.4 442.5 433.8 438.2 165.2 272.2 87.9 6,816.3 5,601 7,647
44235 STARLINK-31 2019-05-26T21:46:33 166 53.0 0.000 15.4 445.2 443.4 444.3 162.0 179.5 292.5 6,822.4 5,608 7,644
44235 STARLINK-31 2019-05-27T13:22:30 149 53.0 0.000 15.4 445.2 443.4 444.3 158.9 181.9 297.0 6,822.4 5,608 7,644
44235 STARLINK-31 2019-05-29T17:52:01 210 53.0 0.000 15.4 458.8 452.8 455.8 148.7 27.1 333.1 6,833.9 5,622 7,637
44235 STARLINK-31 2019-05-30T20:24:21 227 53.0 0.000 15.4 460.3 457.7 459.0 143.5 57.1 303.0 6,837.1 5,626 7,635
44235 STARLINK-31 2019-06-02T21:56:09 136 53.0 0.000 15.3 475.3 472.7 474.0 129.2 87.4 272.7 6,852.1 5,645 7,627
44235 STARLINK-31 2019-06-02T21:56:09 274 53.0 0.000 15.3 475.3 472.7 474.0 129.2 87.4 272.7 6,852.1 5,645 7,627
44235 STARLINK-31 2019-06-05T17:39:01 317 53.0 0.000 15.3 489.9 487.3 488.6 116.1 85.7 324.1 6,866.8 5,663 7,619
44235 STARLINK-31 2019-06-05T23:51:19 130 53.0 0.000 15.3 491.3 488.7 490.0 115.0 92.2 298.2 6,868.1 5,665 7,618
44235 STARLINK-31 2019-06-06T21:44:58 334 53.0 0.001 15.2 498.3 491.1 494.7 110.7 123.1 237.1 6,872.8 5,670 7,616
44235 STARLINK-31 2019-06-07T08:46:35 342 53.0 0.000 15.2 500.3 496.7 498.5 108.6 107.9 252.2 6,876.6 5,675 7,613
44235 STARLINK-31 2019-06-08T02:06:36 353 53.0 0.000 15.2 504.6 500.2 502.4 105.3 116.8 243.3 6,880.5 5,680 7,611
44235 STARLINK-31 2019-06-08T08:24:38 357 53.0 0.000 15.2 502.8 499.8 501.3 104.1 136.2 223.9 6,879.4 5,679 7,612
44235 STARLINK-31 2019-06-08T21:02:05 365 53.0 0.000 15.2 508.4 504.8 506.6 101.7 152.3 207.8 6,884.8 5,685 7,609
44235 STARLINK-31 2019-06-09T04:55:29 370 53.0 0.000 15.2 510.2 506.4 508.3 100.2 62.3 297.8 6,886.4 5,687 7,608
44235 STARLINK-31 2019-06-09T09:39:34 373 53.0 0.000 15.2 510.5 507.9 509.2 99.3 76.6 283.5 6,887.4 5,688 7,608
44235 STARLINK-31 2019-06-09T20:42:42 380 53.0 0.000 15.2 513.0 509.1 511.0 97.2 121.5 238.6 6,889.2 5,691 7,607
44235 STARLINK-31 2019-06-10T04:36:40 385 53.0 0.000 15.2 514.1 509.8 512.0 95.7 225.8 134.2 6,890.1 5,692 7,606
44235 STARLINK-31 2019-06-10T09:21:26 388 53.0 0.000 15.2 516.5 512.6 514.6 94.8 145.7 214.4 6,892.7 5,695 7,605
44235 STARLINK-31 2019-06-11T04:20:14 400 53.0 0.000 15.1 523.7 519.2 521.4 91.2 205.3 154.7 6,899.6 5,704 7,601
Tested under Windows 10, VS Code, and Python 3.x.
Ask me anything!