Add pass.py
parent
8374213939
commit
00e061f2f8
|
@ -16,3 +16,8 @@
|
|||
```
|
||||
./ground_track.py ../examples/sathyabamasat.txt
|
||||
```
|
||||
|
||||
- Calculate and plot satellite passes (requires a site.txt)
|
||||
```
|
||||
./pass.py ../examples/sathyabamasat.txt $ST_DATADIR/sites.txt -s 7300 --starttime 2019-11-06T19:30:00
|
||||
```
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Script to calculate the next passes of a given satellite (from TLE) at a given location
|
||||
"""
|
||||
import sys
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from astropy import units as u
|
||||
|
||||
from beyond.dates import Date, timedelta
|
||||
from beyond.env.jpl import get_orbit
|
||||
from beyond.io.tle import Tle
|
||||
from beyond.frames import create_station
|
||||
from beyond.config import config
|
||||
|
||||
from utils import tle_from_file, read_site_file
|
||||
|
||||
|
||||
def polar_plot(pass_data):
|
||||
plt.figure()
|
||||
ax = plt.subplot(111, projection='polar')
|
||||
ax.set_theta_direction(-1)
|
||||
ax.set_theta_zero_location('N')
|
||||
plt.plot(np.radians(pass_data['azims']), pass_data['elevs'], '-')
|
||||
|
||||
pass_data['meta'] = {}
|
||||
for i, event in enumerate(pass_data['event']):
|
||||
if event:
|
||||
if event.info == 'AOS':
|
||||
style = 'ro'
|
||||
pass_data['meta'].update({'AOS': i})
|
||||
elif event.info == 'MAX':
|
||||
style = 'go'
|
||||
pass_data['meta'].update({'MAX': i})
|
||||
elif event.info == 'LOS':
|
||||
style = 'bo'
|
||||
pass_data['meta'].update({'LOS': i})
|
||||
else:
|
||||
style = 'bo'
|
||||
plt.plot(np.radians(pass_data['azims'][i]), pass_data['elevs'][i], style)
|
||||
|
||||
ax.set_yticks(range(0, 90, 20))
|
||||
ax.set_yticklabels(map(str, range(90, 0, -20)))
|
||||
ax.set_rmax(90)
|
||||
|
||||
try:
|
||||
aos_str = pass_data['date'][pass_data['meta']['AOS']].datetime.strftime('%H:%M:%S')
|
||||
max_str = pass_data['date'][pass_data['meta']['MAX']].datetime.strftime('%H:%M:%S')
|
||||
los_str = pass_data['date'][pass_data['meta']['LOS']].datetime.strftime('%H:%M:%S')
|
||||
textstr = 'AOS {}\nMAX {}\n LOS {}'.format(aos_str, max_str, los_str)
|
||||
|
||||
ax.text(0.05,
|
||||
0.95,
|
||||
textstr,
|
||||
transform=ax.transAxes,
|
||||
fontsize=14,
|
||||
verticalalignment='top',
|
||||
bbox={'boxstyle': 'round',
|
||||
'facecolor': 'white',
|
||||
'alpha': 0.5})
|
||||
except KeyError:
|
||||
# TODO: Fix partial passes
|
||||
pass
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Parse arguments
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Compute the next passes of a satellite at a given location")
|
||||
parser.add_argument('TLEFILE', help="the tle file", type=str)
|
||||
parser.add_argument('SITEFILE', help="the site catalog file", type=str)
|
||||
parser.add_argument("-s",
|
||||
"--site",
|
||||
help="COSPAR_ID (must be present in sites.txt",
|
||||
type=int)
|
||||
parser.add_argument("-t",
|
||||
"--starttime",
|
||||
help="Start time (YYYY-MM-DDTHH:MM:SS) [default: now]",
|
||||
default=datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S"))
|
||||
parser.add_argument("-d",
|
||||
"--duration",
|
||||
help="Duration to search for passes [hours; default: 1.0]",
|
||||
type=float,
|
||||
default=1.0)
|
||||
args = parser.parse_args()
|
||||
|
||||
sites = read_site_file(args.SITEFILE)
|
||||
|
||||
if not args.site in sites.keys():
|
||||
print("ERROR: Site {} not found in {}.".format(args.site, args.SITEFILE))
|
||||
sys.exit(-1)
|
||||
|
||||
site = sites[args.site]
|
||||
|
||||
tle, name = tle_from_file(args.TLEFILE)
|
||||
orbit = tle.orbit()
|
||||
|
||||
# Station definition
|
||||
station = create_station(site['symbol'], (site['latitude'].to(u.deg).value,
|
||||
site['longitude'].to(u.deg).value,
|
||||
site['altitude'].to(u.m).value))
|
||||
starttime = datetime.strptime(args.starttime, '%Y-%m-%dT%H:%M:%S')
|
||||
|
||||
pass_data = {'azims': [],
|
||||
'elevs': [],
|
||||
'date': [],
|
||||
'r': [],
|
||||
'r_dot': [],
|
||||
'event': []}
|
||||
|
||||
print(" Time Azim Elev Distance Radial Velocity")
|
||||
print("=========================================================")
|
||||
|
||||
for orb in station.visibility(orbit,
|
||||
start=Date(starttime),
|
||||
stop=timedelta(hours=args.duration),
|
||||
step=timedelta(seconds=30),
|
||||
events=True):
|
||||
elev = np.degrees(orb.phi)
|
||||
# Radians are counterclockwise and azimuth is clockwise
|
||||
azim = np.degrees(-orb.theta) % 360
|
||||
|
||||
# Archive for plotting
|
||||
pass_data['azims'].append(azim)
|
||||
# Matplotlib actually force 0 to be at the center of the polar plot,
|
||||
# so we trick it by inverting the values
|
||||
pass_data['elevs'].append(90 - elev)
|
||||
pass_data['date'].append(orb.date)
|
||||
pass_data['r'].append(orb.r)
|
||||
pass_data['r_dot'].append(orb.r_dot)
|
||||
pass_data['event'].append(orb.event)
|
||||
|
||||
r = orb.r / 1000.
|
||||
print("{event:7} {orb.date:%H:%M:%S} {azim:7.2f} {elev:7.2f} {r:10.2f} {orb.r_dot:10.2f}".format(
|
||||
orb=orb, r=r, azim=azim, elev=elev, event=orb.event.info if orb.event is not None else ""
|
||||
))
|
||||
|
||||
if orb.event and orb.event.info.startswith("LOS"):
|
||||
# End of the pass, plot the result and
|
||||
# clear the arrays for the next pass
|
||||
print()
|
||||
polar_plot(pass_data)
|
||||
pass_data = {'azims': [],
|
||||
'elevs': [],
|
||||
'date': [],
|
||||
'r': [],
|
||||
'r_dot': [],
|
||||
'event': []}
|
||||
|
||||
# Plot the last (incomplete) pass
|
||||
if pass_data['azims']:
|
||||
polar_plot(pass_data)
|
|
@ -0,0 +1,34 @@
|
|||
import csv
|
||||
from beyond.io.tle import Tle
|
||||
from astropy import units as u
|
||||
|
||||
|
||||
def tle_from_file(filename):
|
||||
'''
|
||||
Returns: TLE:Tle, Object name:str
|
||||
'''
|
||||
|
||||
with open(filename, 'r') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
return Tle(''.join(lines)), lines[0].strip()
|
||||
|
||||
|
||||
def read_site_file(filename):
|
||||
sites = {}
|
||||
|
||||
with open(filename, newline='') as f:
|
||||
reader = csv.reader(f,
|
||||
delimiter=' ',
|
||||
quoting=csv.QUOTE_NONE,
|
||||
skipinitialspace=True)
|
||||
for row in reader:
|
||||
if row[0][0] == '#':
|
||||
continue
|
||||
|
||||
sites[int(row[0])] = {'symbol': row[1],
|
||||
'latitude': float(row[2]) * u.deg,
|
||||
'longitude': float(row[3]) * u.deg,
|
||||
'altitude': float(row[4]) * u.m,
|
||||
'name': ' '.join(row[5:])}
|
||||
return sites
|
Loading…
Reference in New Issue