1
0
Fork 0
satnogsmap/satnogs.py

352 lines
14 KiB
Python

from datetime import datetime, timedelta
import requests
from tqdm import tqdm
from flask import Flask , render_template,redirect,url_for, request
import json
from collections import defaultdict
import random
from apscheduler.schedulers.background import BackgroundScheduler
from satnogs_api_client import fetch_satellites
from satnogs_api_client.satnogs_api_client import DB_BASE_URL, get_paginated_endpoint
from satellite_tle import fetch_tles
from skyfield.api import EarthSatellite, utc, load
import numpy
scheduler = BackgroundScheduler()
app = Flask(__name__)
ts = load.timescale()
Observations = defaultdict(list)
Passes = defaultdict(list)
Stations = []
StationsByID = {}
TLEs = defaultdict(list)
Transmitters = defaultdict(dict)
StationsPasses = defaultdict(list)
SatDescrip = defaultdict(str)
CZMLOnline = []
CZMLTesting = []
CZMLOffline = []
def getFuture():
print("Getting future Passes")
global Observations
global TLEs
observations = defaultdict(dict)
Start = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S%z')
End = (datetime.utcnow() + timedelta(minutes=30))
End = End.strftime('%Y-%m-%dT%H:%M:%S%z')
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
Observations = defaultdict(list)
for x in tqdm(passes):
#if x["id"] in observations:
norad = int(x["tle1"].split(" ")[1].replace("U",""))
try:
start = datetime.strptime(x["start"], '%Y-%m-%dT%H:%M:%Sz')
start = start.replace(tzinfo=utc)
end = datetime.strptime(x["end"], '%Y-%m-%dT%H:%M:%Sz')
end = end.replace(tzinfo=utc)
# "transmitter":Transmitters[observations[x["id"]]["norad_cat_id"]][x["transmitter"]]
if start < end:
Observations[norad].append({"station": x["ground_station"], "transmitter": Transmitters[norad][x["transmitter"]], "start": start, "end": end, "id": x["id"]})
TLEs[norad] = EarthSatellite(x["tle1"], x["tle2"],x["tle0"])
StationsPasses[x["ground_station"]].append({"norad": norad, "transmitter": Transmitters[norad][x["transmitter"]], "start": start, "end": end, "id": x["id"]})
except Exception as e:
print("Error on observation number: " + str(x["id"]) + " "+str(norad)+" " + str(e))
print(str(len(Observations))+" Future passes found.")
def GetGroundStations():
print("Getting Ground Stations")
stations = get_paginated_endpoint("https://network.satnogs.org/api/stations/")
for x in stations:
StationsByID[x["id"]] = x
return stations
def FindPasses(observations_):
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)
return passses
def updateTLE():
print("Updating TLE")
global TLEs
sats = fetch_satellites(None, DB_BASE_URL)
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
tles = fetch_tles(satnogs_db_norad_ids)
TLEs = {}
for norad_id, (source, tle) in tqdm(tles.items()):
TLEs[norad_id] = EarthSatellite(tle[1], Ttle[2], tle[0])
print('\nTLEs for {} of {} requested satellites found ({} satellites with temporary norad ids skipped).'.format(len(tles), len(satnogs_db_norad_ids), len(temporary_norad_ids)))
@scheduler.scheduled_job('interval', days=1)
def updateTransmitters():
global Transmitters
global SatDescrip
SatDescrip = {}
print("Updating Transmitters")
temp = requests.get("https://db.satnogs.org/api/transmitters/").json()
for x in tqdm(temp):
Transmitters[x["norad_cat_id"]][x["uuid"]] = [x["description"], [random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), 255]]
SatDescrip[x["norad_cat_id"]] = ""
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>'
@scheduler.scheduled_job('interval', minutes=15)#hours=1)
def updatePasses():
getFuture()
updateCZML()
@scheduler.scheduled_job('interval', hours=1)
def updateStations():
global Stations
print("Updating Stations")
Stations = GetGroundStations()
#@scheduler.scheduled_job('interval', minutes=5)
def updateCZML():
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)
for x in tqdm(Stations):
color = [0, 230, 64, 255]
if x["status"] == "Testing":
color = [248, 148, 6, 255]
if x["status"] == "Offline":
color = [255, 0, 0, 50]
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"] = {}
station["point"]["outlineColor"]["rgba"] = [255, 255, 255, color[3]]
station["point"]["outlineWidth"] = 2.0
station["position"] = {}
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"]
if x["status"] == "Testing":
CZMLTesting.append(station)
else:
if x["status"] == "Offline":
CZMLOffline.append(station)
else:
CZMLOnline.append(station)
AliveSats = []
for x in tqdm(Observations.keys()):
for y in Observations[x]:
sat = {}
sat["id"] = str(y["id"])
sat["name"] = TLEs[x].name
sat["show"] = True
sat["billboard"] = {"image": "static/sat.png", "scale": 0.50}
sat["position"] = {}
sat["position"]["cartographicDegrees"] = []
sat["description"] = SatDescrip[x]
temp = y["start"]
time = 0
sat["position"]["interpolationAlgorithm"] = "LAGRANGE"
sat["position"]["interpolationDegree"] = 5
sat["position"]["epoch"] = (y["start"].isoformat()+"Z").replace("+00: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}
while temp <= y["end"] + timedelta(seconds=1):
subpoint = TLEs[x].at(ts.utc(temp)).subpoint()
lat = subpoint.latitude.degrees
lng = subpoint.longitude.degrees
if numpy.isnan(lat):
break
elevation = subpoint.elevation.m
sat["position"]["cartographicDegrees"].extend([time, lng, lat, elevation])
time += 60
temp = temp + timedelta(minutes=1)
else:
if StationsByID[y["station"]]["status"] =="Testing":
CZMLTesting.append(sat)
else:
if StationsByID[y["station"]]["status"] == "Offline":
CZMLOffline.append(sat)
else:
CZMLOnline.append(sat)
AliveSats.append(str(y["id"]))
for x in tqdm(Observations.keys()):
for y in Observations[x]:
if str(y["id"]) in AliveSats:
sat = {}
sat["id"] = str(y["id"])+"Link"
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"]}}
if StationsByID[y["station"]]["status"] =="Testing":
CZMLTesting.append(sat)
else:
if StationsByID[y["station"]]["status"] == "Offline":
CZMLOffline.append(sat)
else:
CZMLOnline.append(sat)
@app.route("/")
def index():
return render_template("index.html",url="/czml")
@app.route("/rotating")
def rotating():
speed = request.args.get('speed', default = '30', type = str)
return render_template("rotating.html",url="/czml",speed=speed)
@app.route("/station/<int:station_id>")
def index_station(station_id):
return render_template("index.html",url="/czml/"+str(station_id))
@app.route("/czml")
def api_czml():
return json.dumps(CZMLOnline)
@app.route("/czmloff")
def api_czml1():
return json.dumps(CZMLOffline)
@app.route("/czmltest")
def api_czml2():
return json.dumps(CZMLTesting)
@app.route("/czml/<int:station_id>")
def api_station(station_id):
czml = []
doc = {}
doc["id"] = "document"
doc["name"] = "sats"
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"
doc["clock"]["step"] = "SYSTEM_CLOCK"
czml.append(doc)
for x in Stations:
if x["id"] == station_id:
color = [0, 230, 64, 255]
if x["status"] == "Testing":
color = [248, 148, 6, 255]
if x["status"] == "Offline":
color = [255, 0, 0, 50]
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"] = {}
station["point"]["outlineColor"]["rgba"] = [255, 255, 255, color[3]]
station["point"]["outlineWidth"] = 2.0
station["position"] = {}
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"]
czml.append(station)
break
AliveSats = []
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"] = {}
sat["point"]["color"]["rgba"] = [255, 0, 0, 255]
sat["point"]["pixelSize"] = 8.0
sat["position"] = {}
sat["position"]["cartographicDegrees"] = []
sat["description"] = SatDescrip[y["norad"]]
temp = y["start"]
satObj = EarthSatellite(TLEs[y["norad"]][1], TLEs[y["norad"]][2], TLEs[y["norad"]][0])
time = 0
sat["position"]["interpolationAlgorithm"] = "LAGRANGE"
sat["position"]["interpolationDegree"] = 5
sat["position"]["epoch"] = (y["start"].isoformat() + "Z").replace("+00: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": 1}
while temp <= y["end"]:
subpoint = satObj.at(ts.utc(temp)).subpoint()
lat = subpoint.latitude.degrees
lng = subpoint.longitude.degrees
if numpy.isnan(lat):
break
elevation = subpoint.elevation.m
sat["position"]["cartographicDegrees"].extend([time, lng, lat, elevation])
time += 60
temp = temp + timedelta(minutes=1)
else:
czml.append(sat)
AliveSats.append(str(y["id"]))
for y in StationsPasses[station_id]:
if str(y["id"]) in AliveSats:
sat = {}
sat["id"] = str(y["id"])+"Link"
sat["polyline"] = {"show": {"interval": (y["start"].isoformat()+"Z").replace("+00:00", "") + "/" + ((y["end"]).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)
return json.dumps(czml)
updateStations()
updateTransmitters()
getFuture()
updateCZML()
scheduler.start()
app.run(use_reloader=False, host="0.0.0.0", port=5000)