How would I calculate the area below an EarthSatellite so that I can plot the swath of land covered as the satellite passes over?
Is there anything in Skyfield that would facilitate that?
Edit: Just thought I'd clarify what I mean by area below the satellite. I need to plot the maximum area below the satellite possible to observe given that the Earth is a spheroid. I know how to plot the satellite path, but now I need to plot some lines to represent the area visible by that satellite as it flies over the earth.
Your edit made it clear what you want. The visible area from a satellite can be easily calculated (when the earth is seen as a sphere). A good source to get some background on the visible portion can be found here. To calculate the visible area when the earth is seen as an oblate spheroid will be a lot harder (and maybe even impossible). I think it's better to reform that part of the question and post it on Mathematics.
If you want to calculate the visible area when the earth is seen as a sphere we need to make some adjustments in Skyfield. With a satellite loaded using the TLE api you can easily get a sub point with the position on earth. The library is calling this the Geocentric position, but actually it's the Geodetic position (where the earth is seen as an oblate spheroid). To correct this we need to adjust subpoint of the Geocentric class to use the calculation for the Geocentric position and not the Geodetic position. Due to a bug and missing information in the reverse_terra function we also need to replace that function. And we need to be able to retrieve the earth radius. This results in the following:
from skyfield import api
from skyfield.positionlib import ICRF, Geocentric
from skyfield.constants import (AU_M, ERAD, DEG2RAD,
IERS_2010_INVERSE_EARTH_FLATTENING, tau)
from skyfield.units import Angle
from numpy import einsum, sqrt, arctan2, pi, cos, sin
def reverse_terra(xyz_au, gast, iterations=3):
"""Convert a geocentric (x,y,z) at time `t` to latitude and longitude.
Returns a tuple of latitude, longitude, and elevation whose units
are radians and meters. Based on Dr. T.S. Kelso's quite helpful
article "Orbital Coordinate Systems, Part III":
https://www.celestrak.com/columns/v02n03/
"""
x, y, z = xyz_au
R = sqrt(x*x + y*y)
lon = (arctan2(y, x) - 15 * DEG2RAD * gast - pi) % tau - pi
lat = arctan2(z, R)
a = ERAD / AU_M
f = 1.0 / IERS_2010_INVERSE_EARTH_FLATTENING
e2 = 2.0*f - f*f
i = 0
C = 1.0
while i < iterations:
i += 1
C = 1.0 / sqrt(1.0 - e2 * (sin(lat) ** 2.0))
lat = arctan2(z + a * C * e2 * sin(lat), R)
elevation_m = ((R / cos(lat)) - a * C) * AU_M
earth_R = (a*C)*AU_M
return lat, lon, elevation_m, earth_R
def subpoint(self, iterations):
"""Return the latitude an longitude directly beneath this position.
Returns a :class:`~skyfield.toposlib.Topos` whose ``longitude``
and ``latitude`` are those of the point on the Earth's surface
directly beneath this position (according to the center of the
earth), and whose ``elevation`` is the height of this position
above the Earth's center.
"""
if self.center != 399: # TODO: should an __init__() check this?
raise ValueError("you can only ask for the geographic subpoint"
" of a position measured from Earth's center")
t = self.t
xyz_au = einsum('ij...,j...->i...', t.M, self.position.au)
lat, lon, elevation_m, self.earth_R = reverse_terra(xyz_au, t.gast, iterations)
from skyfield.toposlib import Topos
return Topos(latitude=Angle(radians=lat),
longitude=Angle(radians=lon),
elevation_m=elevation_m)
def earth_radius(self):
return self.earth_R
def satellite_visiable_area(earth_radius, satellite_elevation):
"""Returns the visible area from a satellite in square meters.
Formula is in the form is 2piR^2h/R+h where:
R = earth radius
h = satellite elevation from center of earth
"""
return ((2 * pi * ( earth_radius ** 2 ) *
( earth_radius + satellite_elevation)) /
(earth_radius + earth_radius + satellite_elevation))
stations_url = 'http://celestrak.com/NORAD/elements/stations.txt'
satellites = api.load.tle(stations_url)
satellite = satellites['ISS (ZARYA)']
print(satellite)
ts = api.load.timescale()
t = ts.now()
geocentric = satellite.at(t)
geocentric.subpoint = subpoint.__get__(geocentric, Geocentric)
geocentric.earth_radius = earth_radius.__get__(geocentric, Geocentric)
geodetic_sub = geocentric.subpoint(3)
print('Geodetic latitude:', geodetic_sub.latitude)
print('Geodetic longitude:', geodetic_sub.longitude)
print('Geodetic elevation (m)', int(geodetic_sub.elevation.m))
print('Geodetic earth radius (m)', int(geocentric.earth_radius()))
geocentric_sub = geocentric.subpoint(0)
print('Geocentric latitude:', geocentric_sub.latitude)
print('Geocentric longitude:', geocentric_sub.longitude)
print('Geocentric elevation (m)', int(geocentric_sub.elevation.m))
print('Geocentric earth radius (m)', int(geocentric.earth_radius()))
print('Visible area (m^2)', satellite_visiable_area(geocentric.earth_radius(),
geocentric_sub.elevation.m))
Related
How to generate random latitude and longitude using Python 3 random module? I already googled and read documentation and not found a way to do this.
The problem when using uniform distributions for both the latitude and the longitude
is that physically, the latitude is NOT uniformly distributed.
So if you plan to use these random points for something like some statiscal averaging computation,
or a physics Monte-Carlo simulation, the results will risk being incorrect.
And if you plot a graphical representation of the “uniform” random points, they will seem to cluster in the polar regions.
To picture that, consider on planet Earth the zone that lies between 89 and 90 degrees of latitude (North).
The length of a degree of latitude is 10,000/90 = 111 km. That zone is a circle of radius 111 km,
centered around the North Pole. Its area is about 3.14 * 111 * 111 ≈ 39,000 km2
On the other hand, consider the zone that lies between 0 and 1 degree of latitude.
This is a strip whose length is 40,000 km (the Equator) and whose width is 111 km,
so its area is 4.44 millions km2. Much larger than the polar zone.
A simple algorithm:
A possibility is to use Gaussian-distributed random variables, as provided by the Python library.
If we build a 3D vector whose 3 components have Gaussian distributions, the overall
probability distribution is like
exp(-x2) * exp(-y2) * exp(-z2)
but this is the same thing as exp(-(x2 + y2 + z2))
or exp(-r2), where r is the distance from the origin.
So these vectors have no privileged direction. Once normalized to unit length, they are uniformly
distributed on the unit sphere. They solve our problem with the latitude distribution.
The idea is implemented by the following Python code:
import math
import random
def randlatlon1():
pi = math.pi
cf = 180.0 / pi # radians to degrees Correction Factor
# get a random Gaussian 3D vector:
gx = random.gauss(0.0, 1.0)
gy = random.gauss(0.0, 1.0)
gz = random.gauss(0.0, 1.0)
# normalize to an equidistributed (x,y,z) point on the unit sphere:
norm2 = gx*gx + gy*gy + gz*gz
norm1 = 1.0 / math.sqrt(norm2)
x = gx * norm1
y = gy * norm1
z = gz * norm1
radLat = math.asin(z) # latitude in radians
radLon = math.atan2(y,x) # longitude in radians
return (round(cf*radLat, 5), round(cf*radLon, 5))
A sanity check:
Euclidean geometry provides a formula for the probability of a spherical zone
defined by minimal/maximal latitude and longitude. The corresponding Python code is like this:
def computeProbaG(minLat, maxLat, minLon, maxLon):
pi = math.pi
rcf = pi / 180.0 # degrees to radians Correction Factor
lonProba = (maxLon - minLon) / 360.0
minLatR = rcf * minLat
maxLatR = rcf * maxLat
latProba = (1.0/2.0) * (math.sin(maxLatR) - math.sin(minLatR))
return (lonProba * latProba)
And we can also compute an approximation of that same probability by random sampling, using
the random points provided by a function such as randlatlon1, and counting what
percentage of them happen to fall within the selected zone:
def computeProbaR(randlatlon, ranCount, minLat, maxLat, minLon, maxLon):
norm = 1.0 / ranCount
pairs = [randlatlon() for i in range(ranCount)]
acceptor = lambda p: ( (p[0] > minLat) and (p[0] < maxLat) and
(p[1] > minLon) and (p[1] < maxLon) )
selCount = sum(1 for p in filter(acceptor, pairs))
return (norm * selCount)
Equipped with these two functions, we can check for various geometric parameter sets
that the geometric and probabilistic results are in good agreement, with ranCount set to one million random points:
ranCount = 1000*1000
print (" ")
probaG1 = computeProbaG( 30, 60, 45, 90)
probaR1 = computeProbaR(randlatlon1, ranCount, 30, 60, 45, 90)
print ("probaG1 = %f" % probaG1)
print ("probaR1 = %f" % probaR1)
print (" ")
probaG2 = computeProbaG( 10, 55, -40, 160)
probaR2 = computeProbaR(randlatlon1, ranCount, 10, 55, -40, 160)
print ("probaG2 = %f" % probaG2)
print ("probaR2 = %f" % probaR2)
print (" ")
Execution output:
$ python3 georandom.py
probaG1 = 0.022877
probaR1 = 0.022852
probaG2 = 0.179307
probaR2 = 0.179644
$
So the two sort of numbers appears to agree reasonably here.
Addendum:
For the sake of completeness, we can add a second algorithm which is less intuitive but derives from a wider statistical principle.
To solve the problem of the latitude distribution, we can use the Inverse Transform Sampling theorem. In order to do so, we need some formula for the probability of the latitude to be less than an arbitrary prescribed value, φ.
The region of the unit 3D sphere whose latitude is less than a given φ is known as a spherical cap. Its area can be obtained thru elementary calculus, as described here for example.
The spherical cap area is given by formula: A = 2π * (1 + sin(φ))
The corresponding probability can be obtained by dividing this area by the overall area of the unit 3D sphere, that is 4π, corresponding to φ = φmax = π/2. Hence:
p = Proba{latitude < φ} = (1/2) * (1 + sin(φ))
Or, conversely:
φ = arcsin (2*p - 1)
From the Inverse Transform Sampling theorem, a fair sampling of the latitude (in radians) is obtained by replacing the probability p by a random variable uniformly distributed between 0 and 1. In Python, this gives:
lat = math.asin(2*random.uniform(0.0, 1.0) - 1.0)
As for the longitude, this is an independent random variable that is still uniformly distributed between -π and +π (in radians). So the overall Python sampler code is:
def randlatlon2r():
pi = math.pi
cf = 180.0 / pi # radians to degrees Correction Factor
u0 = random.uniform(0.0, 1.0)
u1 = random.uniform(0.0, 1.0)
radLat = math.asin(2*u0 - 1.0) # angle with Equator - from +pi/2 to -pi/2
radLon = (2*u1 - 1) * pi # longitude in radians - from -pi to +pi
return (round(radLat*cf,5), round(radLon*cf,5))
This code has been found to pass successfully the sanity check as described above.
Generate a random number between
Latitude: -85 to +85 (actually -85.05115 for some reason)
Longitude: -180 to +180
As #tandem wrote in his answer, the range for latitude is almost -90 to +90 (it is cut on maps) and for longitude it is -180 to +180. To generate random float numbers in this range use random.uniform function:
import random
# returns (lat, lon)
def randlatlon():
return (round(random.uniform( -90, 90), 5),
round(random.uniform(-180, 180), 5))
It is rounded to 5 digits after comma because that extra accuracy is unnecessary.
I had a question that I have a GPS point (lat long) and I'd like to add noise that gaussian distribution with radius 10m. How should I write a python code?
Generate random bearing and gaussian distribution of distance. Then calculate Destination point given distance and bearing from start point as described here
def gauss_spread(lat, lon, radius):
angle = random.random()*2.0*math.pi
arcdist = random.gauss(0, radius) / 6371000
rlat = math.radians(lat)
newlat = math.asin(math.sin(rlat)*math.cos(arcdist) + math.cos(rlat)*math.sin(arcdist)*math.cos(angle))
newlon = lon + math.degrees(math.atan2(math.sin(angle)*math.sin(arcdist)*math.cos(rlat),math.cos(arcdist)-math.sin(rlat)*math.sin(newlat)))
return math.degrees(newlat), newlon
For small radii you can try the next approach. Generate random angle direction and gaussian distribution of distance (in degrees of earth arc). Then make correction by latitude factor.
import random, math
def gauss_spread_approx(lat, lon, radius):
angle = random.random()*2.0*math.pi
arcdist = random.gauss(0, radius * 180.0 / 20000000.0)
return lat + arcdist * math.sin(angle), \
lon + arcdist * math.cos(angle) / math.cos(math.radians(lat))
print(gauss_spread_approx(45, 0, 111111))
I have estimates for data in units m2 at gridsquare resolution. I need to calculate the number of m2 in each latitude / longitude grid cell?
Cell sizes are much smaller near the poles than at the equator so this is important.
I would like a netcdf file or array of the number of square meters in each grid square.
In case anyone would like a netcdf of the number of square meters in each lat long grid cell.
This is probably not the cleanest solution, but will create a netcdf (earth_m2.nc) of m2 in each grid using xarray.
The gridsize() function is adapted from another stack overflow question.
We can then make a dummy array and create a earth field of m2s using the longitude distances at each location.
"""
This will create a global grid of the approximate size of each grid square.
"""
import numpy as np
import xarray as xr
import matplotlib.pyplot as plt
def gridsize(lat1):
#https://en.wikipedia.org/wiki/Haversine_formula
#https://stackoverflow.com/questions/639695/how-to-convert-latitude-or-longitude-to-meters/11172685#11172685
lon1=200
import math
lat2=lat1
lon2=lon1+1
R = 6378.137 # // Radius of earth in km
dLat = lat2 * np.pi / 180 - lat1 * np.pi / 180
dLon = lon2 * np.pi / 180 - lon1 * np.pi / 180
a = np.sin(dLat/2) * np.sin(dLat/2) + np.cos(lat1 * np.pi / 180) * np.cos(lat2 * np.pi / 180) * np.sin(dLon/2) * np.sin(dLon/2)
c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1-a))
d = R * c
return d * 1000 #; // meters
boxlo,boxla=np.array(np.meshgrid(np.arange(-179.5,179.5,1),np.arange(-89.5,89.5,1)))
sizes=np.ones(boxlo.shape)
grid=gridsize(boxla)
grid_nc=xr.DataArray(grid,coords={'lat':boxla[:,1],'lon':boxlo[1,:]},dims=['lat','lon'])
lat_size=110567 #in m
grid_nc['m2']=grid_nc*lat_size
grid_nc=grid_nc['m2']
grid_nc.to_netcdf('earth_m2.nc')
plt.pcolormesh(boxlo[1,:],boxla[:,1],grid_nc)
plt.colorbar()
plt.show()
By spherical trigonometry, the surface area of a triangle on a sphere of radius R with 1 vertex at the North pole (latitude: π/2) and 2 vertices at the same latitude -π/2 < x < π/2 separated (longitudinally) by d radians is
S(x, d) = (cos⁻¹((cos(b) - cos²(a))/sin²(a)) + 2cos⁻¹((cos(a) - cos(a)cos(b))/(sin(a)sin(b))) - π)R² where a = R(π/2 - x) and b = Rd
So, the surface area of a grid rectangle on a sphere of radius R between lines of longitude separated by d radians and latitudes x₁ > x₂ is
S(x₂, d) - S(x₁, d)
One option is to transform your cells into a coordinate reference system (CRS) that has units in, say, meters rather than degrees. Then the area calculation is simple.
I assume your coordinates are in WGS84.
For the target CRS there are choices especially if you know the locality of the points, but a common collection of global CRSs like this are Universal Transverse Mercator (UTM), or near the poles Universal Polar Stereographic
For example, for UTM, assuming a list of points of the form [lon, lat] where the last point is equal to the first:
import pyproj
from shapely.geometry import Polygon
from shapely.ops import transform
def utm_epsg(lon: float, lat: float) -> int:
"""
Return the UTM EPSG code for the given lon-lat.
"""
offset = int(round((183 + lon) / 6.0))
return 32600 + offset if lat > 0 else 32700 + offset
for lat in range(-79, 83):
for lon in range(-179, 179):
polygon = Polygon([
[lon, lat],
[lon+1, lat],
[lon+1, lat+1],
[lon, lat+1],
[lon, lat],
])
src_crs = pyproj.CRS.from_epsg(4326)
tgt_crs = pyproj.CRS.from_epsg(utm_epsg(polygon.centroid.x, polygon.centroid.y))
project = pyproj.Transformer.from_crs(src_crs, tgt_crs, always_xy=True).transform
utm_polygon = transform(project, polygon)
# aggregate into some result. Here just printed to stdout.
print(polygon.centroid, utm_polygon.area)
It's worth noting that UTM isn't defined south of 80°S and north of 84°N.
I have the following code. This code is simulation of orbiting objects around other objects, E.g. Solar system. As you run it, the objects orbit in circular trajectory.
import math
from vpython import *
lamp = local_light(pos=vector(0,0,0), color=color.yellow)
# Data in units according to the International System of Units
G = 6.67 * math.pow(10,-11)
# Mass of the Earth
ME = 5.973 * math.pow(10,24)
# Mass of the Moon
MM = 7.347 * math.pow(10,22)
# Mass of the Mars
MMa = 6.39 * math.pow(10,23)
# Mass of the Sun
MS = 1.989 * math.pow(10,30)
# Radius Earth-Moon
REM = 384400000
# Radius Sun-Earth
RSE = 149600000000
RMS = 227900000000
# Force Earth-Moon
FEM = G*(ME*MM)/math.pow(REM,2)
# Force Earth-Sun
FES = G*(MS*ME)/math.pow(RSE,2)
# Force Mars-Sun
FEMa = G*(MMa*MS)/math.pow(RMS,2)
# Angular velocity of the Moon with respect to the Earth (rad/s)
wM = math.sqrt(FEM/(MM * REM))
# Velocity v of the Moon (m/s)
vM = wM * REM
print("Angular velocity of the Moon with respect to the Earth: ",wM," rad/s")
print("Velocity v of the Moon: ",vM/1000," km/s")
# Angular velocity of the Earth with respect to the Sun(rad/s)
wE = math.sqrt(FES/(ME * RSE))
# Angular velocity of the Mars with respect to the Sun(rad/s)
wMa = math.sqrt(FEMa/(MMa * RMS))
# Velocity v of the Earth (m/s)
vE = wE * RSE
# Velocity v of the Earth (m/s)
vMa = wMa * RMS
print("Angular velocity of the Earth with respect to the Sun: ",wE," rad/s")
print("Velocity v of the Earth: ",vE/1000," km/s")
# Initial angular position
theta0 = 0
# Position at each time
def positionMoon(t):
theta = theta0 + wM * t
return theta
def positionMars(t):
theta = theta0 + wMa * t
return theta
def positionEarth(t):
theta = theta0 + wE * t
return theta
def fromDaysToS(d):
s = d*24*60*60
return s
def fromStoDays(s):
d = s/60/60/24
return d
def fromDaysToh(d):
h = d * 24
return h
# Graphical parameters
print("\nSimulation Earth-Moon-Sun motion\n")
days = 365
seconds = fromDaysToS(days)
print("Days: ",days)
print("Seconds: ",seconds)
v = vector(384,0,0)
E = sphere(pos = vector(1500,0,0), color = color.blue, radius = 60, make_trail=True)
Ma = sphere(pos = vector(2300,0,0), color = color.orange, radius = 30, make_trail=True)
M = sphere(pos = E.pos + v, color = color.white,radius = 10, make_trail=True)
S = sphere(pos = vector(0,0,0), color = color.yellow, radius=700)
t = 0
thetaTerra1 = 0
dt = 5000
dthetaE = positionEarth(t+dt)- positionEarth(t)
dthetaM = positionMoon(t+dt) - positionMoon(t)
dthetaMa = positionMars(t+dt) - positionMars(t)
print("delta t:",dt,"seconds. Days:",fromStoDays(dt),"hours:",fromDaysToh(fromStoDays(dt)),sep=" ")
print("Variation angular position of the Earth:",dthetaE,"rad/s that's to say",degrees(dthetaE),"degrees",sep=" ")
print("Variation angular position of the Moon:",dthetaM,"rad/s that's to say",degrees(dthetaM),"degrees",sep=" ")
while t < seconds:
rate(500)
thetaEarth = positionEarth(t+dt)- positionEarth(t)
thetaMoon = positionMoon(t+dt) - positionMoon(t)
thetaMars = positionMars(t+dt) - positionMars(t)
# Rotation only around z axis (0,0,1)
E.pos = rotate(E.pos,angle=thetaEarth,axis=vector(0,1,0))
Ma.pos = rotate(Ma.pos,angle=thetaMars,axis=vector(0,1,0))
v = rotate(v,angle=thetaMoon,axis=vector(0,1,0))
M.pos = E.pos + v
t += dt
I am wondering How to change the path of orbit to elliptical?
I have tried several ways but I could not manage to find any solution.
Thank you.
Thank you
This seems like more of a physics issue as opposed to a programming issue. The problem is that you are assuming that each of the orbits are circular when calculating velocity and integrating position linearly (e.g v * dt). This is not how you would go about calculating the trajectory of an orbiting body.
For the case of simplicity, we will assume all the masses are point masses so there aren't any weird gravity gradients or attitude dynamics to account for.
From there, you can refer to this MIT page. (http://web.mit.edu/12.004/TheLastHandout/PastHandouts/Chap03.Orbital.Dynamics.pdf) on orbit dynamics. On the 7th page, there is an equation relating the radial position from your centerbody as a function of a multitude of orbital parameters. It seems like you have every parameter except the eccentricity of the orbit. You can either look that up online or calculate it if you have detailed ephemeral data or apoapsis/periapsis information.
From that equation, you will see a phi - phi_0 term in the denominator. That is colloquially known as the true anomaly of the satellite. Instead of time, you would iterate on this true anomaly parameter from 0 to 360 to find your radial distance, and from true anomaly, inclination, right angle to the ascending node, and the argument of periapses, you can find the 3D cartesian coordinates at a specific true anomaly.
Going from true anomaly is a little less trivial. You will need to find the eccentric anomaly and then the mean anomaly at each eccentric anomaly step. You now have mean anomaly as a function of time. You can linearly interpolate between "nodes" at which you calculate the position with v * dt. You can calculate the velocity from using the vis-viva equation and dt would be the difference between the calculated time steps.
At each time step you can update the satellite's position in your python program and it will properly draw your trajectories.
For more information of the true anomaly, wikipedia has a good description of it: https://en.wikipedia.org/wiki/True_anomaly
For more information about orbital elements (which are needed to convert from radial position to cartesian coordinates): https://en.wikipedia.org/wiki/Orbital_elements
I'd like to write Python code to specify the orbit of a satellite with Keplerian elements, specify a point on the Earth with latitude, longitude, and altitude, specify a time, and compute the angle between two vectors:
- the vector from the satellite to the specified point on the Earth
- the vector from the satellite to the center of the Earth.
I know I can use poliastro to define the orbit and propagate it to the specified time. The hard part is representing the satellite and the Earth point in the same coordinate system.
poliastro currently doesn't specify a coordinate system. Someone in their chat room told me that Earth orbits are in GCRS. astropy can convert GCRS to ITRS, which is an Earth-centered Earth-fixed frame:
import math
import numpy as np
from astropy import units as u
from astropy.time import Time
from poliastro.bodies import Earth
from poliastro.twobody import Orbit
from astropy.coordinates import SkyCoord
def lla2ecef(lat, lon, alt):
""" Convert lat/lon/alt to cartesian position in ECEF frame.
Origin is center of Earth. +x axis goes through lat/lon (0, 0).
+y axis goes through lat/lon (0, 90). +z axis goes through North Pole.
lat: number, geodetic latitude in degrees
lon: number, longitude in degrees
alt: number, altitude above WGS84 ellipsoid, in km
Returns: tuple (x, y, z) coordinates, in km.
Source: "Department of Defense World Geodetic System 1984"
Page 4-4
National Imagery and Mapping Agency
Last updated June, 2004
NIMA TR8350.2
"""
lon = lon * math.pi/180.0 # Convert to radians
lat = lat * math.pi/180.0 # Convert to radians
# WGS84 ellipsoid constants:
a = 6378.137 #equatorial radius, in km
e = 8.1819190842622e-2
# intermediate calculation: prime vertical radius of curvature
N = a/math.sqrt(1 - e**2*math.sin(lat)**2)
#results
x = (N + alt)*math.cos(lat)*math.cos(lon)
y = (N + alt)*math.cos(lat)*math.sin(lon)
z = ((1 - e**2)*N + alt)*math.sin(lat)
return (x, y, z)
epoch = Time(2018, format='decimalyear', scale='tai') #01-01-2018 00:00:00, in TAI
propagation_time = 9000 #seconds
semi_major_axis = 10000 #km
eccentricity = 0.1
inclination = 50 #deg
raan = 70 #deg
arg_perigee = 60 #deg
true_anomaly = 80 #deg
orbit = Orbit.from_classical(
Earth,
semi_major_axis*u.km,
eccentricity*u.one,
inclination*u.deg, raan*u.deg,
arg_perigee*u.deg,
true_anomaly*u.deg,
epoch)
propagated_orbit = orbit.propagate(propagation_time*u.s)
pos_gcrs = propagated_orbit.state.r
sky_gcrs = SkyCoord(
representation_type='cartesian',
x=pos_gcrs[0], y=pos_gcrs[1], z=pos_gcrs[2],
frame='gcrs',
obstime=(epoch + propagation_time*u.s))
pos_ecef = sky_gcrs.transform_to('itrs')
pos_s = np.array((pos_ecef.x.to(u.km).value,
pos_ecef.y.to(u.km).value,
pos_ecef.z.to(u.km).value))
lat = 40 #deg
lon = 50 #deg
alt = 0.06 #km
pos_t = np.array(lla2ecef(lat, lon, alt))
#Compute angle at satellite between target and center of earth
v1 = pos_t - pos_s
v2 = -pos_s
angle = math.acos(np.dot(v1, v2)/(np.linalg.norm(v1)*np.linalg.norm(v2)))
#convert to degrees
angle = angle*180.0/math.pi