diff --git a/README.md b/README.md index 6182e59..9b98d2f 100644 --- a/README.md +++ b/README.md @@ -25,5 +25,7 @@ http://127.0.0.1:5001/station/ replacing with the id of Events it was displayed by someone at: Athens Science Fest http://www.athens-science-festival.gr + The Aeronautics and Space Engineering event http://cosmos.uca.es/ - A talk about Satnogs https://community.libre.space/t/feedback-about-my-talk-about-satnogs-yesterday-in-grenoble/3783 + + A talk about Satnogs https://community.libre.space/t/feedback-about-my-talk-about-satnogs-yesterday-in-grenoble/3783 \ No newline at end of file diff --git a/requirments.txt b/requirments.txt index 9fb41b9..a99cd1c 100644 --- a/requirments.txt +++ b/requirments.txt @@ -1,6 +1,7 @@ -requests==2.21.0 -flask==1.0.2 -apscheduler==3.5.3 -satellitetle==0.5.1 -tqdm==4.31.1 -skyfield==1.10 +requests>=2.21.0 +flask>=1.0.2 +apscheduler>=3.5.0 +satellitetle>=0.5.1 +tqdm>=4.30.0 +skyfield>=1.10 +numpy >=1.16 diff --git a/satnogs.py b/satnogs.py index 0c7d98b..0a4b52a 100644 --- a/satnogs.py +++ b/satnogs.py @@ -3,13 +3,14 @@ import requests from tqdm import tqdm from flask import Flask , render_template,redirect,url_for, request import json -from collections import defaultdict, Counter +from collections import defaultdict import random from apscheduler.schedulers.background import BackgroundScheduler 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 +import numpy scheduler = BackgroundScheduler() app = Flask(__name__) @@ -17,23 +18,23 @@ app = Flask(__name__) ts = load.timescale() -broken = defaultdict(set) - - -Usage = Counter() -Sats = defaultdict(list) +Observations = defaultdict(list) Passes = defaultdict(list) Stations = [] +StationsByID = {} TLEs = defaultdict(list) Transmitters = defaultdict(dict) StationsPasses = defaultdict(list) SatDescrip = defaultdict(str) -CZML = [] +CZMLOnline = [] +CZMLTesting = [] +CZMLOffline = [] + def getFuture(): print("Getting future Passes") - global Sats + global Observations global TLEs observations = defaultdict(dict) start = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S%z') @@ -43,7 +44,7 @@ def getFuture(): 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) + Observations = defaultdict(list) for x in tqdm(passes): if x["id"] in observations: try: @@ -52,21 +53,41 @@ def getFuture(): 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"]}) + Observations[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.") + + 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 @@ -112,17 +133,19 @@ def updateStations(): @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) - + 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": @@ -146,10 +169,16 @@ def updateCZML(): station["description"] = "ID: "+str(x["id"]) + "
Total Observations: " station["description"] += str(x["observations"]) + "
Status: " + x["status"] + "
QTH: " station["description"] += x["qthlocator"] + "
Description: " + x["description"] - CZML.append(station) - - for x in tqdm(Sats.keys()): - for y in Sats[x]: + 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][0] @@ -161,59 +190,71 @@ def updateCZML(): 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) + + while temp <= y["end"] + timedelta(minutes=1): + 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: + 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(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"]}} + 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(): - 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/") 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) + return json.dumps(CZMLOnline) +@app.route("/czmloff") +def api_czml1(): + return json.dumps(CZMLOffline) -@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("/czmltest") +def api_czml2(): + return json.dumps(CZMLTesting) @app.route("/czml/") def api_station(station_id): @@ -253,7 +294,7 @@ def api_station(station_id): station["description"] += x["qthlocator"] + "
Description: " + x["description"] czml.append(station) break - + AliveSats = [] for y in StationsPasses[station_id]: sat = {} sat["id"] = str(y["id"]) @@ -269,24 +310,31 @@ def api_station(station_id): 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) + + + while temp <= y["end"]+timedelta(minutes=1): + 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]: - 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) + 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(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) @@ -295,4 +343,4 @@ 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=5000) diff --git a/templates/index.html b/templates/index.html index 246b941..e63abd5 100644 --- a/templates/index.html +++ b/templates/index.html @@ -7,39 +7,78 @@
+
+ Online Stations?
+ Offline Stations?
+ Testing Stations?
+ -Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License +Creative Commons License
This visual work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License Source Code is licensed under the MIT license. Produced by William Gaylord KD9KCK diff --git a/templates/rotating.html b/templates/rotating.html index ad2dec6..a0dc861 100644 --- a/templates/rotating.html +++ b/templates/rotating.html @@ -7,26 +7,48 @@
- +
+ Online Stations?
+ Offline Stations?
+ Testing Stations?
+ -Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License + +Creative Commons License
This visual work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License Source Code is licensed under the MIT license. Produced by William Gaylord KD9KCK +