Demonstrate orbit propagation (OP) using Cowell's method.
Example: ISS TLE file downloaded from celestrak.com on 3 August 2017
ISS (ZARYA)
1 25544U 98067A 17213.83387828 .00000874 00000-0 20415-4 0 9996
2 25544 51.6427 170.7730 0006301 93.4612 353.3163 15.54246517 68858
@author: Ashiv Dhondea
Created on 10 August 2017
In [1]:
# Load the libraries required
import AstroConstants as AstCnst
import AstroFunctions as AstFn
import DynamicsFunctions as DynFn
import MathsFunctions as MathsFn
import Num_Integ as Integ
import math
import numpy as np
# Importing what's needed for nice plots.
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid.anchored_artists import AnchoredText
from matplotlib import rc
rc('font', **{'family': 'serif', 'serif': ['Helvetica']});
rc('text', usetex=True)
params = {'text.latex.preamble' : [r'\usepackage{amsmath}', r'\usepackage{amssymb}']}
plt.rcParams.update(params)
# Matplotlib inline. Comment out if you don't want inline plots.
%matplotlib inline
# Libraries related to time keeping and formatting.
import datetime as dt
import pytz
import aniso8601
In [2]:
# Place the contents of the TLE file here.
## ISS (ZARYA)
tle_line1 = '1 25544U 98067A 17213.83387828 .00000874 00000-0 20415-4 0 9996';
tle_line2 = '2 25544 51.6427 170.7730 0006301 93.4612 353.3163 15.54246517 68858';
In [3]:
# Read the Keplerians as well as the epoch of validity of the TLE file
a,e,i,BigOmega,omega,E,nu,epoch = AstFn.fnTLEtoKeps(tle_line1,tle_line2);
In [4]:
print 'semi-major axis in [km]'
print a
print 'eccentricity'
print e
print 'inclination in [deg]'
print i
print 'RAAN in [deg]'
print BigOmega
print 'argument of perigee in [deg]'
print omega
print 'true anomaly in [deg]'
print math.degrees(nu)
print 'epoch'
print epoch
In [5]:
# Calculate epoch at which the TLE was valid
year,dayy, hrs, mins, secs, millisecs = AstFn.fn_Calculate_Epoch_Time(epoch);
todays_date = AstFn.fn_epoch_date(year,dayy);
print "TLE epoch date is", todays_date
print "UTC time = ",hrs,"h",mins,"min",secs+millisecs,"s"
In [6]:
# Create a datetime object for the epoch of validity
timestamp_tle_epoch = dt.datetime(year=todays_date.year,month=todays_date.month,day=todays_date.day,hour=hrs,minute=mins,second=secs,microsecond=int(millisecs),tzinfo= pytz.utc)
In [7]:
# Convert Keplerians to Cartesian state vector
xstate = AstFn.fnKepsToCarts(a,e, math.radians(i), math.radians(omega),math.radians(BigOmega),nu);
print 'ECI state vector read from TLE file'
print xstate
In [8]:
# Find Kepler period
T = AstFn.fnKeplerOrbitalPeriod(a);
print "the Kepler period is %f min" %(T/60.)
In [9]:
# time step for simulation
delta_t = 1; #[s]
# duration of simulation.
# here, we have chosen to simulate the orbit over two orbits, at a constant time step of delta_t
timevec = np.arange(0,T*2 +delta_t,delta_t,dtype=np.float64);
# state vectors in the ECI frame. contain x, y, z position and x, y, z velocity in [km] & [km/s]
x_state = np.zeros([6,len(timevec)],dtype=np.float64);
# position state vectors in the ECEF frame
xecef = np.zeros([3,len(timevec)],dtype=np.float64);
# Latitude & longitude of the ground track
lat = np.zeros([len(timevec)],dtype=np.float64);
lon = np.zeros([len(timevec)],dtype=np.float64);
In [10]:
index = 0;
x_state[:,index] = xstate; # load initial conditions to the Initial Value Problem
theta_GMST = math.radians(AstFn.fn_Convert_Datetime_to_GMST(timestamp_tle_epoch));
## Rotate ECI position vector by GMST angle to get ECEF position
theta_GMST = AstFn.fnZeroTo2Pi(theta_GMST);
xecef[:,index] = AstFn.fnECItoECEF(x_state[0:3,index],theta_GMST);
lat[index],lon[index] = AstFn.fnCarts_to_LatLon(xecef[:,index]); # convert Cartesian position vector to latitude, longitude
for index in range(1,len(timevec)):
# Read the time and figure out the timestamps for each time instant
current_time = timevec[index];
hrs,mins,secs = AstFn.fnSeconds_To_Hours(current_time + (timestamp_tle_epoch.hour*60*60) + (timestamp_tle_epoch.minute*60)+ timestamp_tle_epoch.second);
dys = timestamp_tle_epoch.day + int(math.ceil(hrs/24));
if hrs >= 24:
hrs = hrs - 24*int(math.ceil(hrs/24)) ;
# Perform the RK4 simulation step
x_state[:,index] = Integ.fnRK4_vector(DynFn.fnKepler_J2, delta_t, x_state[:,index-1],timevec[index]);
# Find the epoch object & figure out the GMST angle from the epoch
tle_epoch_test = dt.datetime(year=timestamp_tle_epoch.year,month=timestamp_tle_epoch.month,day=int(dys),hour=int(hrs),minute=int(mins),second=int(secs),microsecond=0,tzinfo= pytz.utc);
theta_GMST = math.radians(AstFn.fn_Convert_Datetime_to_GMST(tle_epoch_test));
## Rotate ECI position vector by GMST angle to get ECEF position
theta_GMST = AstFn.fnZeroTo2Pi(theta_GMST);
xecef[:,index] = AstFn.fnECItoECEF(x_state[0:3,index],theta_GMST);
lat[index],lon[index] = AstFn.fnCarts_to_LatLon(xecef[:,index]);# convert Cartesian position vector to latitude, longitude
In [11]:
# plot results
coastline_data= np.loadtxt('Coastline.txt',skiprows=1)
w, h = plt.figaspect(0.5)
fig = plt.figure(figsize=(w,h))
ax = fig.gca()
plt.rc('text', usetex=True)
plt.rc('font', family='serif');
plt.rc('font',family='helvetica');
params = {'legend.fontsize': 8,
'legend.handlelength': 2}
plt.rcParams.update(params)
groundtrack_title = timestamp_tle_epoch.strftime('%d %B %Y %H:%M:%S')
fig.suptitle(r"\textbf{ISS Ground Track on %s}" %groundtrack_title,fontsize=14)
plt.plot(coastline_data[:,0],coastline_data[:,1],'g');
ax.set_xlabel(r'Longitude $[\mathrm{^\circ}]$',fontsize=12)
ax.set_ylabel(r'Latitude $[\mathrm{^\circ}]$',fontsize=12)
plt.xlim(-180,180);
plt.ylim(-90,90);
plt.yticks([-90,-80,-70,-60,-50,-40,-30,-20,-10,0,10,20,30,40,50,60,70,80,90]);
plt.xticks([-180,-150,-120,-90,-60,-30,0,30,60,90,120,150,180]);
plt.plot(math.degrees(lon[0]),math.degrees(lat[0]),'yo',markersize=5,label=timestamp_tle_epoch.isoformat() + 'Z');
for index in range(1,len(timevec)-1):
plt.plot(math.degrees(lon[index]),math.degrees(lat[index]),'b.',markersize=1);
ax.grid(True);
at = AnchoredText("AshivD",prop=dict(size=5), frameon=True,loc=4)
at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2")
ax.add_artist(at)
plt.show()
In [ ]: