#!/usr/bin/env python import json import requests import ephem import math import random from datetime import datetime, timedelta import matplotlib.pyplot as plt from matplotlib.patches import Rectangle from matplotlib.dates import DayLocator, HourLocator, DateFormatter, drange from matplotlib import colors as mcolors class satellite: """Satellite class""" def __init__(self, tle0, tle1, tle2): """Define a satellite""" self.tle0 = tle0 self.tle1 = tle1 self.tle2 = tle2 if tle0[:2]=="0 ": self.name = tle0[2:] else: self.name = tle0 self.id = tle1.split(" ")[1][:5] def get_scheduled_passes_from_network(ground_station, tmin, tmax): # Get first page client = requests.session() # Loop start = True scheduledpasses = [] while True: if start: r = client.get("https://network.satnogs.org/api/observations/?ground_station=%d"%ground_station) start=False else: nextpage = r.links.get("next") r = client.get(nextpage["url"]) # r.json() is a list of dicts 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} if satpass['ts']>tmin and satpass['tr']=scheduledpass['tr'] and satpass['ts']=satpass['tr'] and scheduledpass['ts']=scheduledpass['tr'] and satpass['tr']=scheduledpass['tr'] and satpass['ts']tmax: tmax=satpass['ts'] # Total time covered dttot = tmax-tmin return dt.total_seconds(),dttot.total_seconds(), dt.total_seconds()/dttot.total_seconds() if __name__ == "__main__": # Settings tlefile = "uhf.txt" observer_longitude = "6.379" observer_latitude = "53.834" observer_elevation = 10 minimum_altitude = 10 ground_station = 40 tmin = datetime.now() tmax = datetime.now()+timedelta(hours=24) # Read satellites with open(tlefile, "r") as f: lines = f.readlines() satellites = [satellite(lines[i], lines[i+1], lines[i+2]) for i in range(0, len(lines), 3)] # Set observer observer = ephem.Observer() observer.lon = observer_longitude observer.lat = observer_latitude observer.elevation = observer_elevation # Loop over satellites passes = [] passid = 0 for satellite in satellites: # Set start time observer.date = ephem.date(tmin) # Load TLE try: sat_ephem = ephem.readtle(str(satellite.tle0), str(satellite.tle1), str(satellite.tle2)) except (ValueError, AttributeError): continue # Loop over passes keep_digging = True while keep_digging: try: tr, azr, tt, altt, ts, azs = observer.next_pass(sat_ephem) except ValueError: break # there will be sats in our list that fall below horizon, skip except TypeError: break # if there happens to be a non-EarthSatellite object in the list except Exception: break if tr is None: break # using the angles module convert the sexagesimal degree into # something more easily read by a human try: elevation = format(math.degrees(altt), '.0f') azimuth_r = format(math.degrees(azr), '.0f') azimuth_s = format(math.degrees(azs), '.0f') except TypeError: break passid += 1 # show only if >= configured horizon and in next 6 hours, # and not directly overhead (tr < ts see issue 199) if tr < ephem.date(tmax): if (float(elevation) >= minimum_altitude and tr < ts): valid = True if tr < ephem.Date(datetime.now() + timedelta(minutes=5)): valid = False satpass = {'passid': passid, 'mytime': str(observer.date), 'name': str(satellite.name), 'id': str(satellite.id), 'tle1': str(satellite.tle1), 'tle2': str(satellite.tle2), 'tr': tr.datetime(), # Rise time 'azr': azimuth_r, # Rise Azimuth 'tt': tt.datetime(), # Max altitude time 'altt': elevation, # Max altitude 'ts': ts.datetime(), # Set time 'azs': azimuth_s, # Set azimuth 'valid': valid, 'scheduled': False} passes.append(satpass) observer.date = ephem.Date(ts).datetime() + timedelta(minutes=1) else: keep_digging = False # Priorities # priorities = {"40069": 1.000, "25338": 0.990, "28654": 0.990, "33591": 0.990, "43017": 0.980, "43137": 0.980, "40967": 0.980, "40074": 0.970, "42017": 0.970, "39444": 0.970, "40903": 0.960, "40906": 0.960, "40907": 0.960, "40909": 0.960, "40910": 0.960, "40911": 0.960, "42778": 0.950, "00965": 0.950, "42759": 0.940, "42761": 0.940, "25544": 0.000} priorities = {"40043": 1.000, "40012": 1.000, "42768": 1.000, "40968": 1.000, "40014": 1.000, "40379": 1.000, "42789": 1.000, "27844": 0.900, "27848": 0.900, "35935": 0.900, "43589": 0.900, "43590": 0.900, "43591": 0.900, "41935": 1.000, "24278": 0.900, "43156": 0.900, "42775": 0.900, "43596": 0.800, "40377": 0.800, "40378": 0.800, "43156": 0.800, "43468": 0.800, "43552": 0.800, "43466": 0.800} # List of scheduled passes # scheduledpasses = [] scheduledpasses = get_scheduled_passes_from_network(ground_station, tmin, tmax) print("Found %d scheduled passes between %s and %s on ground station %d\n"%(len(scheduledpasses), tmin, tmax, ground_station)) # Get passes of priority objects prioritypasses = [] normalpasses = [] for satpass in passes: # Get user defined priorities if satpass['id'] in priorities: satpass['priority'] = priorities[satpass['id']] prioritypasses.append(satpass) else: satpass['priority'] = float(satpass['altt'])/90.0 normalpasses.append(satpass) # Priority scheduler prioritypasses = sorted(prioritypasses, key=lambda satpass: -satpass['priority']) scheduledpasses = ordered_scheduler(prioritypasses, scheduledpasses) # Random scheduler normalpasses = sorted(normalpasses, key=lambda satpass: -satpass['priority']) scheduledpasses = ordered_scheduler(normalpasses, scheduledpasses) dt, dttot, eff = efficiency(scheduledpasses) print("%d passes scheduled out of %d, %.0f s out of %.0f s at %.3f%% efficiency"%(len(scheduledpasses), len(passes), dt, dttot, 100*eff)) # Find unique objects satids = sorted(set([satpass['id'] for satpass in passes])) # Set up figure fig = plt.figure(figsize=(20,len(satids)*0.2)) ax = fig.add_subplot(111) ax.set_xlim(tmin, tmax) ax.set_ylim(-3,len(satids)+1) ax.xaxis.set_major_locator(HourLocator(xrange(0, 25, 3))) ax.xaxis.set_minor_locator(HourLocator(xrange(0, 25, 1))) ax.xaxis.set_major_formatter(DateFormatter('%Y-%m-%d %H:%M:%S')) ax.grid() ax.get_yaxis().set_visible(False) fig.autofmt_xdate(rotation=0, ha='center') plt.xlabel("Time (UTC) for station #%d"%ground_station) # Get list of colors colors = [key for key in mcolors.BASE_COLORS.keys() if key!='w'] # Loop over objects for i, satid in enumerate(satids): plt.text(tmax+timedelta(minutes=5), i-0.25, satid, color=colors[i%len(colors)]) for satpass in passes: if satpass['id']==satid: width = satpass['ts']-satpass['tr'] ax.add_patch(Rectangle((satpass['tr'], i-0.4), width, 0.8, color=colors[i%len(colors)])) # Plot scheduled passes if satpass in scheduledpasses: ax.add_patch(Rectangle((satpass['tr'], -2.4), width, 0.8, color=colors[i%len(colors)])) # Time axis setter plt.savefig("schedule.png") # print("%d passes scheduled out of %d, %.0f s out of %.0f s at %.3f%% efficiency"%(len(scheduledpasses), len(passes), dt, dttot, 100*eff)) # for satpass in scheduledpasses: for satpass in sorted(scheduledpasses, key=lambda satpass: satpass['tr']): if satpass['scheduled']==False: print("%s %s %3d %3d %3d %5.2f | %s %s"%(satpass['tr'].strftime("%Y-%m-%dT%H:%M:%S"), satpass['ts'].strftime("%Y-%m-%dT%H:%M:%S"), float(satpass['azr']), float(satpass['altt']), float(satpass['azs']),satpass['priority'],satpass['id'], satpass['name'].rstrip())) else: print("%s %s %3d %3d %3d %5.2f | %s %s"%(satpass['tr'].strftime("%Y-%m-%dT%H:%M:%S"), satpass['ts'].strftime("%Y-%m-%dT%H:%M:%S"), 0.0, 0.0, 0.0, 0.0, satpass['id'], "")) # Print schedule commands for satpass in sorted(scheduledpasses, key=lambda satpass: satpass['tr']): if satpass['scheduled']==False: print("firefox \"https://network.satnogs.org/observations/new/?norad=%s&ground_station=%d&start_date=%s&end_date=%s\""%(satpass['id'], ground_station, (satpass['tr']-timedelta(minutes=1)).strftime("%Y/%m/%d%%20%H:%M"), (satpass['ts']+timedelta(minutes=1)).strftime("%Y/%m/%d%%20%H:%M")))