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-04-30 17:30:04 -06:00
from collections import defaultdict
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
2019-09-04 09:50:28 -06:00
from satnogs_api_client . 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-04-30 17:30:04 -06:00
import numpy
2019-10-31 10:33:14 -06:00
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
2019-04-30 17:30:04 -06:00
Observations = defaultdict ( list )
2019-02-20 13:00:22 -07:00
Passes = defaultdict ( list )
Stations = [ ]
2019-04-30 17:30:04 -06:00
StationsByID = { }
2019-02-20 13:00:22 -07:00
TLEs = defaultdict ( list )
Transmitters = defaultdict ( dict )
2019-10-31 10:33:14 -06:00
Raw_Transmitters = { }
2019-02-25 10:25:57 -07:00
StationsPasses = defaultdict ( list )
2019-02-25 08:19:57 -07:00
SatDescrip = defaultdict ( str )
2019-04-30 17:30:04 -06:00
CZMLOnline = [ ]
CZMLTesting = [ ]
CZMLOffline = [ ]
2019-10-31 10:33:14 -06:00
TransmitterStats = [ ]
2019-02-20 13:00:22 -07:00
2019-02-25 11:24:46 -07:00
2019-02-20 13:00:22 -07:00
def getFuture ( ) :
2019-03-15 14:55:21 -06:00
print ( " Getting future Passes " )
2019-04-30 17:30:04 -06:00
global Observations
2019-02-20 13:00:22 -07:00
global TLEs
2019-10-02 15:51:20 -06:00
global StationsPasses
2019-10-29 08:00:32 -06:00
norads = { }
2019-10-02 15:51:20 -06:00
TLEs = defaultdict ( list )
StationsPasses = defaultdict ( list )
2019-02-25 11:24:46 -07:00
observations = defaultdict ( dict )
2019-09-04 09:50:28 -06:00
Start = datetime . utcnow ( ) . strftime ( ' % Y- % m- %d T % H: % M: % S % z ' )
2019-10-29 08:00:32 -06:00
End = ( datetime . utcnow ( ) + timedelta ( days = 1 ) )
2019-09-04 09:50:28 -06:00
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/ " )
2019-10-29 08:00:32 -06:00
# print("Started")
# obs = get_paginated_endpoint("https://network.satnogs.org/api/observations/?end="+End+"&format=json&start="+Start)
# for x in obs:
# norads[x["id"]] = x["noard_cat_id"]
2019-04-30 17:30:04 -06:00
Observations = defaultdict ( list )
2019-10-02 15:51:20 -06:00
for x in passes :
if x [ " ground_station " ] == None :
continue
2019-09-04 09:50:28 -06:00
#if x["id"] in observations:
2019-10-29 08:00:32 -06:00
#noard = norads[x["id"]]
2019-09-04 09:50:28 -06:00
norad = int ( x [ " tle1 " ] . split ( " " ) [ 1 ] . replace ( " U " , " " ) )
try :
2019-10-29 08:00:32 -06:00
if ( not norad in SatDescrip . keys ( ) ) :
continue
2019-09-04 09:50:28 -06:00
start = datetime . strptime ( x [ " start " ] , ' % Y- % m- %d T % H: % M: % Sz ' )
start = start . replace ( tzinfo = utc )
end = datetime . strptime ( x [ " end " ] , ' % Y- % m- %d T % H: % M: % Sz ' )
end = end . replace ( tzinfo = utc )
2019-02-20 13:00:22 -07:00
# "transmitter":Transmitters[observations[x["id"]]["norad_cat_id"]][x["transmitter"]]
2019-09-04 09:50:28 -06:00
if start < end :
2019-10-29 08:00:32 -06:00
Observations [ norad ] . append ( { " station " : x [ " ground_station " ] , " transmitter " : Transmitters [ x [ " transmitter " ] ] , " start " : start , " end " : end , " id " : x [ " id " ] } )
2019-09-04 09:50:28 -06:00
TLEs [ norad ] = EarthSatellite ( x [ " tle1 " ] , x [ " tle2 " ] , x [ " tle0 " ] )
2019-10-29 08:00:32 -06:00
StationsPasses [ x [ " ground_station " ] ] . append ( { " norad " : norad , " transmitter " : Transmitters [ x [ " transmitter " ] ] , " start " : start , " end " : end , " id " : x [ " id " ] } )
2019-09-04 09:50:28 -06:00
except Exception as e :
2019-10-02 15:51:20 -06:00
pass
2019-09-04 09:50:28 -06:00
print ( " Error on observation number: " + str ( x [ " id " ] ) + " " + str ( norad ) + " " + str ( e ) )
2019-10-02 15:51:20 -06:00
del observations
del passes
del End
del Start
2019-04-30 17:30:04 -06:00
print ( str ( len ( Observations ) ) + " Future passes found. " )
2019-02-25 11:24:46 -07:00
2019-10-02 15:51:20 -06:00
2019-02-20 13:00:22 -07:00
def GetGroundStations ( ) :
2019-03-15 14:55:21 -06:00
print ( " Getting Ground Stations " )
2019-02-20 13:00:22 -07:00
stations = get_paginated_endpoint ( " https://network.satnogs.org/api/stations/ " )
2019-04-30 17:30:04 -06:00
for x in stations :
StationsByID [ x [ " id " ] ] = x
2019-02-20 13:00:22 -07:00
return stations
2019-02-25 11:24:46 -07:00
2019-04-30 17:30:04 -06:00
def FindPasses ( observations_ ) :
2019-10-02 15:51:20 -06:00
print ( " Finding Future Passes " )
2019-04-30 17:30:04 -06:00
observations = observations_ . copy ( )
passses = [ ]
observation = observations . pop ( 0 )
for x in observations :
if x [ " end " ] > = observation [ " start " ] :
observation [ " start " ] = x [ " start " ]
else :
if ( observation [ " start " ] - x [ " end " ] ) . total_seconds ( ) < 120 :
observation [ " start " ] = x [ " start " ]
else :
passses . append ( observation )
observation = x
passses . append ( observation )
2019-10-02 15:51:20 -06:00
print ( " Finished Finding Future Passes " )
2019-04-30 17:30:04 -06:00
return passses
2019-02-20 13:00:22 -07:00
def updateTLE ( ) :
2019-03-15 14:55:21 -06:00
print ( " Updating TLE " )
2019-02-20 13:00:22 -07:00
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-09-04 09:50:28 -06:00
TLEs [ norad_id ] = EarthSatellite ( tle [ 1 ] , Ttle [ 2 ] , tle [ 0 ] )
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-10-31 10:33:14 -06:00
@scheduler.scheduled_job ( ' interval ' , days = 0.5 )
2019-02-20 13:00:22 -07:00
def updateTransmitters ( ) :
global Transmitters
2019-09-04 09:50:28 -06:00
global SatDescrip
SatDescrip = { }
2019-10-02 15:51:20 -06:00
Transmitters = defaultdict ( dict )
2019-03-15 14:55:21 -06:00
print ( " Updating Transmitters " )
2019-02-20 13:00:22 -07:00
temp = requests . get ( " https://db.satnogs.org/api/transmitters/ " ) . json ( )
2019-10-02 15:51:20 -06:00
for x in temp :
2019-10-31 10:33:14 -06:00
Raw_Transmitters [ x [ " uuid " ] ] = x
2019-10-29 08:00:32 -06:00
Transmitters [ x [ " uuid " ] ] = [ x [ " description " ] , [ random . randint ( 0 , 255 ) , random . randint ( 0 , 255 ) , random . randint ( 0 , 255 ) , 255 ] ]
2019-09-04 09:50:28 -06:00
SatDescrip [ x [ " norad_cat_id " ] ] = " "
2019-10-29 08:00:32 -06:00
#for x in Transmitters.keys():
for x in temp :
SatDescrip [ x [ " norad_cat_id " ] ] + = ' <div class= " trans " style= " background-color:# ' + str ( " %02x " % Transmitters [ x [ " uuid " ] ] [ 1 ] [ 0 ] ) + str ( " %02x " % Transmitters [ x [ " uuid " ] ] [ 1 ] [ 1 ] ) + str ( " %02x " % Transmitters [ x [ " uuid " ] ] [ 1 ] [ 2 ] ) + ' " ;> ' + Transmitters [ x [ " uuid " ] ] [ 0 ] + ' </div> '
2019-10-02 15:51:20 -06:00
print ( " Finished Updating Transmitters " )
2019-10-31 10:33:14 -06:00
updateTransmitterStats ( )
2019-02-25 11:24:46 -07:00
2019-10-31 10:33:14 -06:00
def updateTransmitterStats ( ) :
print ( " Updating Transmitter Stats " )
global TransmitterStats
TransmitterStats = [ ]
transmitterStats = get_paginated_endpoint ( " https://network.satnogs.org/api/transmitters/ " )
satellites = requests . get ( " https://db.satnogs.org/api/satellites/ " ) . json ( )
sats = { }
for x in satellites :
sats [ x [ " norad_cat_id " ] ] = x
for x in transmitterStats :
try :
transmitter = Raw_Transmitters [ x [ " uuid " ] ]
stat = x [ " stats " ]
sat = sats [ transmitter [ " norad_cat_id " ] ]
stat [ " transmitter_name " ] = transmitter [ " description " ]
stat [ " sat_name " ] = sat [ " name " ]
stat [ " norad " ] = transmitter [ " norad_cat_id " ]
TransmitterStats . append ( stat )
except :
pass
TransmitterStats . sort ( key = lambda x : x [ " success_rate " ] , reverse = True )
print ( " Finished Updating Transmitter Stats " )
2019-05-21 10:39:08 -06:00
@scheduler.scheduled_job ( ' interval ' , minutes = 15 ) #hours=1)
2019-02-20 13:00:22 -07:00
def updatePasses ( ) :
getFuture ( )
2019-05-21 10:39:08 -06:00
updateCZML ( )
2019-10-02 15:51:20 -06:00
# total = 0
# for x in StationsPasses.keys():
# total+=len(StationsPasses[x])
# print("Total StationsPassses: " +str(total))
# print("Total TLE: "+str(len(TLEs)))
# print("Total Passes: "+str(len(Passes)))
#print("Total StationsPasses: "+str(len(StationsPasses)))
# all_objects = muppy.get_objects()
# sum1 = summary.summarize(all_objects)
# summary.print_(sum1)
# del all_objects
# del sum1
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
2019-03-15 14:55:21 -06:00
print ( " Updating Stations " )
2019-02-20 13:00:22 -07:00
Stations = GetGroundStations ( )
2019-02-25 11:24:46 -07:00
2019-05-21 10:39:08 -06:00
#@scheduler.scheduled_job('interval', minutes=5)
2019-02-25 11:24:46 -07:00
def updateCZML ( ) :
2019-10-29 08:00:32 -06:00
#print(SatDescrip.keys())
2019-10-02 15:51:20 -06:00
print ( " Updating CZML " )
2019-04-30 17:30:04 -06:00
global CZMLOnline
global CZMLOffline
global CZMLTesting
CZMLOffline = [ ]
CZMLOnline = [ ]
CZMLTesting = [ ]
onlineDoc = { " id " : " document " , " name " : " Online " , " version " : " 1.0 " , " clock " : { " interval " : " 0000-00-00T00:00:00Z/9999-12-31T24:00:00Z " , " step " : " SYSTEM_CLOCK " } }
offlineDoc = { " id " : " document " , " name " : " Offline " , " version " : " 1.0 " , " clock " : { " interval " : " 0000-00-00T00:00:00Z/9999-12-31T24:00:00Z " , " step " : " SYSTEM_CLOCK " } }
testingDoc = { " id " : " document " , " name " : " Testing " , " version " : " 1.0 " , " clock " : { " interval " : " 0000-00-00T00:00:00Z/9999-12-31T24:00:00Z " , " step " : " SYSTEM_CLOCK " } }
CZMLOffline . append ( offlineDoc )
CZMLOnline . append ( onlineDoc )
CZMLTesting . append ( testingDoc )
2019-10-02 15:51:20 -06:00
for x in 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-04-30 17:30:04 -06:00
if x [ " status " ] == " Testing " :
CZMLTesting . append ( station )
else :
if x [ " status " ] == " Offline " :
CZMLOffline . append ( station )
else :
CZMLOnline . append ( station )
AliveSats = [ ]
2019-10-02 15:51:20 -06:00
for x in Observations . keys ( ) :
2019-04-30 17:30:04 -06:00
for y in Observations [ x ] :
2019-02-20 13:00:22 -07:00
sat = { }
sat [ " id " ] = str ( y [ " id " ] )
2019-09-04 09:50:28 -06:00
sat [ " name " ] = TLEs [ x ] . name
2019-02-20 13:00:22 -07:00
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-10-29 08:00:32 -06:00
# print(y)
2019-02-25 08:19:57 -07:00
sat [ " description " ] = SatDescrip [ x ]
2019-02-20 13:00:22 -07:00
temp = y [ " start " ]
time = 0
2019-04-30 17:30:04 -06:00
sat [ " position " ] [ " interpolationAlgorithm " ] = " LAGRANGE "
sat [ " position " ] [ " interpolationDegree " ] = 5
sat [ " position " ] [ " epoch " ] = ( y [ " start " ] . isoformat ( ) + " Z " ) . replace ( " +00:00 " , " " )
2019-05-21 10:39:08 -06:00
sat [ " path " ] = { " show " : { " interval " : ( y [ " start " ] . isoformat ( ) + " Z " ) . replace ( " +00:00 " , " " ) + " / " + ( ( y [ " end " ] ) . isoformat ( ) + " Z " ) . replace ( " +00:00 " , " " ) , " boolean " : True } , " width " : 2 , " material " : { " solidColor " : { " color " : { " rgba " : [ 0 , 255 , 0 , 255 ] } } } , " leadTime " : 100000 , " trailTime " : 100000 }
2019-04-30 17:30:04 -06:00
2019-05-21 10:39:08 -06:00
while temp < = y [ " end " ] + timedelta ( seconds = 1 ) :
2019-09-04 09:50:28 -06:00
subpoint = TLEs [ x ] . at ( ts . utc ( temp ) ) . subpoint ( )
2019-02-20 13:00:22 -07:00
lat = subpoint . latitude . degrees
lng = subpoint . longitude . degrees
2019-04-30 17:30:04 -06:00
if numpy . isnan ( lat ) :
break
2019-02-20 13:00:22 -07:00
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-04-30 17:30:04 -06:00
else :
2019-10-02 15:51:20 -06:00
try :
# print(StationsByID)
# print(y["station"])
if StationsByID [ y [ " station " ] ] [ " status " ] == " Testing " :
CZMLTesting . append ( sat )
2019-04-30 17:30:04 -06:00
else :
2019-10-02 15:51:20 -06:00
if StationsByID [ y [ " station " ] ] [ " status " ] == " Offline " :
CZMLOffline . append ( sat )
else :
CZMLOnline . append ( sat )
except :
print ( y )
print ( StationsByID [ y [ " station " ] ] )
2019-04-30 17:30:04 -06:00
AliveSats . append ( str ( y [ " id " ] ) )
2019-10-02 15:51:20 -06:00
for x in Observations . keys ( ) :
2019-04-30 17:30:04 -06:00
for y in Observations [ x ] :
if str ( y [ " id " ] ) in AliveSats :
sat = { }
sat [ " id " ] = str ( y [ " id " ] ) + " Link "
2019-05-21 10:39:08 -06:00
sat [ " polyline " ] = { " show " : { " interval " : ( y [ " start " ] . isoformat ( ) + " Z " ) . replace ( " +00:00 " , " " ) + " / " + ( ( y [ " end " ] + timedelta ( seconds = 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 " ] } }
2019-04-30 17:30:04 -06:00
if StationsByID [ y [ " station " ] ] [ " status " ] == " Testing " :
CZMLTesting . append ( sat )
else :
if StationsByID [ y [ " station " ] ] [ " status " ] == " Offline " :
CZMLOffline . append ( sat )
else :
CZMLOnline . append ( sat )
2019-10-02 15:51:20 -06:00
print ( " Finished Updating CZML " )
2019-02-25 11:24:46 -07:00
2019-02-20 13:00:22 -07:00
@app.route ( " / " )
def index ( ) :
2019-02-25 10:25:57 -07:00
return render_template ( " index.html " , url = " /czml " )
2019-03-15 14:55:21 -06:00
2019-03-13 10:50:50 -06:00
@app.route ( " /rotating " )
2019-03-13 10:55:40 -06:00
def rotating ( ) :
2019-03-15 14:55:21 -06:00
speed = request . args . get ( ' speed ' , default = ' 30 ' , type = str )
return render_template ( " rotating.html " , url = " /czml " , speed = speed )
2019-04-30 17:30:04 -06:00
2019-02-20 13:00:22 -07:00
@app.route ( " /czml " )
def api_czml ( ) :
2019-04-30 17:30:04 -06:00
return json . dumps ( CZMLOnline )
2019-02-25 11:24:46 -07:00
2019-04-30 17:30:04 -06:00
@app.route ( " /czmloff " )
2019-10-31 10:33:14 -06:00
def api_czmloff ( ) :
2019-04-30 17:30:04 -06:00
return json . dumps ( CZMLOffline )
2019-02-25 11:24:46 -07:00
2019-04-30 17:30:04 -06:00
@app.route ( " /czmltest " )
2019-10-31 10:33:14 -06:00
def api_czmltest ( ) :
2019-04-30 17:30:04 -06:00
return json . dumps ( CZMLTesting )
2019-03-13 10:50:50 -06:00
2019-10-31 10:33:14 -06:00
@app.route ( " /transmitterstats " )
def transmitterStats ( ) :
return render_template ( " transmitterstats.html " , stats = TransmitterStats )
2019-02-25 11:24:46 -07:00
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 ( )
2020-01-11 21:22:24 -07:00
app . run ( use_reloader = False , host = " 127.0.0.1 " , port = 5000 )