Skip to main content

An astropy-based package to find the coordinates of the Earth's shadow.

Project description

earthshadow

An astropy based package to calculate if objects are in Earth's shadow

Introduction

The Earth casts a shadow roughly the shape of a cylinder into space, with a radius equal to the Earth's radius, around the point opposite the sun (the "anti-solar point"). Three things make it non-trivial to calculate if an object in space is inside the shadow:

  • The shadow's angular size depends on the distance of the object from the center of the Earth (or the altitude of the object above the surface). E.g., for a Low Earth Orbit (LEO) satellite, most of the sky is in shadow, but for a geostationary (GEO) satellite, the angular radius is about 9 degrees.
  • The exact center and extent of the shadow depend on the observer's position, because of parallax.
  • The shadow does not have a sharp edge, because the Earth's atmosphere refracts some of the light into the geometric shadow region. This allows some light to penetrate about 1 or 2 degrees into the shadow.

The reason this is important is that satellites tend to reflect sunlight, either constant (diffuse reflection) so they tend to leave streaks in astronomical images (e.g., see Nir et al. 2018) or by specular reflection causing glints (e.g., see Nir et al. 2021). Knowing that a point in the sky is in the shadow of the Earth allows one to rule out a source of light as coming from a satellite in Earth's orbit (up to some altitude). Because the vast majority of satellites are in LEO or GEO, it is usually good enough to to test this for GEO altitude.

Installation

Just pip install earthshadow. There are no exceptional dependencies other than astropy.

Usage

The two easiest functions to use are get_shadow_center and get_shadow_radius. These will tell you the position and radius of the shadow in the sky, allowing, e.g., to plan observations in a certain direction to purposefully take images in the shadow (where there are no satellite streaks/glints).

    from astropy.coordinates import SkyCoord
    from astropy.time import Time
    from astropy import units as u
    from earthshadow import get_shadow_center, get_shadow_radius

    # Get the center of the shadow in the sky
    center = get_shadow_center(Time.now(), location='APO', orbit='GEO')

    # Get the radius of the shadow in the sky
    radius = get_shadow_radius(orbit='GEO')

The center of the shadow is essentially the anti-sun position, shifted by parallax due to the observer's position. It is returned as a SkyCoord object, to be used, e.g., for targeting a telescope in that direction. The shadow radius is just the angular size (in degrees) given the orbit of the target. Note that this is includes the full geometric shadow, not accounting for atmospheric refraction. The full shadow is generally 1--2 degrees smaller than this.

Note that by default the get_shadow_radius function will return the size of the shadow as seen from the center of the Earth, as this is most useful for internal calculations in the module. To translate this angle to the size of the shadow as seen by an observer at sea-level, use geocentric_angle = False in the function call.

Another use-case is to check if a given object is in the shadow. This is usually used after already taking observations, to rule out that a streak/glint is due to a satellite. Use dist_from_shadow_center to get the angle (in degrees) the point (or points) are from the shadow's center, and compare that to the output of get_shadow_radius. Both are given, by default, as angles seen by an observer at the center of the Earth. If the distance is more than 2 degrees smaller than the shadow, the source can not be a satellite reflecting sunlight. Note that the source can still be an object in very high orbit or a light emitting object (e.g., a laser shot down from a satellite).

For example, given two arrays of coordinates given by ra_values and dec_values and a single observation time given by time:

    dist = dist_from_shadow_center(ra_values, dec_values, time=time, location='Palomar', orbit='GEO')

    # Check if the object is in the shadow
    for i, d in enumerate(dist):
        if d < radius - 2*u.deg:
            print(f'Object ({ra_values[i]}, {dec_values[i]}) is in the shadow')
        else:
            print(f'Object ({ra_values[i]}, {dec_values[i]}) is not in the shadow')

To make plots of the shadow region we provide two methods: The show_shadow_region which will display a rectangular map of the RA/Dec region around the anti-sun position, that shows, for a given orbit, what the shadow looks like after applying the parallax appropriate for that observer. The show_skymap will show the position of the shadow on an aitoff projection, with some other useful mapping aids (to be added...).

    show_shadow_region(time='now', location='Cerro Paranal', orbit='GEO')
    show_skymap('now', location='Green Bank Telescope', orbit='Medium')

Input formatting

There are a few ways to format the inputs to various functions in this package.

  • Time: input a string in ISO format, e.g., '2021-01-01 00:00:00', or an astropy Time object. Also, can simply use the string "now". If given as a float, assumes it is a Julian Date. Can also input a standard datetime object.
  • Location (obs): use a string name of the observatory (e.g., 'palomar') or give the earth location as an astropy EarthLocation object, or as a tuple of three numbers: (longitude, latitude, altitude). The numbers can be given as astropy Quantity objects, or as numbers, in which case they are assumed to be in degrees and meters, respectively.
  • Orbit (orbit): use a string name of the orbit (one of: 'LEO', 'GEO', 'GPS', 'MEDIUM', or 'HIGH') or give the radius of the orbit as a number in kilometers. This assumes the orbit distance is measured from the center of the Earth. If instead you'd like to specify the altitude above sea-level, use the additional input geocentric_orbit = False which means the Earth's radius is subtracted from the input to orbit in km.

The default values are given at the top of the earthshadow.py module, and can always be accessed by passing a None value to any of these inputs. Unless edited by the user, these defaults are time='now', obs='Palomar', and orbit='GEO'. The medium Earth orbit is arbitrarily defined as 1000 (which is the upper end of LEO, really) and the high Earth orbit is arbitrarily defined as twice the GEO orbital radius. Without a better defition of "high orbit", this is a good estimate of where high satellites will orbit. Above this altitude the Earth's shadow is smaller than the atmospheric refraction, so there is no real place where a satellite is in complete shadow (e.g., the Moon is never completely darkened by the Earth, even in a total lunar eclipse).

Additional notes

To be added...

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

earthshadow-1.0.4.tar.gz (12.0 kB view hashes)

Uploaded Source

Built Distribution

earthshadow-1.0.4-py3-none-any.whl (12.9 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page