|
| 1 | +import os |
| 2 | +from pathlib import Path |
| 3 | + |
| 4 | +from skyfield.api import Loader, load |
| 5 | + |
| 6 | +_tle_dir: Path = Path(os.environ.get("TLE_DIR", Path.home())) |
| 7 | +print("_TLE_DIR", _tle_dir) |
| 8 | +_tle_filename = "iss.tle" |
| 9 | +_tle_url = "http://celestrak.com/NORAD/elements/stations.txt" |
| 10 | + |
| 11 | +_bsp_dir = os.environ.get("BSP_DIR", Path.home()) |
| 12 | +print("_BSP_DIR", _bsp_dir) |
| 13 | +_bsp_421_filename = "de421.bsp" |
| 14 | +_bsp_440s_filename = "de440s.bsp" |
| 15 | + |
| 16 | +_timescale = load.timescale() |
| 17 | + |
| 18 | + |
| 19 | +def _load_iss(): |
| 20 | + """ |
| 21 | + Retrieves ISS telemetry data from a local or remote TLE file and |
| 22 | + returns a Skyfield EarthSatellite object corresponding to the ISS. |
| 23 | + """ |
| 24 | + loader = Loader(_tle_dir, verbose=False) |
| 25 | + try: |
| 26 | + # find telemetry data locally |
| 27 | + satellites = loader.tle_file(_tle_filename) |
| 28 | + except FileNotFoundError: |
| 29 | + pass |
| 30 | + else: |
| 31 | + iss = next((sat for sat in satellites if sat.name == "ISS (ZARYA)"), None) |
| 32 | + if iss is None: |
| 33 | + raise RuntimeError( |
| 34 | + f"Unable to retrieve ISS TLE data from {loader.path_to(_tle_filename)}" |
| 35 | + ) |
| 36 | + return iss |
| 37 | + |
| 38 | + try: |
| 39 | + # find telemetry data remotely |
| 40 | + satellites = loader.tle_file(_tle_url) |
| 41 | + Path(_tle_dir / Path(_tle_url).name).rename(_tle_dir / _tle_filename) |
| 42 | + except Exception as e: |
| 43 | + print(e) |
| 44 | + pass |
| 45 | + else: |
| 46 | + iss = next((sat for sat in satellites if sat.name == "ISS (ZARYA)"), None) |
| 47 | + if iss is None: |
| 48 | + raise RuntimeError(f"Unable to retrieve ISS TLE data from {_tle_url}") |
| 49 | + return iss |
| 50 | + |
| 51 | + raise FileNotFoundError( |
| 52 | + "Unable to retrieve ISS TLE data: " |
| 53 | + + f"cannot find {loader.path_to(_tle_filename)} or download {_tle_url}." |
| 54 | + ) |
| 55 | + |
| 56 | + |
| 57 | +def load_iss(): |
| 58 | + ISS = _load_iss() |
| 59 | + # bind the `coordinates` function to the ISS object as a method |
| 60 | + setattr(ISS, "coordinates", coordinates.__get__(ISS, ISS.__class__)) |
| 61 | + return ISS |
| 62 | + |
| 63 | + |
| 64 | +def coordinates(satellite): |
| 65 | + """ |
| 66 | + Return a Skyfield GeographicPosition object corresponding to the Earth |
| 67 | + latitude and longitude beneath the current celestial position of the ISS. |
| 68 | +
|
| 69 | + See: rhodesmill.org/skyfield/api-topos.html#skyfield.toposlib.GeographicPosition |
| 70 | + """ |
| 71 | + return satellite.at(_timescale.now()).subpoint() |
| 72 | + |
| 73 | + |
| 74 | +def load_ephemeris(bsp_filename): |
| 75 | + loader = Loader(_bsp_dir, verbose=False) |
| 76 | + return loader(bsp_filename) |
| 77 | + |
| 78 | + |
| 79 | +# create ISS as a Skyfield EarthSatellite object |
| 80 | +# See: rhodesmill.org/skyfield/api-satellites.html#skyfield.sgp4lib.EarthSatellite |
| 81 | +ISS = load_iss |
| 82 | + |
| 83 | + |
| 84 | +# Expose ephemeris in the API |
| 85 | +de421 = load_ephemeris(_bsp_421_filename) |
| 86 | +de440s = load_ephemeris(_bsp_440s_filename) |
| 87 | +ephemeris = de421 |
0 commit comments