2019-02-25 11:24:46 -07:00
from datetime import datetime , timedelta
2019-02-20 13:00:22 -07:00
import requests
from tqdm import tqdm
2019-03-13 10:50:50 -06:00
from flask import Flask , render_template , redirect , url_for , request
2019-02-20 13:00:22 -07:00
import json
2019-03-13 10:50:50 -06:00
from collections import defaultdict , Counter
2019-02-20 13:00:22 -07:00
import random
from apscheduler . schedulers . background import BackgroundScheduler
2019-02-25 11:24:46 -07:00
from satnogs_api_client import fetch_satellites
from satnogs_api_client import DB_BASE_URL , get_paginated_endpoint
2019-02-20 13:00:22 -07:00
from satellite_tle import fetch_tles
2019-02-25 11:24:46 -07:00
from skyfield . api import EarthSatellite , utc , load
2019-02-20 13:00:22 -07:00
scheduler = BackgroundScheduler ( )
app = Flask ( __name__ )
2019-02-25 11:24:46 -07:00
ts = load . timescale ( )
2019-02-20 13:00:22 -07:00
broken = defaultdict ( set )
2019-03-13 10:50:50 -06:00
Usage = Counter ( )
2019-02-20 13:00:22 -07:00
Sats = defaultdict ( list )
Passes = defaultdict ( list )
Stations = [ ]
TLEs = defaultdict ( list )
Transmitters = defaultdict ( dict )
2019-02-25 10:25:57 -07:00
StationsPasses = defaultdict ( list )
2019-02-25 08:19:57 -07:00
SatDescrip = defaultdict ( str )
2019-02-20 13:00:22 -07:00
CZML = [ ]
2019-02-25 11:24:46 -07:00
2019-02-20 13:00:22 -07:00
def getFuture ( ) :
print " Getting future Passes "
global Sats
global TLEs
2019-02-25 11:24:46 -07:00
observations = defaultdict ( dict )
2019-02-20 13:00:22 -07:00
start = datetime . utcnow ( ) . strftime ( ' % Y- % m- %d T % H: % M: % S % z ' )
2019-02-25 11:24:46 -07:00
end = ( datetime . utcnow ( ) + timedelta ( hours = 4 , minutes = 30 ) )
end = end . strftime ( ' % Y- % m- %d T % H: % M: % S % z ' )
2019-02-20 13:00:22 -07:00
passes = get_paginated_endpoint ( " https://network.satnogs.org/api/jobs/ " )
obs = get_paginated_endpoint ( " https://network.satnogs.org/api/observations/?end= " + end + " &format=json&start= " + start )
for x in tqdm ( obs ) :
observations [ x [ " id " ] ] = x
Sats = defaultdict ( list )
for x in tqdm ( passes ) :
if x [ " id " ] in observations :
try :
2019-02-25 11:24:46 -07:00
start = datetime . strptime ( x [ " start " ] , ' % Y- % m- %d T % H: % M: % Sz ' )
2019-02-20 13:00:22 -07:00
start = start . replace ( tzinfo = utc )
2019-02-25 11:24:46 -07:00
end = datetime . strptime ( x [ " end " ] , ' % Y- % m- %d T % H: % M: % Sz ' )
2019-02-20 13:00:22 -07:00
end = end . replace ( tzinfo = utc )
# "transmitter":Transmitters[observations[x["id"]]["norad_cat_id"]][x["transmitter"]]
2019-02-25 11:24:46 -07:00
Sats [ observations [ x [ " id " ] ] [ " norad_cat_id " ] ] . append ( { " station " : x [ " ground_station " ] , " transmitter " : Transmitters [ observations [ x [ " id " ] ] [ " norad_cat_id " ] ] [ x [ " transmitter " ] ] , " start " : start , " end " : end , " id " : x [ " id " ] } )
TLEs [ observations [ x [ " id " ] ] [ " norad_cat_id " ] ] = [ x [ " tle0 " ] , x [ " tle1 " ] , x [ " tle2 " ] ]
StationsPasses [ x [ " ground_station " ] ] . append ( { " norad " : observations [ x [ " id " ] ] [ " norad_cat_id " ] , " transmitter " : Transmitters [ observations [ x [ " id " ] ] [ " norad_cat_id " ] ] [ x [ " transmitter " ] ] , " start " : start , " end " : end , " id " : x [ " id " ] } )
2019-02-20 13:00:22 -07:00
except Exception as e :
2019-02-25 11:24:46 -07:00
print " Error on observation number: " + str ( x [ " id " ] ) + " " + str ( e )
2019-02-20 13:00:22 -07:00
broken [ observations [ x [ " id " ] ] [ " norad_cat_id " ] ] . update ( [ x [ " transmitter " ] ] )
print str ( len ( Sats ) ) + " Future passes found. "
2019-02-25 11:24:46 -07:00
2019-02-20 13:00:22 -07:00
def GetGroundStations ( ) :
print " Getting Ground Stations "
stations = get_paginated_endpoint ( " https://network.satnogs.org/api/stations/ " )
return stations
2019-02-25 11:24:46 -07:00
2019-02-20 13:00:22 -07:00
def updateTLE ( ) :
print " Updating TLE "
global TLEs
2019-02-25 11:24:46 -07:00
sats = fetch_satellites ( None , DB_BASE_URL )
2019-02-20 13:00:22 -07:00
satnogs_db_norad_ids = set ( sat [ ' norad_cat_id ' ] for sat in sats if sat [ ' status ' ] != ' re-entered ' )
# Remove satellites with temporary norad ids
temporary_norad_ids = set ( filter ( lambda norad_id : norad_id > = 99900 , satnogs_db_norad_ids ) )
satnogs_db_norad_ids = satnogs_db_norad_ids - temporary_norad_ids
# Fetch TLEs for the satellites of interest
2019-02-25 11:24:46 -07:00
2019-02-20 13:00:22 -07:00
tles = fetch_tles ( satnogs_db_norad_ids )
TLEs = { }
for norad_id , ( source , tle ) in tqdm ( tles . items ( ) ) :
2019-02-25 11:24:46 -07:00
TLEs [ norad_id ] = [ str ( tle [ 0 ] ) , str ( tle [ 1 ] ) , str ( tle [ 2 ] ) ]
2019-02-20 13:00:22 -07:00
print ( ' \n TLEs for {} of {} requested satellites found ( {} satellites with temporary norad ids skipped). ' . format ( len ( tles ) , len ( satnogs_db_norad_ids ) , len ( temporary_norad_ids ) ) )
2019-02-25 11:24:46 -07:00
@scheduler.scheduled_job ( ' interval ' , days = 5 )
2019-02-20 13:00:22 -07:00
def updateTransmitters ( ) :
global Transmitters
print " Updating Transmitters "
temp = requests . get ( " https://db.satnogs.org/api/transmitters/ " ) . json ( )
for x in tqdm ( temp ) :
2019-02-25 11:24:46 -07:00
Transmitters [ x [ " norad_cat_id " ] ] [ x [ " uuid " ] ] = [ x [ " description " ] , [ random . randint ( 0 , 255 ) , random . randint ( 0 , 255 ) , random . randint ( 0 , 255 ) , 255 ] ]
2019-02-25 08:19:57 -07:00
for x in Transmitters . keys ( ) :
for y in Transmitters [ x ] . keys ( ) :
SatDescrip [ x ] + = ' <div class= " trans " style= " background-color:# ' + str ( " %02x " % Transmitters [ x ] [ y ] [ 1 ] [ 0 ] ) + str ( " %02x " % Transmitters [ x ] [ y ] [ 1 ] [ 1 ] ) + str ( " %02x " % Transmitters [ x ] [ y ] [ 1 ] [ 2 ] ) + ' " ;> ' + Transmitters [ x ] [ y ] [ 0 ] + ' </div> '
2019-02-25 11:24:46 -07:00
@scheduler.scheduled_job ( ' interval ' , hours = 1 )
2019-02-20 13:00:22 -07:00
def updatePasses ( ) :
getFuture ( )
2019-02-25 11:24:46 -07:00
@scheduler.scheduled_job ( ' interval ' , hours = 1 )
2019-02-20 13:00:22 -07:00
def updateStations ( ) :
global Stations
print " Updating Stations "
Stations = GetGroundStations ( )
2019-02-25 11:24:46 -07:00
@scheduler.scheduled_job ( ' interval ' , minutes = 30 )
def updateCZML ( ) :
2019-02-25 08:19:57 -07:00
global CZML
2019-02-25 11:24:46 -07:00
CZML = [ ]
2019-02-20 13:00:22 -07:00
doc = { }
doc [ " id " ] = " document "
doc [ " name " ] = " sats "
2019-02-25 11:24:46 -07:00
doc [ " version " ] = " 1.0 "
doc [ " clock " ] = { }
doc [ " clock " ] [ " interval " ] = " 0000-00-00T00:00:00Z/9999-12-31T24:00:00Z "
2019-02-25 10:25:57 -07:00
doc [ " clock " ] [ " step " ] = " SYSTEM_CLOCK "
2019-02-20 13:00:22 -07:00
CZML . append ( doc )
2019-02-25 11:24:46 -07:00
2019-02-20 13:00:22 -07:00
for x in tqdm ( Stations ) :
2019-02-25 11:24:46 -07:00
color = [ 0 , 230 , 64 , 255 ]
2019-02-20 13:00:22 -07:00
if x [ " status " ] == " Testing " :
2019-02-25 11:24:46 -07:00
color = [ 248 , 148 , 6 , 255 ]
2019-02-20 13:00:22 -07:00
if x [ " status " ] == " Offline " :
2019-02-25 11:24:46 -07:00
color = [ 255 , 0 , 0 , 50 ]
2019-02-20 13:00:22 -07:00
station = { }
station [ " id " ] = str ( x [ " id " ] )
station [ " name " ] = x [ " name " ]
station [ " point " ] = { }
station [ " show " ] = True
station [ " point " ] [ " color " ] = { }
station [ " point " ] [ " color " ] [ " rgba " ] = color
station [ " point " ] [ " outlineColor " ] = { }
2019-02-25 11:24:46 -07:00
station [ " point " ] [ " outlineColor " ] [ " rgba " ] = [ 255 , 255 , 255 , color [ 3 ] ]
2019-02-20 13:00:22 -07:00
station [ " point " ] [ " outlineWidth " ] = 2.0
station [ " position " ] = { }
2019-02-25 11:24:46 -07:00
station [ " point " ] [ " pixelSize " ] = 7.0
station [ " position " ] [ " cartographicDegrees " ] = [ x [ " lng " ] , x [ ' lat ' ] , x [ " altitude " ] ]
station [ " description " ] = " <b>ID: " + str ( x [ " id " ] ) + " </b><br><b>Total Observations: "
station [ " description " ] + = str ( x [ " observations " ] ) + " </b><br><b>Status: " + x [ " status " ] + " </b><br><b>QTH: "
station [ " description " ] + = x [ " qthlocator " ] + " </b><br></b>Description: </b> " + x [ " description " ]
2019-02-20 13:00:22 -07:00
CZML . append ( station )
2019-02-25 11:24:46 -07:00
2019-02-20 13:00:22 -07:00
for x in tqdm ( Sats . keys ( ) ) :
for y in Sats [ x ] :
sat = { }
sat [ " id " ] = str ( y [ " id " ] )
sat [ " name " ] = TLEs [ x ] [ 0 ]
sat [ " show " ] = True
2019-02-25 11:24:46 -07:00
sat [ " billboard " ] = { " image " : " static/sat.png " , " scale " : 0.50 }
2019-02-20 13:00:22 -07:00
sat [ " position " ] = { }
2019-02-25 11:24:46 -07:00
sat [ " position " ] [ " cartographicDegrees " ] = [ ]
2019-02-25 08:19:57 -07:00
sat [ " description " ] = SatDescrip [ x ]
2019-02-20 13:00:22 -07:00
temp = y [ " start " ]
2019-02-25 11:24:46 -07:00
satObj = EarthSatellite ( TLEs [ x ] [ 1 ] , TLEs [ x ] [ 2 ] , TLEs [ x ] [ 0 ] )
2019-02-20 13:00:22 -07:00
time = 0
2019-02-25 11:24:46 -07:00
while temp < = y [ " end " ] + timedelta ( minutes = 1 ) :
2019-02-20 13:00:22 -07:00
subpoint = satObj . at ( ts . utc ( temp ) ) . subpoint ( )
lat = subpoint . latitude . degrees
lng = subpoint . longitude . degrees
elevation = subpoint . elevation . m
2019-02-25 11:24:46 -07:00
sat [ " position " ] [ " cartographicDegrees " ] . extend ( [ time , lng , lat , elevation ] )
time + = 60
temp = temp + timedelta ( minutes = 1 )
2019-02-20 13:00:22 -07:00
sat [ " position " ] [ " interpolationAlgorithm " ] = " LAGRANGE "
sat [ " position " ] [ " interpolationDegree " ] = 5
2019-02-25 11:24:46 -07:00
sat [ " position " ] [ " epoch " ] = ( y [ " start " ] . isoformat ( ) + " Z " ) . replace ( " +00:00 " , " " )
sat [ " path " ] = { " show " : { " interval " : ( y [ " start " ] . isoformat ( ) + " Z " ) . replace ( " +00:00 " , " " ) + " / " + ( ( y [ " end " ] + timedelta ( minutes = 1 ) ) . isoformat ( ) + " Z " ) . replace ( " +00:00 " , " " ) , " boolean " : True } , " width " : 2 , " material " : { " solidColor " : { " color " : { " rgba " : [ 0 , 255 , 0 , 255 ] } } } , " leadTime " : 100000 , " trailTime " : 100000 }
2019-02-20 13:00:22 -07:00
CZML . append ( sat )
for x in tqdm ( Sats . keys ( ) ) :
for y in Sats [ x ] :
sat = { }
sat [ " id " ] = str ( y [ " id " ] ) + " Link "
2019-02-25 11:24:46 -07:00
sat [ " polyline " ] = { " show " : { " interval " : ( y [ " start " ] . isoformat ( ) + " Z " ) . replace ( " +00:00 " , " " ) + " / " + ( ( y [ " end " ] + timedelta ( minutes = 1 ) ) . isoformat ( ) + " Z " ) . replace ( " +00:00 " , " " ) , " boolean " : True } , " width " : 2 , " material " : { " solidColor " : { " color " : { " rgba " : y [ " transmitter " ] [ 1 ] } } } , " followSurface " : False , " positions " : { " references " : [ str ( y [ " id " ] ) + " #position " , str ( y [ " station " ] ) + " #position " ] } }
CZML . append ( sat )
2019-02-20 13:00:22 -07:00
@app.route ( " / " )
def index ( ) :
2019-03-13 10:50:50 -06:00
Usage . update ( [ str ( request . headers . get ( " CF-IPCountry " ) ) ] )
2019-02-25 10:25:57 -07:00
return render_template ( " index.html " , url = " /czml " )
2019-03-13 10:50:50 -06:00
@app.route ( " /rotating " )
2019-03-13 10:55:40 -06:00
def rotating ( ) :
2019-03-13 10:50:50 -06:00
Usage . update ( [ str ( request . headers . get ( " CF-IPCountry " ) ) ] )
return render_template ( " rotating.html " , url = " /czml " )
2019-02-25 10:25:57 -07:00
@app.route ( " /station/<int:station_id> " )
def index_station ( station_id ) :
2019-03-13 10:50:50 -06:00
Usage . update ( request . headers . get ( " CF-IPCountry " ) )
2019-02-25 10:25:57 -07:00
return render_template ( " index.html " , url = " /czml/ " + str ( station_id ) )
2019-02-20 13:00:22 -07:00
@app.route ( ' /future_sats ' )
def api_future_sats ( ) :
return json . dumps ( Sats )
2019-02-25 11:24:46 -07:00
2019-02-20 13:00:22 -07:00
@app.route ( " /czml " )
def api_czml ( ) :
return json . dumps ( CZML )
2019-02-25 11:24:46 -07:00
@app.route ( " /broken " )
2019-02-20 13:00:22 -07:00
def api_broken ( ) :
output = defaultdict ( list )
for x in broken . keys ( ) :
output [ x ] = list ( broken [ x ] )
return json . dumps ( output )
2019-03-13 10:50:50 -06:00
2019-02-25 11:24:46 -07:00
@app.route ( " /czml/<int:station_id> " )
2019-02-25 10:25:57 -07:00
def api_station ( station_id ) :
czml = [ ]
doc = { }
doc [ " id " ] = " document "
doc [ " name " ] = " sats "
2019-02-25 11:24:46 -07:00
doc [ " version " ] = " 1.0 "
doc [ " clock " ] = { }
doc [ " clock " ] [ " interval " ] = " 0000-00-00T00:00:00Z/9999-12-31T24:00:00Z "
doc [ " clock " ] [ " currentTime " ] = datetime . utcnow ( ) . isoformat ( ) + " Z "
2019-02-25 10:25:57 -07:00
doc [ " clock " ] [ " step " ] = " SYSTEM_CLOCK "
czml . append ( doc )
2019-02-25 11:24:46 -07:00
2019-02-25 10:25:57 -07:00
for x in Stations :
if x [ " id " ] == station_id :
2019-02-25 11:24:46 -07:00
color = [ 0 , 230 , 64 , 255 ]
2019-02-25 10:25:57 -07:00
if x [ " status " ] == " Testing " :
2019-02-25 11:24:46 -07:00
color = [ 248 , 148 , 6 , 255 ]
2019-02-25 10:25:57 -07:00
if x [ " status " ] == " Offline " :
2019-02-25 11:24:46 -07:00
color = [ 255 , 0 , 0 , 50 ]
2019-02-25 10:25:57 -07:00
station = { }
station [ " id " ] = str ( x [ " id " ] )
station [ " name " ] = x [ " name " ]
station [ " point " ] = { }
station [ " show " ] = True
station [ " point " ] [ " color " ] = { }
station [ " point " ] [ " color " ] [ " rgba " ] = color
station [ " point " ] [ " outlineColor " ] = { }
2019-02-25 11:24:46 -07:00
station [ " point " ] [ " outlineColor " ] [ " rgba " ] = [ 255 , 255 , 255 , color [ 3 ] ]
2019-02-25 10:25:57 -07:00
station [ " point " ] [ " outlineWidth " ] = 2.0
station [ " position " ] = { }
2019-02-25 11:24:46 -07:00
station [ " point " ] [ " pixelSize " ] = 7.0
station [ " position " ] [ " cartographicDegrees " ] = [ x [ " lng " ] , x [ ' lat ' ] , x [ " altitude " ] ]
station [ " description " ] = " <b>ID: " + str ( x [ " id " ] ) + " </b><br><b>Total Observations: "
station [ " description " ] + = str ( x [ " observations " ] ) + " </b><br><b>Status: " + x [ " status " ] + " </b><br><b>QTH: "
station [ " description " ] + = x [ " qthlocator " ] + " </b><br></b>Description: </b> " + x [ " description " ]
2019-02-25 10:25:57 -07:00
czml . append ( station )
break
2019-02-20 13:00:22 -07:00
2019-02-25 10:25:57 -07:00
for y in StationsPasses [ station_id ] :
sat = { }
sat [ " id " ] = str ( y [ " id " ] )
sat [ " name " ] = TLEs [ y [ " norad " ] ] [ 0 ]
sat [ " show " ] = True
sat [ " point " ] = { }
sat [ " point " ] [ " color " ] = { }
2019-02-25 11:24:46 -07:00
sat [ " point " ] [ " color " ] [ " rgba " ] = [ 255 , 0 , 0 , 255 ]
sat [ " point " ] [ " pixelSize " ] = 8.0
2019-02-25 10:25:57 -07:00
sat [ " position " ] = { }
2019-02-25 11:24:46 -07:00
sat [ " position " ] [ " cartographicDegrees " ] = [ ]
2019-02-25 10:25:57 -07:00
sat [ " description " ] = SatDescrip [ y [ " norad " ] ]
temp = y [ " start " ]
2019-02-25 11:24:46 -07:00
satObj = EarthSatellite ( TLEs [ y [ " norad " ] ] [ 1 ] , TLEs [ y [ " norad " ] ] [ 2 ] , TLEs [ y [ " norad " ] ] [ 0 ] )
2019-02-25 10:25:57 -07:00
time = 0
while temp < = y [ " end " ] + timedelta ( minutes = 1 ) :
subpoint = satObj . at ( ts . utc ( temp ) ) . subpoint ( )
lat = subpoint . latitude . degrees
lng = subpoint . longitude . degrees
elevation = subpoint . elevation . m
2019-02-25 11:24:46 -07:00
sat [ " position " ] [ " cartographicDegrees " ] . extend ( [ time , lng , lat , elevation ] )
time + = 60
temp = temp + timedelta ( minutes = 1 )
2019-02-25 10:25:57 -07:00
sat [ " position " ] [ " interpolationAlgorithm " ] = " LAGRANGE "
sat [ " position " ] [ " interpolationDegree " ] = 5
2019-02-25 11:24:46 -07:00
sat [ " position " ] [ " epoch " ] = ( y [ " start " ] . isoformat ( ) + " Z " ) . replace ( " +00:00 " , " " )
sat [ " path " ] = { " show " : { " interval " : ( y [ " start " ] . isoformat ( ) + " Z " ) . replace ( " +00:00 " , " " ) + " / " + ( ( y [ " end " ] + timedelta ( minutes = 1 ) ) . isoformat ( ) + " Z " ) . replace ( " +00:00 " , " " ) , " boolean " : True } , " width " : 2 , " material " : { " solidColor " : { " color " : { " rgba " : [ 0 , 255 , 0 , 255 ] } } } , " leadTime " : 100000 , " trailTime " : 100000 }
2019-02-25 10:25:57 -07:00
czml . append ( sat )
for y in StationsPasses [ station_id ] :
sat = { }
sat [ " id " ] = str ( y [ " id " ] ) + " Link "
2019-02-25 11:24:46 -07:00
sat [ " polyline " ] = { " show " : { " interval " : ( y [ " start " ] . isoformat ( ) + " Z " ) . replace ( " +00:00 " , " " ) + " / " + ( ( y [ " end " ] + timedelta ( minutes = 1 ) ) . isoformat ( ) + " Z " ) . replace ( " +00:00 " , " " ) , " boolean " : True } , " width " : 2 , " material " : { " solidColor " : { " color " : { " rgba " : y [ " transmitter " ] [ 1 ] } } } , " followSurface " : False , " positions " : { " references " : [ str ( y [ " id " ] ) + " #position " , str ( station_id ) + " #position " ] } }
czml . append ( sat )
2019-02-25 10:25:57 -07:00
return json . dumps ( czml )
2019-02-20 13:00:22 -07:00
2019-02-25 11:24:46 -07:00
2019-02-20 13:00:22 -07:00
updateStations ( )
updateTransmitters ( )
getFuture ( )
updateCZML ( )
scheduler . start ( )
2019-02-25 11:24:46 -07:00
app . run ( use_reloader = False , host = " 0.0.0.0 " , port = 5001 )