Merge branch 'dev' into 'master'
Merge Current working system into master. See merge request chibill/satnogsmap!2dev
commit
e1d7e0a713
24
README.md
24
README.md
|
@ -1,6 +1,24 @@
|
|||
# SatnogsMap
|
||||
#Satnogs-Map
|
||||
|
||||
Uses Satnogs data to generate a map of all stations on Production and all sats currently observed.
|
||||
Uses CesiumJS and a python backend to make a very nice map view of the satnogs network.
|
||||
|
||||
Launch using python satnogs.py
|
||||
|
||||
|
||||
Currently hosted at satnogs.minecraft16.ml
|
||||
Access the basic map at
|
||||
|
||||
http://127.0.0.1:5001 when launched
|
||||
|
||||
Access an auto rotating view at
|
||||
|
||||
http://127.0.0.1:5001/rotating
|
||||
|
||||
add a speed arg to the url to change the speed.
|
||||
|
||||
Eaxmple
|
||||
|
||||
http://127.0.0.1:5001/rotating?speed=60
|
||||
|
||||
Access station specific views at
|
||||
|
||||
http://127.0.0.1:5001/station/<station_id> replacing <station_id> with the id of the station on the network.
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
requests==2.21.0
|
||||
flask==1.0.2
|
||||
apscheduler==3.5.3
|
||||
satellitetle==0.5.1
|
||||
satellitetle==0.5.1
|
||||
tqdm==4.31.1
|
||||
skyfield==1.10
|
||||
|
|
423
satnogs.py
423
satnogs.py
|
@ -1,197 +1,298 @@
|
|||
from datetime import datetime , timedelta
|
||||
from datetime import datetime, timedelta
|
||||
import requests
|
||||
from flask import Flask , render_template,redirect,url_for
|
||||
from tqdm import tqdm
|
||||
from flask import Flask , render_template,redirect,url_for, request
|
||||
import json
|
||||
from collections import defaultdict, Counter
|
||||
import random
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from satnogs_api_client import fetch_satellites, DB_BASE_URL,fetch_tle_of_observation
|
||||
from satnogs_api_client import fetch_satellites
|
||||
from satnogs_api_client import DB_BASE_URL, get_paginated_endpoint
|
||||
from satellite_tle import fetch_tles
|
||||
from skyfield.api import EarthSatellite, utc, load
|
||||
|
||||
scheduler = BackgroundScheduler()
|
||||
app = Flask(__name__)
|
||||
|
||||
ts = load.timescale()
|
||||
|
||||
Passes = []
|
||||
Occuring_sats = {}
|
||||
|
||||
broken = defaultdict(set)
|
||||
|
||||
|
||||
Usage = Counter()
|
||||
Sats = defaultdict(list)
|
||||
Passes = defaultdict(list)
|
||||
Stations = []
|
||||
TLEs = {}
|
||||
Transmitters = {}
|
||||
|
||||
class Pass:
|
||||
id = 0
|
||||
start = None
|
||||
end = None
|
||||
ground_station = None
|
||||
satellite = None
|
||||
transmitter = None
|
||||
norad = 0
|
||||
|
||||
TLEs = defaultdict(list)
|
||||
Transmitters = defaultdict(dict)
|
||||
StationsPasses = defaultdict(list)
|
||||
SatDescrip = defaultdict(str)
|
||||
CZML = []
|
||||
|
||||
|
||||
def getFuture():
|
||||
print("Getting future Passes")
|
||||
global Sats
|
||||
global TLEs
|
||||
observations = defaultdict(dict)
|
||||
start = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S%z')
|
||||
end = (datetime.utcnow() + timedelta(hours=4, 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
|
||||
Sats = defaultdict(list)
|
||||
for x in tqdm(passes):
|
||||
if x["id"] in observations:
|
||||
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"]]
|
||||
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"]})
|
||||
except Exception as e:
|
||||
print("Error on observation number: " + str(x["id"]) + " " + str(e))
|
||||
broken[observations[x["id"]]["norad_cat_id"]].update([x["transmitter"]])
|
||||
print(str(len(Sats))+" Future passes found.")
|
||||
|
||||
|
||||
def getActive():
|
||||
start = (datetime.utcnow() - timedelta(0,0,0,0,20)).strftime('%Y-%m-%dT%H:%M:%S%z')
|
||||
end = (datetime.utcnow() + timedelta(0,0,0,0,30)).strftime('%Y-%m-%dT%H:%M:%S%z')
|
||||
|
||||
passesR = requests.get("https://network.satnogs.org/api/observations/?end="+end+"&format=json&start="+start)
|
||||
passes = passesR.json()
|
||||
if passesR.links.has_key("next"):
|
||||
while passesR.links.has_key("next"):
|
||||
passesR = requests.get(passesR.links["next"]["url"])
|
||||
passes += passesR.json()
|
||||
ground_stations = {}
|
||||
for x in passes:
|
||||
if datetime.strptime(x["start"],'%Y-%m-%dT%H:%M:%Sz') > datetime.utcnow() or datetime.strptime(x["end"],'%Y-%m-%dT%H:%M:%Sz') < datetime.utcnow():
|
||||
passes.remove(x)
|
||||
else:
|
||||
if ground_stations.has_key(x["ground_station"]):
|
||||
ground_stations[x["ground_station"]].append(x)
|
||||
else:
|
||||
ground_stations[x["ground_station"]] = []
|
||||
ground_stations[x["ground_station"]].append(x)
|
||||
passes = []
|
||||
for x in ground_stations:
|
||||
start = datetime.utcnow()
|
||||
current = {"start":datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S%z')+"z"}
|
||||
for y in ground_stations[x]:
|
||||
if datetime.strptime(y["start"],'%Y-%m-%dT%H:%M:%Sz') < datetime.strptime(current["start"],'%Y-%m-%dT%H:%M:%Sz'):
|
||||
current = y
|
||||
passes.append(current)
|
||||
Passes =[]
|
||||
for x in passes:
|
||||
temp = Pass()
|
||||
temp.id = x["id"]
|
||||
temp.start = datetime.strptime(x["start"],'%Y-%m-%dT%H:%M:%Sz')
|
||||
temp.end = datetime.strptime(x["end"],'%Y-%m-%dT%H:%M:%Sz')
|
||||
temp.ground_station = x["ground_station"]
|
||||
temp.transmitter = x["transmitter"]
|
||||
temp.norad = str(x["norad_cat_id"])
|
||||
try:
|
||||
temp.satellite = requests.get("https://db.satnogs.org/api/satellites/"+str(x["norad_cat_id"])).json()
|
||||
except:
|
||||
temp.satellite = {"name":""}
|
||||
Passes.append(temp)
|
||||
|
||||
|
||||
|
||||
return Passes
|
||||
|
||||
|
||||
def GetGroundStations():
|
||||
stationsR = requests.get("https://network.satnogs.org/api/stations/")
|
||||
stations = stationsR.json()
|
||||
while stationsR.links.has_key("next"):
|
||||
stationsR = requests.get(stationsR.links["next"]["url"])
|
||||
stations += stationsR.json()
|
||||
|
||||
for x in stations:
|
||||
if x["last_seen"] == None:
|
||||
stations.remove(x)
|
||||
continue
|
||||
|
||||
if datetime.strptime(x["last_seen"],'%Y-%m-%dT%H:%M:%Sz') < (datetime.utcnow()- timedelta(10,0,0,0)):
|
||||
stations.remove(x)
|
||||
for x in stations:
|
||||
if x["last_seen"] == None:
|
||||
stations.remove(x)
|
||||
continue
|
||||
|
||||
if datetime.strptime(x["last_seen"],'%Y-%m-%dT%H:%M:%Sz') < (datetime.utcnow()- timedelta(10,0,0,0)):
|
||||
stations.remove(x)
|
||||
|
||||
|
||||
print("Getting Ground Stations")
|
||||
stations = get_paginated_endpoint("https://network.satnogs.org/api/stations/")
|
||||
return stations
|
||||
|
||||
@scheduler.scheduled_job('interval',days=5)
|
||||
def updateTransmitters():
|
||||
global Transmitters
|
||||
print "Updating Transmitters"
|
||||
temp = requests.get("https://db.satnogs.org/api/transmitters/").json()
|
||||
for x in temp:
|
||||
if str(x["norad_cat_id"]) in Transmitters.keys():
|
||||
Transmitters[str(x["norad_cat_id"])][x["uuid"]] = [x["description"],"#"+str("%06x" % random.randint(0, 0xFFFFFF))]
|
||||
else:
|
||||
Transmitters[str(x["norad_cat_id"])]={}
|
||||
Transmitters[str(x["norad_cat_id"])][x["uuid"]] = [x["description"],"#"+str("%06x" % random.randint(0, 0xFFFFFF))]
|
||||
#print Transmitters
|
||||
|
||||
@scheduler.scheduled_job('interval',minutes=3)
|
||||
def updatePasses():
|
||||
global Passes
|
||||
global Occuring_sats
|
||||
print "Updating Passes"
|
||||
Passes = getActive()
|
||||
Occuring_sats = {}
|
||||
for x in Passes:
|
||||
if x.satellite['norad_cat_id'] not in TLEs.keys():
|
||||
q = fetch_tle_of_observation(x.id)
|
||||
TLEs[ x.norad ] = [str(x.satellite["name"]),str(q[0]),str(q[1])]
|
||||
Occuring_sats[x.norad] = TLEs[x.norad]
|
||||
|
||||
@scheduler.scheduled_job('interval',hours=1)
|
||||
def updateStations():
|
||||
global Stations
|
||||
print "Updating Stations"
|
||||
Stations = GetGroundStations()
|
||||
|
||||
@scheduler.scheduled_job('interval',days=1)
|
||||
|
||||
|
||||
def updateTLE():
|
||||
print "Updating TLE"
|
||||
global TlEs
|
||||
sats = fetch_satellites(None,DB_BASE_URL)
|
||||
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 tles.items():
|
||||
TLEs[norad_id] = [str(tle[0]),str(tle[1]),str(tle[2])]
|
||||
for norad_id, (source, tle) in tqdm(tles.items()):
|
||||
TLEs[norad_id] = [str(tle[0]), str(tle[1]), str(tle[2])]
|
||||
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=5)
|
||||
def updateTransmitters():
|
||||
global Transmitters
|
||||
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]]
|
||||
|
||||
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', hours=1)
|
||||
def updatePasses():
|
||||
getFuture()
|
||||
|
||||
|
||||
@scheduler.scheduled_job('interval', hours=1)
|
||||
def updateStations():
|
||||
global Stations
|
||||
print("Updating Stations")
|
||||
Stations = GetGroundStations()
|
||||
|
||||
|
||||
@scheduler.scheduled_job('interval', minutes=30)
|
||||
def updateCZML():
|
||||
global CZML
|
||||
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"]["step"] = "SYSTEM_CLOCK"
|
||||
CZML.append(doc)
|
||||
|
||||
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"]
|
||||
CZML.append(station)
|
||||
|
||||
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
|
||||
sat["billboard"] = {"image": "static/sat.png", "scale": 0.50}
|
||||
sat["position"] = {}
|
||||
sat["position"]["cartographicDegrees"] = []
|
||||
sat["description"] = SatDescrip[x]
|
||||
temp = y["start"]
|
||||
satObj = EarthSatellite(TLEs[x][1], TLEs[x][2], TLEs[x][0])
|
||||
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
|
||||
sat["position"]["cartographicDegrees"].extend([time, lng, lat, elevation])
|
||||
time += 60
|
||||
temp = temp + timedelta(minutes=1)
|
||||
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"] + timedelta(minutes=1)).isoformat()+"Z").replace("+00:00", ""), "boolean": True}, "width": 2, "material": {"solidColor": {"color": {"rgba": [0, 255, 0, 255]}}}, "leadTime": 100000, "trailTime": 100000}
|
||||
CZML.append(sat)
|
||||
for x in tqdm(Sats.keys()):
|
||||
for y in Sats[x]:
|
||||
sat = {}
|
||||
sat["id"] = str(y["id"])+"Link"
|
||||
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)
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def map_view():
|
||||
return render_template("map.html")
|
||||
|
||||
@app.route('/active_stations')
|
||||
def api_active_stations():
|
||||
sations = []
|
||||
def index():
|
||||
Usage.update([str(request.headers.get("CF-IPCountry"))])
|
||||
return render_template("index.html",url="/czml")
|
||||
|
||||
@app.route("/rotating")
|
||||
def rotating():
|
||||
speed = request.args.get('speed', default = '30', type = str)
|
||||
Usage.update([str(request.headers.get("CF-IPCountry"))])
|
||||
return render_template("rotating.html",url="/czml",speed=speed)
|
||||
|
||||
@app.route("/station/<int:station_id>")
|
||||
def index_station(station_id):
|
||||
Usage.update(request.headers.get("CF-IPCountry"))
|
||||
return render_template("index.html",url="/czml/"+str(station_id))
|
||||
|
||||
@app.route('/future_sats')
|
||||
def api_future_sats():
|
||||
return json.dumps(Sats)
|
||||
|
||||
|
||||
@app.route("/czml")
|
||||
def api_czml():
|
||||
return json.dumps(CZML)
|
||||
|
||||
|
||||
@app.route("/broken")
|
||||
def api_broken():
|
||||
output = defaultdict(list)
|
||||
for x in broken.keys():
|
||||
output[x]=list(broken[x])
|
||||
return json.dumps(output)
|
||||
|
||||
@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:
|
||||
sations.append({'id':x['id'],'name':x['name'],'lat_lng':[x["lat"],x['lng']]})
|
||||
return json.dumps(sations)
|
||||
|
||||
@app.route('/stations_from_sat/<string:norad>')
|
||||
def api_occuring_observations(norad):
|
||||
obs = []
|
||||
trans = []
|
||||
for x in Passes:
|
||||
if x.norad == norad:
|
||||
obs.append([x.ground_station,Transmitters[norad][x.transmitter][1]])
|
||||
trans.append(x.transmitter)
|
||||
#print Transmitters[norad].values()
|
||||
|
||||
transList = []
|
||||
for x in set(trans):
|
||||
transList.append(Transmitters[norad][x])
|
||||
#print transList,norad
|
||||
return json.dumps([obs,transList])
|
||||
|
||||
@app.route('/occuring_sats')
|
||||
def api_occuring_sats():
|
||||
return json.dumps(Occuring_sats)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
sat["position"]["cartographicDegrees"].extend([time, lng, lat, elevation])
|
||||
time += 60
|
||||
temp = temp + timedelta(minutes=1)
|
||||
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"] + timedelta(minutes=1)).isoformat() + "Z").replace("+00:00", ""), "boolean": True}, "width": 2, "material": {"solidColor": {"color": {"rgba": [0, 255, 0, 255]}}}, "leadTime": 100000, "trailTime": 100000}
|
||||
czml.append(sat)
|
||||
for y in StationsPasses[station_id]:
|
||||
sat = {}
|
||||
sat["id"] = str(y["id"])+"Link"
|
||||
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)
|
||||
return json.dumps(czml)
|
||||
|
||||
|
||||
|
||||
|
||||
updatePasses()
|
||||
updateStations()
|
||||
updateTLE()
|
||||
updateTransmitters()
|
||||
getFuture()
|
||||
updateCZML()
|
||||
scheduler.start()
|
||||
app.run(use_reloader=False,host = "0.0.0.0",port=5001)
|
||||
app.run(use_reloader=False, host="0.0.0.0", port=5001)
|
||||
|
|
Binary file not shown.
|
@ -1,48 +0,0 @@
|
|||
self.importScripts("satellite.js");
|
||||
|
||||
norad = ""
|
||||
groundStations = []
|
||||
TLE = []
|
||||
|
||||
onmessage = function(e) {
|
||||
norad = e.data[0]
|
||||
TLE = e.data[1]
|
||||
getStations()
|
||||
}
|
||||
|
||||
setInterval(function(){
|
||||
getStations()
|
||||
}, 20000);
|
||||
|
||||
setInterval(function(){
|
||||
var satrec = self.satellite_js.twoline2satrec(TLE[1],TLE[2]);
|
||||
var gmst = self.satellite_js.gstime(new Date());
|
||||
var positionAndVelocity = self.satellite_js.propagate(satrec, new Date());
|
||||
var positionEci = positionAndVelocity.position
|
||||
var positionGd = self.satellite_js.eciToGeodetic(positionEci, gmst)
|
||||
var longitude = positionGd.longitude
|
||||
var latitude = positionGd.latitude
|
||||
|
||||
if (groundStations[1] == undefined){
|
||||
groundStations.push([])
|
||||
}
|
||||
|
||||
postMessage([norad,TLE[0],[degress(latitude),degress(longitude)],groundStations[0],groundStations[1]])
|
||||
|
||||
|
||||
}, 500);
|
||||
|
||||
function getStations(){
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
groundStations = JSON.parse(this.responseText);
|
||||
}
|
||||
};
|
||||
xhttp.open("GET", "/stations_from_sat/"+norad, true);
|
||||
xhttp.send();
|
||||
}
|
||||
|
||||
function degress (radians) {
|
||||
return radians * 180 / Math.PI;
|
||||
};
|
Binary file not shown.
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.5 KiB |
File diff suppressed because one or more lines are too long
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,45 @@
|
|||
<head>
|
||||
<script src="https://cesiumjs.org/releases/1.53/Build/Cesium/Cesium.js"></script>
|
||||
<link href="https://cesiumjs.org/releases/1.53/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<style>
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
<div id="cesiumContainer" style="width: 100%; height:100%"></div>
|
||||
<script>
|
||||
|
||||
|
||||
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIwYmEwOTc5YS01N2I3LTRhYmQtOGE0MS1lNTkyMWU2ZmM3YWUiLCJpZCI6Njg1OCwic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTU0NzU5ODkxOX0.p4alNyuyt_Eufuo8xX_SB6HKHuSixBTxgiVpD6UGI3Y';
|
||||
var viewer = new Cesium.Viewer('cesiumContainer',{shadows:true,timeline:false,vrButton:false,homeButton:false,animation:true,baseLayerPicker:true});
|
||||
viewer.terrainProvider = Cesium.createWorldTerrain();
|
||||
viewer.scene.globe.shadows=Cesium.ShadowMode.CAST_ONLY
|
||||
viewer.scene.globe.enableLighting = true
|
||||
viewer.dataSources.add(Cesium.CzmlDataSource.load("{{ url }}"))
|
||||
var now = new Cesium.JulianDate();
|
||||
viewer.clock.currentTime = now;
|
||||
viewer.clock.shouldAnimate = true
|
||||
|
||||
|
||||
setInterval(function(){
|
||||
viewer.dataSources.removeAll()
|
||||
viewer.dataSources.add(Cesium.CzmlDataSource.load("{{ url }}"))
|
||||
|
||||
|
||||
}, 1000*60*60);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>
|
|
@ -1,195 +0,0 @@
|
|||
<head>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.4/dist/leaflet.css"
|
||||
integrity="sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA=="
|
||||
crossorigin=""/>
|
||||
<script src="https://unpkg.com/leaflet@1.3.4/dist/leaflet.js"
|
||||
integrity="sha512-nMMmRyTVoLYqjP9hrbed9S+FzjZHW5gY1TWCHA5ckwXZBadntCNs8kEqAWdrb9O7rxbCaA4lKTIWjDXZxflOcA=="
|
||||
crossorigin=""></script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
||||
<script src=" https://unpkg.com/@joergdietrich/leaflet.terminator@1.0.0/L.Terminator.js"></script>
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<style>
|
||||
#mapid { height: 700px; }
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
<div class="container">
|
||||
<div class="row"> .
|
||||
<div class="col-md-12">
|
||||
<center>
|
||||
<img src="/static/satnogs-net-logo.png">
|
||||
<br>
|
||||
<br>
|
||||
<div id="mapid" ></div>
|
||||
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
test = null
|
||||
var light_sat = L.icon({
|
||||
iconUrl: 'static/satellite-marker-light.png',
|
||||
iconSize: [40, 40],
|
||||
iconAnchor: [20, 20],
|
||||
popupAnchor: [0, 0],
|
||||
|
||||
});
|
||||
|
||||
var dark_sat = L.icon({
|
||||
iconUrl: 'static/satellite-marker-dark.png',
|
||||
iconSize: [40, 40],
|
||||
iconAnchor: [20, 20],
|
||||
popupAnchor: [0, 0],
|
||||
|
||||
});
|
||||
|
||||
var station = L.icon({
|
||||
iconUrl: 'static/station-marker.png',
|
||||
iconSize: [10, 10],
|
||||
iconAnchor: [5, 5],
|
||||
popupAnchor: [0, 0],
|
||||
|
||||
});
|
||||
|
||||
var active_station = L.icon({
|
||||
iconUrl: 'static/active-station-marker.png',
|
||||
iconSize: [20, 20],
|
||||
iconAnchor: [10, 10],
|
||||
popupAnchor: [0, 0],
|
||||
|
||||
});
|
||||
|
||||
var mymap = L.map('mapid',{worldCopyJump:true}).setView([0,0], 2);
|
||||
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
|
||||
attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
|
||||
maxZoom: 18,
|
||||
id: 'mapbox.streets',
|
||||
accessToken: 'pk.eyJ1IjoiY2hpYmlsbCIsImEiOiJjamxsNHBuZG4wdG1uM3FwYTN5c2ZubmxrIn0.ghkx6AngBzUiZQWBAWKziQ'
|
||||
}).addTo(mymap);
|
||||
var t = L.terminator();
|
||||
t.addTo(mymap);
|
||||
setInterval(function(){updateTerminator(t)}, 500);
|
||||
function updateTerminator(t) {
|
||||
var t2 = L.terminator();
|
||||
t.setLatLngs(t2.getLatLngs());
|
||||
t.redraw();
|
||||
}
|
||||
|
||||
Stations = {}
|
||||
Workers = {}
|
||||
Lines = {}
|
||||
Sats = {}
|
||||
|
||||
dataS = null
|
||||
function UpdateMap(e) {
|
||||
var norad = e.data[0]
|
||||
var name = e.data[1]
|
||||
var satPos = e.data[2]
|
||||
if (norad in Sats){
|
||||
Sats[norad]._latlng = {"lat":satPos[0],"lng":satPos[1]}
|
||||
Sats[norad].update()
|
||||
}else{
|
||||
Sats[norad] = L.marker(satPos,{icon: dark_sat,zIndexOffset:1000}).addTo(mymap);
|
||||
Lines[norad] = {}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
popupText = "<b>Name: "+name+"</b><br><b>Norad: "+norad+"</b><br><b>Station Count: "+e.data[3].length+"</b><br>"
|
||||
|
||||
e.data[4].forEach(function(x){
|
||||
//console.log(x + " "+norad)
|
||||
popupText = popupText + '<div class="trans" style="background-color:'+x[1]+'";>'+x[0]+"</div>"
|
||||
})
|
||||
Sats[norad].bindPopup(popupText)
|
||||
Sats[norad]._popup.setContent(popupText)
|
||||
|
||||
temp = []
|
||||
e.data[3].forEach(function(x){
|
||||
temp.push(x[0])
|
||||
})
|
||||
|
||||
e.data[3].forEach(function(x){
|
||||
if (Object.keys(Lines[norad]).includes(String(x[0]))){
|
||||
Object.keys(Lines[norad]).forEach(function(y){
|
||||
Lines[norad][y]._latlngs[1]= {"lat":satPos[0],"lng":satPos[1]}
|
||||
|
||||
})}else{Lines[norad][x[0]] = new L.Polyline([[Stations[x[0]]._latlng.lat,Stations[x[0]]._latlng.lng],[satPos[0],satPos[1]]], {color: x[1],weight: 3,opacity: 1,smoothFactor: 1});
|
||||
Lines[norad][x[0]].addTo(mymap)};
|
||||
})
|
||||
Object.keys(Lines[norad]).forEach(function(x){
|
||||
if (temp.includes(Number(x))){
|
||||
|
||||
}else{
|
||||
Lines[norad][x].removeFrom(mymap)
|
||||
delete Lines[norad][x]
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
$.get("/active_stations", function(data, status){
|
||||
data = JSON.parse(data)
|
||||
dataS = data
|
||||
data.forEach(function(x){
|
||||
|
||||
marker = L.marker(x["lat_lng"],{icon: station,zIndexOffset:-1000}).addTo(mymap);
|
||||
marker.bindPopup("<b>Name: "+x['name']+"</b>")
|
||||
Stations[x["id"]] = marker
|
||||
});
|
||||
});
|
||||
|
||||
$.get("/occuring_sats", function(data, status){
|
||||
data = JSON.parse(data)
|
||||
Object.keys(data).forEach(function(x){
|
||||
worker = new Worker('/static/Worker.js');
|
||||
worker.onmessage = UpdateMap
|
||||
worker.postMessage([x,data[x]]);
|
||||
Workers[x] =worker
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
setInterval(function(){
|
||||
$.get("/occuring_sats", function(data, status){
|
||||
data = JSON.parse(data)
|
||||
Object.keys(data).forEach(function(x){
|
||||
if (x in Workers){
|
||||
}else{
|
||||
worker = new Worker('/static/Worker.js');
|
||||
worker.onmessage = UpdateMap
|
||||
worker.postMessage([x,data[x]]);
|
||||
Workers[x] =worker
|
||||
}
|
||||
});
|
||||
Object.keys(Workers).forEach(function(x){
|
||||
if (Object.keys(data).includes(x)){
|
||||
}else{
|
||||
Workers[x].terminate()
|
||||
delete Workers[x]
|
||||
UpdateMap({"data":[x,"",[0,0],[],[]]})
|
||||
Sats[x].removeFrom(mymap)
|
||||
delete Sats[x]
|
||||
delete Lines[x]
|
||||
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
}, 20000);
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>
|
|
@ -0,0 +1,69 @@
|
|||
<head>
|
||||
<script src="https://cesiumjs.org/releases/1.53/Build/Cesium/Cesium.js"></script>
|
||||
<link href="https://cesiumjs.org/releases/1.53/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<style>
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
<div id="cesiumContainer" style="width: 100%; height:100%"></div>
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIwYmEwOTc5YS01N2I3LTRhYmQtOGE0MS1lNTkyMWU2ZmM3YWUiLCJpZCI6Njg1OCwic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTU0NzU5ODkxOX0.p4alNyuyt_Eufuo8xX_SB6HKHuSixBTxgiVpD6UGI3Y';
|
||||
var viewer = new Cesium.Viewer('cesiumContainer',{shadows:true,timeline:false,vrButton:false,homeButton:false,animation:true,baseLayerPicker:true});
|
||||
viewer.terrainProvider = Cesium.createWorldTerrain();
|
||||
viewer.scene.globe.shadows=Cesium.ShadowMode.CAST_ONLY
|
||||
viewer.scene.globe.enableLighting = true
|
||||
viewer.dataSources.add(Cesium.CzmlDataSource.load("{{ url }}"))
|
||||
var now = new Cesium.JulianDate();
|
||||
viewer.clock.currentTime = now;
|
||||
viewer.clock.shouldAnimate = true
|
||||
temp = 0
|
||||
function icrf(scene, time) {
|
||||
if (scene.mode !== Cesium.SceneMode.SCENE3D) {
|
||||
return;
|
||||
}
|
||||
t = Object()
|
||||
t.dayNumber = time.dayNumber
|
||||
t.secondsOfDay = temp
|
||||
|
||||
icrfToFixed = Cesium.Transforms.computeIcrfToFixedMatrix(t);
|
||||
if (Cesium.defined(icrfToFixed)) {
|
||||
var camera = viewer.camera;
|
||||
var offset = Cesium.Cartesian3.clone(camera.position);
|
||||
var transform = Cesium.Matrix4.fromRotationTranslation(icrfToFixed);
|
||||
camera.lookAtTransform(transform, offset);
|
||||
}
|
||||
|
||||
temp+={{speed}}
|
||||
if (temp > 86400){
|
||||
temp = 0
|
||||
}
|
||||
}
|
||||
|
||||
viewer.scene.postUpdate.addEventListener(icrf);
|
||||
|
||||
setInterval(function(){
|
||||
viewer.dataSources.removeAll()
|
||||
viewer.dataSources.add(Cesium.CzmlDataSource.load("{{ url }}"))
|
||||
|
||||
|
||||
}, 1000*60*60);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>
|
Loading…
Reference in New Issue