3

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.

rfkortekaas
  • 6,049
  • 2
  • 27
  • 34
Gordon13
  • 464
  • 5
  • 21
  • It is possible to calculate the geocentric position of the satellite according to the center of earth and map it to the geocentric position on earth. For the area you should make a lookup table with the sizes of the satellite and plot the area around the geocentric position. – rfkortekaas Mar 03 '19 at 17:48
  • @rfkortekaas By area, I mean, the maximum visible area from the position of the satellite (ignoring imaging payload optical system). what I'm trying to do right now is using trigonometry to construct triangles, and calculate the length of the edge that intersects the earth's surface and the vector from the center of the earth to the satellite. I'm assuming a spherical earth though, which I don't like. But this should give me enough information to calculate a rough circular area visible by the sat. – Gordon13 Mar 03 '19 at 18:52

1 Answers1

7

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))
rfkortekaas
  • 6,049
  • 2
  • 27
  • 34
  • 1
    This is really great, thanks a lot. For now spherical earth will do. I think the way to do it with the oblate sphere is to somehow fit that spheroid on top of the calculated earth coordinates in Skyfield, then get a cone to represent the view of view of the satellite (assuming a zoomed in optical system), from the satellite coordinates, and then intersect that with the spheroid. But that's a question for another day! – Gordon13 Mar 06 '19 at 13:06
  • 1
    What bug have you worked around in the `reverse_terra()` function? I'm interested in fixing it if the function is wrong in Skyfield — it looks like you added a new return parameter, but didn't necessarily change how the other parameters are calculated? – Brandon Rhodes Mar 24 '19 at 22:37
  • 1
    @BrandonRhodes The bug in the `reverse_terra()` function is that `C` is not initialized. When the `iterations` are zero for the geocentric position it fails due to that. Adding the `earth_R` return parameter was a missing feature for the calculation of the satellite above the earth centre. – rfkortekaas Mar 25 '19 at 06:57
  • 1
    I had never considered zero iterations, interesting! The parameter was there in case people wanted more iterations, not less. I'll consider adding a test for the zero-iterations case, though it might be cleaner to break that out as a separate routine since in that case it returns a different coordinate system. Maybe the routine should return an error for iterations ≤ 0. In any case, thanks for clarifying! – Brandon Rhodes Mar 26 '19 at 12:06
  • @BrandonRhodes you’re welcome. You are right about the different coordinate system, but I think you’ve mixed them up. What `reverse_terra` returns with 3 iterations is the `geodetic` coordinate and with 0 iterations is the `geocentric` coordinate. – rfkortekaas Mar 26 '19 at 14:58
  • Skyfield uses the term `Geocentric` for an x,y,z vector whose origin is the geocenter, which matches the USNO use of the term in the source code whose results Skyfield is designed to match. I've tried to avoid applying that term to latitude and longitude, which Skyfield always considers to be geodetic = true latitude and longitude. If you find anywhere that I've accidentally called this latitude and longitude "geocentric" then let me know and I'll get those references cleaned up. – Brandon Rhodes Mar 27 '19 at 15:06