Refactor SatNOGS API calls into an own module
Eventually this module will be replace by the offical SatNOGS API client.merge-requests/50/head
parent
0cbf664991
commit
3e69cfdcd7
6
cache.py
6
cache.py
|
@ -3,9 +3,9 @@ import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from satellite_tle import fetch_tles
|
from satellite_tle import fetch_tles
|
||||||
from utils import get_active_transmitter_info, \
|
from satnogs_client import get_active_transmitter_info, \
|
||||||
get_satellite_info, \
|
get_satellite_info, \
|
||||||
get_transmitter_stats
|
get_transmitter_stats
|
||||||
|
|
||||||
|
|
||||||
class CacheManager:
|
class CacheManager:
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
import requests
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
import lxml
|
||||||
|
import settings
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def get_paginated_endpoint(url, max_entries=None):
|
||||||
|
r = requests.get(url=url)
|
||||||
|
r.raise_for_status()
|
||||||
|
|
||||||
|
data = r.json()
|
||||||
|
|
||||||
|
while 'next' in r.links and (not max_entries or len(data) < max_entries):
|
||||||
|
next_page_url = r.links['next']['url']
|
||||||
|
|
||||||
|
r = requests.get(url=next_page_url)
|
||||||
|
r.raise_for_status()
|
||||||
|
|
||||||
|
data.extend(r.json())
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def get_satellite_info():
|
||||||
|
# Open session
|
||||||
|
logging.info("Fetching satellite information from DB.")
|
||||||
|
r = requests.get('{}/api/satellites'.format(settings.DB_BASE_URL))
|
||||||
|
logging.info("Satellites received!")
|
||||||
|
|
||||||
|
# Select alive satellites
|
||||||
|
norad_cat_ids = []
|
||||||
|
for o in r.json():
|
||||||
|
if o["status"] == "alive":
|
||||||
|
norad_cat_ids.append(o["norad_cat_id"])
|
||||||
|
|
||||||
|
return norad_cat_ids
|
||||||
|
|
||||||
|
|
||||||
|
def get_active_transmitter_info(fmin, fmax):
|
||||||
|
# Open session
|
||||||
|
logging.info("Fetching transmitter information from DB.")
|
||||||
|
r = requests.get('{}/api/transmitters'.format(settings.DB_BASE_URL))
|
||||||
|
logging.info("Transmitters received!")
|
||||||
|
|
||||||
|
# Loop
|
||||||
|
transmitters = []
|
||||||
|
for o in r.json():
|
||||||
|
if o["downlink_low"]:
|
||||||
|
if o["status"] == "active" and o["downlink_low"] > fmin and o["downlink_low"] <= fmax:
|
||||||
|
transmitter = {"norad_cat_id": o["norad_cat_id"], "uuid": o["uuid"], "mode": o["mode"]}
|
||||||
|
transmitters.append(transmitter)
|
||||||
|
logging.info("Transmitters filtered based on ground station capability.")
|
||||||
|
return transmitters
|
||||||
|
|
||||||
|
|
||||||
|
def get_transmitter_stats():
|
||||||
|
logging.debug("Requesting transmitter success rates for all satellite")
|
||||||
|
transmitters = get_paginated_endpoint('{}/api/transmitters/'.format(settings.NETWORK_BASE_URL))
|
||||||
|
return transmitters
|
||||||
|
|
||||||
|
|
||||||
|
def get_scheduled_passes_from_network(ground_station, tmin, tmax):
|
||||||
|
# Get first page
|
||||||
|
client = requests.session()
|
||||||
|
|
||||||
|
# Loop
|
||||||
|
start = True
|
||||||
|
scheduledpasses = []
|
||||||
|
|
||||||
|
logging.info("Requesting scheduled passes for ground station %d" % ground_station)
|
||||||
|
# Fetch observations until the time of the end of the last fetched observation happends to be
|
||||||
|
# before the start time of the selected timerange for scheduling
|
||||||
|
# NOTE: This algorithm is based on the order in which the API returns the observations, i.e.
|
||||||
|
# most recent observations are returned at first!
|
||||||
|
while True:
|
||||||
|
if start:
|
||||||
|
r = client.get('{}/api/observations/?ground_station={:d}'.format(
|
||||||
|
settings.NETWORK_BASE_URL, ground_station))
|
||||||
|
start = False
|
||||||
|
else:
|
||||||
|
nextpage = r.links.get("next")
|
||||||
|
r = client.get(nextpage["url"])
|
||||||
|
|
||||||
|
if not r.json():
|
||||||
|
# Ground station has no observations yet
|
||||||
|
break
|
||||||
|
|
||||||
|
# r.json() is a list of dicts/observations
|
||||||
|
for o in r.json():
|
||||||
|
satpass = {
|
||||||
|
"id": o['norad_cat_id'],
|
||||||
|
"tr": datetime.strptime(o['start'].replace("Z", ""), "%Y-%m-%dT%H:%M:%S"),
|
||||||
|
"ts": datetime.strptime(o['end'].replace("Z", ""), "%Y-%m-%dT%H:%M:%S"),
|
||||||
|
"scheduled": True,
|
||||||
|
"altt": o['max_altitude'],
|
||||||
|
"priority": 1,
|
||||||
|
"uuid": o['transmitter'],
|
||||||
|
"name": '',
|
||||||
|
"mode": ''
|
||||||
|
}
|
||||||
|
|
||||||
|
if satpass['ts'] > tmin and satpass['tr'] < tmax:
|
||||||
|
# Only store observations which are during the ROI for scheduling
|
||||||
|
scheduledpasses.append(satpass)
|
||||||
|
|
||||||
|
if satpass['ts'] < tmin:
|
||||||
|
# Last fetched observation is older than the ROI for scheduling, end loop.
|
||||||
|
break
|
||||||
|
|
||||||
|
logging.info("Scheduled passes for ground station %d retrieved!" % ground_station)
|
||||||
|
return scheduledpasses
|
||||||
|
|
||||||
|
|
||||||
|
def get_groundstation_info(ground_station_id, allow_testing):
|
||||||
|
|
||||||
|
logging.info("Requesting information for ground station %d" % ground_station_id)
|
||||||
|
|
||||||
|
# Loop
|
||||||
|
r = requests.get("{}/api/stations/?id={:d}".format(settings.NETWORK_BASE_URL,
|
||||||
|
ground_station_id))
|
||||||
|
|
||||||
|
selected_stations = list(filter(lambda s: s['id'] == ground_station_id, r.json()))
|
||||||
|
|
||||||
|
if not selected_stations:
|
||||||
|
logging.info('No ground station information found!')
|
||||||
|
# Exit if no ground station found
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
logging.info('Ground station information retrieved!')
|
||||||
|
station = selected_stations[0]
|
||||||
|
|
||||||
|
if station['status'] == 'Online' or (station['status'] == 'Testing' and allow_testing):
|
||||||
|
return station
|
||||||
|
else:
|
||||||
|
if station['status'] == 'Testing' and not allow_testing:
|
||||||
|
logging.info("Ground station {} is in testing mode but auto-scheduling is not "
|
||||||
|
"allowed. Use -T command line argument to enable scheduling.".format(ground_station_id))
|
||||||
|
else:
|
||||||
|
logging.info("Ground station {} neither in 'online' nor in 'testing' mode, "
|
||||||
|
"can't schedule!".format(ground_station_id))
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def schedule_observation(session, norad_cat_id, uuid, ground_station_id, starttime, endtime):
|
||||||
|
|
||||||
|
obsURL = '{}/observations/new/'.format(settings.NETWORK_BASE_URL) # Observation URL
|
||||||
|
# Get the observation/new/ page to get the CSFR token
|
||||||
|
obs = session.get(obsURL)
|
||||||
|
obs_html = lxml.html.fromstring(obs.text)
|
||||||
|
hidden_inputs = obs_html.xpath(r'//form//input[@type="hidden"]')
|
||||||
|
form = {x.attrib["name"]: x.attrib["value"] for x in hidden_inputs}
|
||||||
|
form["obs-0-transmitter_uuid"] = uuid
|
||||||
|
form["obs-0-start"] = starttime
|
||||||
|
form["obs-0-end"] = endtime
|
||||||
|
form["obs-0-ground_station"] = ground_station_id
|
||||||
|
form["obs-TOTAL_FORMS"] = str(1)
|
||||||
|
form["obs-INITIAL_FORMS"] = str(0)
|
||||||
|
session.post(obsURL, data=form, headers={'referer': obsURL})
|
||||||
|
logging.debug("Scheduled!")
|
|
@ -7,16 +7,19 @@ import os
|
||||||
import lxml.html
|
import lxml.html
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
from utils import get_groundstation_info, \
|
from utils import read_priorities_transmitters, \
|
||||||
get_scheduled_passes_from_network, \
|
|
||||||
schedule_observation, \
|
|
||||||
read_priorities_transmitters, \
|
|
||||||
get_priority_passes
|
get_priority_passes
|
||||||
from auto_scheduler import Twolineelement, Satellite
|
from auto_scheduler import Twolineelement, Satellite
|
||||||
from auto_scheduler.pass_predictor import find_passes
|
from auto_scheduler.pass_predictor import find_passes
|
||||||
from auto_scheduler.schedulers import ordered_scheduler, \
|
from auto_scheduler.schedulers import ordered_scheduler, \
|
||||||
report_efficiency
|
report_efficiency
|
||||||
from cache import CacheManager
|
from cache import CacheManager
|
||||||
|
from satnogs_client import get_active_transmitter_info, \
|
||||||
|
get_groundstation_info, \
|
||||||
|
get_satellite_info, \
|
||||||
|
get_scheduled_passes_from_network, \
|
||||||
|
get_transmitter_stats, \
|
||||||
|
schedule_observation
|
||||||
import settings
|
import settings
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
import sys
|
import sys
|
||||||
|
|
|
@ -5,7 +5,7 @@ import logging
|
||||||
import settings
|
import settings
|
||||||
|
|
||||||
from cache import CacheManager
|
from cache import CacheManager
|
||||||
from utils import get_groundstation_info
|
from satnogs_client import get_groundstation_info
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
158
utils.py
158
utils.py
|
@ -1,27 +1,6 @@
|
||||||
import requests
|
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime
|
|
||||||
import lxml
|
|
||||||
import settings
|
import settings
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def get_paginated_endpoint(url, max_entries=None):
|
|
||||||
r = requests.get(url=url)
|
|
||||||
r.raise_for_status()
|
|
||||||
|
|
||||||
data = r.json()
|
|
||||||
|
|
||||||
while 'next' in r.links and (not max_entries or len(data) < max_entries):
|
|
||||||
next_page_url = r.links['next']['url']
|
|
||||||
|
|
||||||
r = requests.get(url=next_page_url)
|
|
||||||
r.raise_for_status()
|
|
||||||
|
|
||||||
data.extend(r.json())
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def read_priorities_transmitters(filename):
|
def read_priorities_transmitters(filename):
|
||||||
|
@ -47,96 +26,6 @@ def read_priorities_transmitters(filename):
|
||||||
return ({}, {})
|
return ({}, {})
|
||||||
|
|
||||||
|
|
||||||
def get_satellite_info():
|
|
||||||
# Open session
|
|
||||||
logging.info("Fetching satellite information from DB.")
|
|
||||||
r = requests.get('{}/api/satellites'.format(settings.DB_BASE_URL))
|
|
||||||
logging.info("Satellites received!")
|
|
||||||
|
|
||||||
# Select alive satellites
|
|
||||||
norad_cat_ids = []
|
|
||||||
for o in r.json():
|
|
||||||
if o["status"] == "alive":
|
|
||||||
norad_cat_ids.append(o["norad_cat_id"])
|
|
||||||
|
|
||||||
return norad_cat_ids
|
|
||||||
|
|
||||||
|
|
||||||
def get_active_transmitter_info(fmin, fmax):
|
|
||||||
# Open session
|
|
||||||
logging.info("Fetching transmitter information from DB.")
|
|
||||||
r = requests.get('{}/api/transmitters'.format(settings.DB_BASE_URL))
|
|
||||||
logging.info("Transmitters received!")
|
|
||||||
|
|
||||||
# Loop
|
|
||||||
transmitters = []
|
|
||||||
for o in r.json():
|
|
||||||
if o["downlink_low"]:
|
|
||||||
if o["status"] == "active" and o["downlink_low"] > fmin and o["downlink_low"] <= fmax:
|
|
||||||
transmitter = {"norad_cat_id": o["norad_cat_id"], "uuid": o["uuid"], "mode": o["mode"]}
|
|
||||||
transmitters.append(transmitter)
|
|
||||||
logging.info("Transmitters filtered based on ground station capability.")
|
|
||||||
return transmitters
|
|
||||||
|
|
||||||
|
|
||||||
def get_transmitter_stats():
|
|
||||||
logging.debug("Requesting transmitter success rates for all satellite")
|
|
||||||
transmitters = get_paginated_endpoint('{}/api/transmitters/'.format(settings.NETWORK_BASE_URL))
|
|
||||||
return transmitters
|
|
||||||
|
|
||||||
|
|
||||||
def get_scheduled_passes_from_network(ground_station, tmin, tmax):
|
|
||||||
# Get first page
|
|
||||||
client = requests.session()
|
|
||||||
|
|
||||||
# Loop
|
|
||||||
start = True
|
|
||||||
scheduledpasses = []
|
|
||||||
|
|
||||||
logging.info("Requesting scheduled passes for ground station %d" % ground_station)
|
|
||||||
# Fetch observations until the time of the end of the last fetched observation happends to be
|
|
||||||
# before the start time of the selected timerange for scheduling
|
|
||||||
# NOTE: This algorithm is based on the order in which the API returns the observations, i.e.
|
|
||||||
# most recent observations are returned at first!
|
|
||||||
while True:
|
|
||||||
if start:
|
|
||||||
r = client.get('{}/api/observations/?ground_station={:d}'.format(
|
|
||||||
settings.NETWORK_BASE_URL, ground_station))
|
|
||||||
start = False
|
|
||||||
else:
|
|
||||||
nextpage = r.links.get("next")
|
|
||||||
r = client.get(nextpage["url"])
|
|
||||||
|
|
||||||
if not r.json():
|
|
||||||
# Ground station has no observations yet
|
|
||||||
break
|
|
||||||
|
|
||||||
# r.json() is a list of dicts/observations
|
|
||||||
for o in r.json():
|
|
||||||
satpass = {
|
|
||||||
"id": o['norad_cat_id'],
|
|
||||||
"tr": datetime.strptime(o['start'].replace("Z", ""), "%Y-%m-%dT%H:%M:%S"),
|
|
||||||
"ts": datetime.strptime(o['end'].replace("Z", ""), "%Y-%m-%dT%H:%M:%S"),
|
|
||||||
"scheduled": True,
|
|
||||||
"altt": o['max_altitude'],
|
|
||||||
"priority": 1,
|
|
||||||
"uuid": o['transmitter'],
|
|
||||||
"name": '',
|
|
||||||
"mode": ''
|
|
||||||
}
|
|
||||||
|
|
||||||
if satpass['ts'] > tmin and satpass['tr'] < tmax:
|
|
||||||
# Only store observations which are during the ROI for scheduling
|
|
||||||
scheduledpasses.append(satpass)
|
|
||||||
|
|
||||||
if satpass['ts'] < tmin:
|
|
||||||
# Last fetched observation is older than the ROI for scheduling, end loop.
|
|
||||||
break
|
|
||||||
|
|
||||||
logging.info("Scheduled passes for ground station %d retrieved!" % ground_station)
|
|
||||||
return scheduledpasses
|
|
||||||
|
|
||||||
|
|
||||||
def get_priority_passes(passes, priorities, favorite_transmitters, only_priority, min_priority):
|
def get_priority_passes(passes, priorities, favorite_transmitters, only_priority, min_priority):
|
||||||
priority = []
|
priority = []
|
||||||
normal = []
|
normal = []
|
||||||
|
@ -166,50 +55,3 @@ def get_priority_passes(passes, priorities, favorite_transmitters, only_priority
|
||||||
if satpass['priority'] >= min_priority:
|
if satpass['priority'] >= min_priority:
|
||||||
normal.append(satpass)
|
normal.append(satpass)
|
||||||
return (priority, normal)
|
return (priority, normal)
|
||||||
|
|
||||||
def get_groundstation_info(ground_station_id, allow_testing):
|
|
||||||
|
|
||||||
logging.info("Requesting information for ground station %d" % ground_station_id)
|
|
||||||
|
|
||||||
# Loop
|
|
||||||
r = requests.get("{}/api/stations/?id={:d}".format(settings.NETWORK_BASE_URL,
|
|
||||||
ground_station_id))
|
|
||||||
|
|
||||||
selected_stations = list(filter(lambda s: s['id'] == ground_station_id, r.json()))
|
|
||||||
|
|
||||||
if not selected_stations:
|
|
||||||
logging.info('No ground station information found!')
|
|
||||||
# Exit if no ground station found
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
logging.info('Ground station information retrieved!')
|
|
||||||
station = selected_stations[0]
|
|
||||||
|
|
||||||
if station['status'] == 'Online' or (station['status'] == 'Testing' and allow_testing):
|
|
||||||
return station
|
|
||||||
else:
|
|
||||||
if station['status'] == 'Testing' and not allow_testing:
|
|
||||||
logging.info("Ground station {} is in testing mode but auto-scheduling is not "
|
|
||||||
"allowed. Use -T command line argument to enable scheduling.".format(ground_station_id))
|
|
||||||
else:
|
|
||||||
logging.info("Ground station {} neither in 'online' nor in 'testing' mode, "
|
|
||||||
"can't schedule!".format(ground_station_id))
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
def schedule_observation(session, norad_cat_id, uuid, ground_station_id, starttime, endtime):
|
|
||||||
|
|
||||||
obsURL = '{}/observations/new/'.format(settings.NETWORK_BASE_URL) # Observation URL
|
|
||||||
# Get the observation/new/ page to get the CSFR token
|
|
||||||
obs = session.get(obsURL)
|
|
||||||
obs_html = lxml.html.fromstring(obs.text)
|
|
||||||
hidden_inputs = obs_html.xpath(r'//form//input[@type="hidden"]')
|
|
||||||
form = {x.attrib["name"]: x.attrib["value"] for x in hidden_inputs}
|
|
||||||
form["obs-0-transmitter_uuid"] = uuid
|
|
||||||
form["obs-0-start"] = starttime
|
|
||||||
form["obs-0-end"] = endtime
|
|
||||||
form["obs-0-ground_station"] = ground_station_id
|
|
||||||
form["obs-TOTAL_FORMS"] = str(1)
|
|
||||||
form["obs-INITIAL_FORMS"] = str(0)
|
|
||||||
session.post(obsURL, data=form, headers={'referer': obsURL})
|
|
||||||
logging.debug("Scheduled!")
|
|
||||||
|
|
Loading…
Reference in New Issue