def test_sun(helper): with patch('beyond.dates.date.EopDb.get') as m: m.return_value = Eop(x=0, y=0, dx=0, dy=0, deps=0, dpsi=0, lod=0, ut1_utc=0.2653703, tai_utc=33.0) sun = get_body('Sun') sun_orb = sun.propagate(Date(2006, 4, 2)) assert str(sun_orb.form) == 'cartesian' assert str(sun_orb.frame) == 'MOD' assert isinstance(sun_orb.propagator, SunPropagator) assert sun_orb.date.scale.name == "UT1" assert abs(32.734629699999999274950823746622 - sun_orb.date._offset) <= np.finfo(float).eps helper.assert_vector( sun_orb.base, np.array([ 146186235643.53641, 28789144480.499767, 12481136552.345926, -5757.901470756284, 26793.699110930935, 11616.03628136728 ]))
def test_moon(): with patch('beyond.dates.date.EopDb.get') as m: m.return_value = Eop(x=0, y=0, dx=0, dy=0, deps=0, dpsi=0, lod=0, ut1_utc=-0.0889898, tai_utc=28.0) moon = get_body('Moon') moon_orb = moon.propagate(Date(1994, 4, 28)) assert str(moon_orb.form) == 'cartesian' assert str(moon_orb.frame) == 'EME2000' assert isinstance(moon_orb.propagator, MoonPropagator) assert moon_orb.date.scale.name == "TDB" assert abs(32.185528758994394138426287099719 + moon_orb.date._offset) <= np.finfo(float).eps np.testing.assert_array_equal( moon_orb.base, np.array([ -134181155.8063418, -311598172.21627569, -126699062.57176001, 0.0, 0.0, 0.0 ]))
def loads(text): """Convert a string formatted along the CCSDS standard into an Orbit or Ephem instance. This function is a wrapper of :py:func:`beyond.io.ccsds.loads` and allows to integrate some space-command specific fields """ orb = ccsds.loads(text) if isinstance(orb, StateVector) and "ccsds_user_defined" in orb.complements: ud = orb.complements["ccsds_user_defined"] name = ud["PROPAGATOR"] if name == "KeplerNum": kwargs = { "step": timedelta(seconds=float(ud["PROPAGATOR_STEP_SECONDS"])), "bodies": [get_body(orb.frame.center.name)], "frame": orb.frame, "method": ud["PROPAGATOR_METHOD"], } else: kwargs = {} orb.as_orbit(get_propagator(name)(**kwargs)) return orb
def test_moon(helper): with patch('beyond.dates.date.EopDb.get') as m: m.return_value = Eop(x=0, y=0, dx=0, dy=0, deps=0, dpsi=0, lod=0, ut1_utc=-0.0889898, tai_utc=28.0) moon = get_body('Moon') moon_orb = moon.propagate(Date(1994, 4, 28)) assert str(moon_orb.form) == 'cartesian' assert str(moon_orb.frame) == 'EME2000' assert isinstance(moon_orb.propagator, MoonPropagator) assert moon_orb.date.scale.name == "TDB" assert abs(32.185528758994394138426287099719 + moon_orb.date._offset) <= np.finfo(float).eps helper.assert_vector( moon_orb, np.array([ -134181155.8063418, -311598172.21627569, -126699062.57176001, 976.8878152910264, -438.8323937765414, -87.42035305682552 ]))
def orbit_kepler(iss_tle): orbit = iss_tle.orbit() orbit.propagator = KeplerNum(timedelta(seconds=60), bodies=get_body('Earth')) return orbit
def molniya_kepler(molniya_tle): molniya = molniya_tle.orbit() molniya.propagator = KeplerNum(timedelta(seconds=120), bodies=get_body('Earth')) return molniya
def orb(): orb = Tle("""0 ISS (ZARYA) 1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927 2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537""").orbit() orb.propagator = Kepler( timedelta(seconds=60), bodies=get_body('Earth') ) return orb
def orbit_man(orbit): orbit = orbit.copy() orbit.propagator = KeplerNum(get_body("Earth"), timedelta(seconds=60)) orbit.maneuvers = [ ImpulsiveMan( Date(2008, 9, 20, 12, 41, 9, 984493), [280, 0, 0], frame="TNW", comment="Maneuver 1", ), ImpulsiveMan(Date(2008, 9, 20, 13, 33, 11, 374985), [270, 0, 0], frame="TNW"), ] return orbit
def orbit(request, iss_tle): orb = iss_tle.orbit() if request.param == "tle": return orb elif request.param == "ephem": start = Date(2018, 4, 5, 16, 50) stop = timedelta(hours=6) step = timedelta(seconds=15) return orb.ephem(start=start, stop=stop, step=step) elif request.param == "kepler": orb.propagator = KeplerNum(timedelta(seconds=60), get_body('Earth')) return orb
def test_moon(): with patch('beyond.dates.date.EopDb.get') as m: m.return_value = Eop(x=0, y=0, dx=0, dy=0, deps=0, dpsi=0, lod=0, ut1_utc=-0.0889898, tai_utc=28.0) moon = get_body('Moon') moon_orb = moon.propagate(Date(1994, 4, 28)) assert str(moon_orb.form) == 'cartesian' assert str(moon_orb.frame) == 'EME2000' assert isinstance(moon_orb.propagator, MoonPropagator) assert moon_orb.date.scale.name == "TDB" assert abs(32.185528758994394138426287099719 + moon_orb.date._offset) <= np.finfo(float).eps np.testing.assert_array_equal( moon_orb.base, np.array([ -134181155.8063418, -311598172.21627569, -126699062.57176001, 0.0, 0.0, 0.0 ]) )
def test_sun(): with patch('beyond.dates.date.EopDb.get') as m: m.return_value = Eop(x=0, y=0, dx=0, dy=0, deps=0, dpsi=0, lod=0, ut1_utc=0.2653703, tai_utc=33.0) sun = get_body('Sun') sun_orb = sun.propagate(Date(2006, 4, 2)) assert str(sun_orb.form) == 'cartesian' assert str(sun_orb.frame) == 'MOD' assert isinstance(sun_orb.propagator, SunPropagator) assert sun_orb.date.scale.name == "UT1" assert abs(32.734629699999999274950823746622 - sun_orb.date._offset) <= np.finfo(float).eps np.testing.assert_array_equal( sun_orb.base, np.array([ 146186235643.53641, 28789144480.499767, 12481136552.345926, 0.0, 0.0, 0.0 ]) )
def orbit_continuous_man(orbit): orbit = orbit.copy() orbit.propagator = KeplerNum(get_body("Earth"), timedelta(seconds=60)) orbit.maneuvers = [ ContinuousMan( Date(2008, 9, 20, 12, 41, 9, 984493), timedelta(minutes=3), dv=[280, 0, 0], frame="TNW", comment="Maneuver 1", ), ContinuousMan( Date(2008, 9, 20, 13, 33, 11, 374985), timedelta(minutes=3), dv=[270, 0, 0], frame="TNW", ), ] return orbit
def orbit(request, common_env): orb = Tle("""ISS (ZARYA) 1 25544U 98067A 18124.55610684 .00001524 00000-0 30197-4 0 9997 2 25544 51.6421 236.2139 0003381 47.8509 47.6767 15.54198229111731""").orbit() if request.param == "tle": return orb elif request.param == "ephem": start = Date(2018, 4, 5, 16, 50) stop = timedelta(hours=6) step = timedelta(seconds=15) return orb.ephem(start=start, stop=stop, step=step) elif request.param == "kepler": orb.propagator = Kepler( timedelta(seconds=60), get_body('Earth') ) return orb
def space_phase(*argv): """Compute the phase of the moon or other solar system bodies Usage: space-phase <body> [<date>] [--graph] [--frame <frame>] [-a] [--file <file>] Options: <body> Body <date> Date to compute the moon phase at [default: now] (format %Y-%m-%dT%H:%M:%S) -g, --graph Display the moon phase -a, --analytical Use analytical model instead of JPL files --file <file> File """ import sys from .clock import Date from .utils import docopt, parse_date from .station import StationDb args = docopt(space_phase.__doc__) if args["<date>"] is None: args["<date>"] = "now" try: date = parse_date(args["<date>"]) except ValueError as e: print(e, file=sys.stderr) sys.exit(1) StationDb.list() body = args["<body>"] if body == "Moon": center = "EME2000" second = "Sun" else: center = body body = "Sun" second = "Earth" if args["--analytical"] and body.lower() == "moon": first = solar.get_body(body).propagate(date) second = solar.get_body(second).propagate(first.date) src = "analytical" else: src = "JPL" if args["--analytical"]: log.warning( "No analytical model available for '{}'. Switching to JPL source" .format(body)) jpl.create_frames() first = jpl.get_orbit(body, date) second = jpl.get_orbit(second, first.date) if body == "Moon": phase = compute_phase(first, second, center) else: phase = np.pi - compute_phase(first, second, center) body = center illumin = illumination(phase) log.debug("Computing {} phase using source '{}'".format(body, src)) log.info("{} at {:%Y-%m-%dT%H:%M:%S} : {:0.2f}%".format( body, date, illumin * 100)) if args["--graph"] or args["--file"]: draw_phase(date, phase, body=args["<body>"], filepath=args["--file"])
def space_planet(*args): """Compute position of a planet of the solar system and its major moons Usage: space-planet space-planet fetch space-planet <planet>... [options] Options: fetch Retrieve .bsp file <planet> Names of the planet to compute the ephemeris of. If absent, list all bodies available -f, --frame <frame> Frame in which to display the ephemeris to [default: EME2000] -d, --date <date> Start date of the ephem (%Y-%m-%d) [default: midnight] -r, --range <days> Duration of extrapolation [default: 3d] -s, --step <step> Step size of the ephemeris. [default: 60m] -a, --analytical Force analytical model instead of .bsp files Example: space-planet Mars # Position of Mars in EME2000 space-planet Moon -f Phobos # Position of the moon as seen from Phobos This command relies on .bsp files, parsed by the incredible jplephem lib. Bsp file can be retrieved at https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/ Files examples: de432s.bsp Moon, Sun, Mercury, Venus and main bodies barycentre mar097.bsp Mars, Phobos and Deimos jup310.bsp Jupiter and its major moons sat360xl.bsp Saturn and its major moons The 'beyond.env.jpl' config variable must be set to a list of bsp files paths. See beyond documentation about JPL files: http://beyond.readthedocs.io/en/latest/api/env.html#module-beyond.env.jpl If no .bsp file is provided, the command falls back to analytical methods for Moon and Sun. Other bodies are not provided. """ import requests from logging import getLogger log = getLogger(__name__) args = docopt(space_planet.__doc__) if args["fetch"]: folder = ws.folder / "jpl" if not folder.exists(): folder.mkdir() naif = "https://naif.jpl.nasa.gov/pub/naif/generic_kernels/" baseurl = { "de403_2000-2020.bsp": naif + "spk/planets/a_old_versions/", # Data until 2020-01-01 "de430.bsp": naif + "spk/planets/", "de432s.bsp": naif + "spk/planets/", "de435.bsp": naif + "spk/planets/", "jup310.bsp": naif + "spk/satellites/", "sat360xl.bsp": naif + "spk/satellites/", "mar097.bsp": naif + "spk/satellites/", "pck00010.tpc": naif + "pck/", "gm_de431.tpc": naif + "pck/", } success = [] filelist = ws.config.get("beyond", "env", "jpl", fallback="de403_2000-2020.bsp") if not isinstance(filelist, list): filelist = [filelist] for filepath in filelist: filepath = Path(filepath) if not filepath.is_absolute(): filepath = folder / filepath if not filepath.exists(): url = baseurl.get(filepath.name, "") + str(filepath.name) log.info("Fetching {}".format(filepath.name)) log.debug(url) try: r = requests.get(url, stream=True) except requests.exceptions.ConnectionError as e: log.error(e) else: try: r.raise_for_status() except requests.exceptions.HTTPError as e: log.error("{} {}".format(filepath.name, e)) else: total = int(r.headers.get("content-length", 0)) size = 0 with filepath.open("bw") as fp: for chunk in r.iter_content(chunk_size=1024): fp.write(chunk) if total: size += len(chunk) print( "\r> {:6.2f} % {} / {}".format( 100 * size / total, humanize(size), humanize(total), ), end="", ) if total: print("\r", " " * 40, end="\r") success.append(str(filepath.absolute())) log.debug("Adding {} to the list of jpl files".format( filepath.name)) else: success.append(str(filepath.absolute())) log.info("File {} already downloaded".format(filepath.name)) # Adding the file to the list and saving the new state of configuration if success: ws.config.set("beyond", "env", "jpl", success) ws.config.save() elif args["<planet>"]: try: date = parse_date(args["--date"], fmt="date") stop = parse_timedelta(args["--range"]) step = parse_timedelta(args["--step"]) except ValueError as e: print(e, file=sys.stderr) sys.exit(1) if not args["--analytical"]: # Create all frames from .bsp files, if they are available try: jpl.create_frames() except jpl.JplError: jpl_error = True else: jpl_error = False # Create all frames from stations database StationDb.list() try: frame = get_frame(args["--frame"]) except UnknownFrameError as e: print(e, file=sys.stderr) sys.exit(1) # Computation ephems = [] for body_name in args["<planet>"]: try: if args["--analytical"] or jpl_error: body = solar.get_body(body_name).propagate(date) else: body = jpl.get_orbit(body_name, date) except UnknownBodyError as e: print(e, file=sys.stderr) sys.exit(1) ephem = body.ephem(start=date, stop=stop, step=step) ephem.frame = frame ephem.name = body_name ephems.append(ephem) else: print(ccsds.dumps(ephems)) else: print("List of all available bodies") try: jpl.create_frames() txt = recurse(jpl.get_frame("Earth").center.node, set()) except jpl.JplError as e: print(" Sun") print(" Moon") else: print(txt)
def space_passes(*argv): """Compute and plot passes geometry Usage: space-passes <station> (- | <satellite>...) [options] Option: -h --help Show this help <station> Location from which the satellite is tracked <satellite> Satellite to track. - If used the orbit should be provided as stdin in TLE or CCSDS format (see example) -d --date <date> Starting date of the computation [default: now] (format: "%Y-%m-%dT%H:%M:%S") -r --range <days> Range of the computation [default: 1d] -n --no-events Don't compute AOS, MAX and LOS -e --events-only Only show AOS, MAX and LOS -l --light Compute day/penumbra/umbra transitions -s --step <step> Step-size [default: 30s] -p --passes <nb> Number of passes to display [default: 1] -g --graphs Display graphics with matplotlib -z --zenital Reverse direction of azimut angle on the polar plot to show as the passes as seen from the station looking to the sky --radial Compute radial velocity nullation point --csv Print in CSV format --sep=<sep> CSV separator [default: ,] Examples: Simple computation of the ISS, TLS is the name of my station $ space passes TLS ISS Hubble is not part of my satellite database, but I want to compute its visibility just once $ space tle norad 20580 | space passes TLS """ args = docopt(space_passes.__doc__) try: start = parse_date(args["--date"]) step = parse_timedelta(args["--step"]) stop = parse_timedelta(args["--range"]) pass_nb = int(args["--passes"]) sats = Sat.from_command(*args["<satellite>"], text=sys.stdin.read() if args["-"] else "") except ValueError as e: log.error(e) sys.exit(1) try: station = StationDb.get(args["<station>"]) except UnknownFrameError: log.error("Unknwon station '{}'".format(args["<station>"])) sys.exit(1) events = not args["--no-events"] light = LightListener() if args["--light"] and events: events = [light, LightListener(LightListener.PENUMBRA)] if args["--radial"]: rad = RadialVelocityListener(station, sight=True) if isinstance(events, list): events.append(rad) else: events = rad # Computation of the passes for sat in sats: lats, lons = [], [] lats_e, lons_e = [], [] azims, elevs = [], [] azims_e, elevs_e, text_e = [], [], [] info_size = 0 if args["--csv"]: print(args["--sep"].join([ "date", "event", "name", "azimut", "elevation", "distance", "light", ])) else: info_size = len(station.name) + 10 header = "Time Infos{} Sat{} Azim Elev Dist (km) Light ".format( " " * (info_size - 5), " " * (len(sat.name) - 3)) print(header) print("=" * len(header)) count = 0 dates = [] for orb in station.visibility(sat.orb, start=start, stop=stop, step=step, events=events): if args["--events-only"] and orb.event is None: continue azim = -np.degrees(orb.theta) % 360 elev = np.degrees(orb.phi) azims.append(azim) elevs.append(90 - elev) dates.append(orb.date) r = orb.r / 1000.0 if orb.event: azims_e.append(azim) elevs_e.append(90 - elev) light_info = "Umbra" if light(orb) <= 0 else "Light" if args["--csv"]: fmt = [ "{orb.date:%Y-%m-%dT%H:%M:%S.%f}", "{event}", "{sat.name}", "{azim:.2f}", "{elev:.2f}", "{r:.2f}", "{light}", ] fmt = args["--sep"].join(fmt) else: fmt = "{orb.date:%Y-%m-%dT%H:%M:%S.%f} {event:{info_size}} {sat.name} {azim:7.2f} {elev:7.2f} {r:10.2f} {light}" print( fmt.format( orb=orb, r=r, azim=azim, elev=elev, light=light_info, sat=sat, event=orb.event if orb.event is not None else "", info_size=info_size, )) orb_itrf = orb.copy(frame="ITRF") lon, lat = np.degrees(orb_itrf[1:3]) lats.append(lat) lons.append(lon) if orb.event: lats_e.append(lat) lons_e.append(lon) text_e.append(orb.event.info) if (orb.event is not None and orb.event.info == "LOS" and orb.event.elev == 0): print() count += 1 if count == pass_nb: break # Plotting if args["--graphs"] and azims: # Polar plot of the passes plt.figure(figsize=(15.2, 8.2)) ax = plt.subplot(121, projection="polar") ax.set_theta_zero_location("N") plt.title("{} from {}".format(sat.name, station)) if not args["--zenital"]: ax.set_theta_direction(-1) plt.plot(np.radians(azims), elevs, ".") for azim, elev, txt in zip(azims_e, elevs_e, text_e): plt.plot(np.radians(azim), elev, "ro") plt.text(np.radians(azim), elev, txt, color="r") if station.mask is not None: m_azims = np.arange(0, 2 * np.pi, np.pi / 180.0) m_elevs = [ 90 - np.degrees(station.get_mask(azim)) for azim in m_azims ] plt.plot(-m_azims, m_elevs) # Add the Moon and Sun traces bodies = (('Sun', 'yo', None), ('Moon', 'wo', 'k')) bodies_ephem = {} for body, marker, edge in bodies: b_ephem = get_body(body).propagate(orb.date).ephem(dates=dates) bodies_ephem[body] = b_ephem mazim, melev = [], [] for m in station.visibility(b_ephem): mazim.append(-m.theta) melev.append(90 - np.degrees(m.phi)) plt.plot(mazim, melev, marker, mec=edge, mew=0.5) ax.set_yticks(range(0, 90, 20)) ax.set_yticklabels(map(str, range(90, 0, -20))) ax.set_xticklabels(["N", "NE", "E", "SE", "S", "SW", "W", "NW"]) ax.set_rmax(90) plt.tight_layout() plt.subplot(122) # Ground-track of the passes set_background() plt.plot(lons, lats, "b.") plt.plot(lons_e, lats_e, "r.") color = "#202020" # Ground Station sta_lat, sta_lon = np.degrees(station.latlonalt[:-1]) # Visibility circle lon, lat = np.degrees( list(zip(*circle(orb_itrf.r, *station.latlonalt[-2::-1])))) lon = ((lon + 180) % 360) - 180 plt.plot(lon, lat, ".", color=color, ms=2) # Mask if station.mask is not None: m_azims = np.arange(0, 2 * np.pi, np.pi / 180.0) m_elevs = [station.get_mask(azim) for azim in m_azims] mask = [m_azims, m_elevs] lon, lat = np.degrees( list( zip(*circle( orb_itrf.r, np.radians(sta_lon), np.radians(sta_lat), mask=mask, )))) lon = ((lon + 180) % 360) - 180 plt.plot(lon, lat, color="c", ms=2) # Add the moon and sun traces for body, marker, edge in bodies: b_itrf = np.asarray(bodies_ephem[body].copy(frame="ITRF", form="spherical")) lon = ((np.degrees(b_itrf[:, 1]) + 180) % 360) - 180 lat = np.degrees(b_itrf[:, 2]) plt.plot(lon, lat, marker, mec=edge, mew=0.5) plt.xlim([-180, 180]) plt.ylim([-90, 90]) plt.grid(linestyle=":") plt.xticks(range(-180, 181, 30)) plt.yticks(range(-90, 91, 30)) plt.tight_layout() plt.subplots_adjust(left=0.02, right=0.98, top=0.98, bottom=0.02) if args["--graphs"]: plt.show()
from beyond.propagators.keplernum import KeplerNum from beyond.env.solarsystem import get_body from beyond.orbits.man import ImpulsiveMan from beyond.orbits.listeners import ApsideListener, find_event orb = Tle("""ISS (ZARYA) 1 25544U 98067A 18124.55610684 .00001524 00000-0 30197-4 0 9997 2 25544 51.6421 236.2139 0003381 47.8509 47.6767 15.54198229111731""" ).orbit() start = orb.date stop = timedelta(minutes=300) step = timedelta(seconds=60) # Changing the propagator to Keplerian, as SGP4 is not able to perform maneuvers orb.propagator = KeplerNum(step, bodies=get_body("Earth")) # Research for the next perigee perigee = find_event(orb.iter(stop=stop, listeners=ApsideListener()), 'Periapsis') man1 = ImpulsiveMan(perigee.date, (280, 0, 0), frame="TNW") orb.maneuvers = [man1] dates1, alt1 = [], [] # Research for the next apogee after the first maneuver apogee = find_event( orb.iter(start=perigee.date - step * 10, stop=stop, listeners=ApsideListener()), 'Apoapsis')
from beyond.propagators.kepler import Kepler from beyond.env.solarsystem import get_body from beyond.orbits.man import Maneuver from beyond.orbits.listeners import ApsideListener orb = Tle("""ISS (ZARYA) 1 25544U 98067A 18124.55610684 .00001524 00000-0 30197-4 0 9997 2 25544 51.6421 236.2139 0003381 47.8509 47.6767 15.54198229111731""").orbit() start = orb.date stop = timedelta(minutes=300) step = timedelta(seconds=60) # Changing the propagator to Keplerian, as SGP4 is not able to perform maneuvers orb.propagator = Kepler(step, bodies=get_body("Earth")) # Research for the next perigee for p in orb.iter(start=start, stop=stop, step=step, listeners=ApsideListener()): if p.event and p.event.info == "Periapsis": perigee = p break man1 = Maneuver(perigee.date, (280, 0, 0), frame="TNW") orb.maneuvers = [man1] dates1, alt1 = [], [] # Research for the next apogee after the first maneuver for p in orb.iter(start=perigee.date, stop=stop, step=step, listeners=ApsideListener()): if p.event and p.event.info == "Apoapsis": apogee = p
def _generic_cmd(ext, doc, *argv): # pragma: no cover """Generic command handling """ from .utils import docopt from .sat import Sat args = docopt(doc, argv=argv) if "compute" in args and args["compute"]: try: start = parse_date(args["--date"]) if ext == "oem": stop = parse_timedelta(args["--range"]) step = parse_timedelta(args["--step"]) satlist = Sat.from_command( *args["<selector>"], text=sys.stdin.read() if args["-"] else "") except ValueError as e: log.error(e) sys.exit(1) orbs = [] txt = "" for sat in satlist: if ext == "oem": ephem = sat.orb.ephem(start=start, stop=stop, step=step) ephem.name = sat.name ephem.cospar_id = sat.cospar_id ephem.method = args["--interp"] if args["--frame"] is not None: ephem.frame = StationDb.get(args["--frame"]) orbs.append(ephem) else: orb = sat.orb.propagate(start) orb.name = sat.name orb.cospar_id = sat.cospar_id if args["--frame"] is not None: orb.frame = StationDb.get(args["--frame"]) if args["--propagator"]: propagator_cls = get_propagator(args["--propagator"]) if issubclass(propagator_cls, get_propagator("KeplerNum")): orb.propagator = propagator_cls( parse_timedelta(args["--step"]), get_body(args["--body"])) else: orb.propagator = propagator_cls() txt = dumps(orb, originator=config.get("center", "name", fallback="N/A")) if ext == "oem": txt = dumps(orbs, originator=config.get("center", "name", fallback="N/A")) if not args["--insert"]: print(txt) if args["insert"] or ("compute" in args and args["compute"] and args["--insert"]): if args["--insert"]: pass elif args["-"] and not sys.stdin.isatty(): txt = sys.stdin.read() else: txt = open(args["<file>"]).read() try: sats = Sat.from_command(text=txt, create=True) except ValueError as e: log.error(e) sys.exit(1) for sat in sats: try: CcsdsDb.insert(sat, args["--force"]) except FileExistsError as e: continue elif args["list"]: max_idx = int(args["--last"]) try: for sat in Sat.from_selectors(*args["<selector>"], src=ext, orb=False): print(sat.name.center(65)) rtags = CcsdsDb.rtags(sat, ext) if rtags: tagw = len(max(rtags.values(), key=len)) else: tagw = 4 if ext == "oem": print( "idx Tag{} Frame Start Stop Steps" .format(" " * (tagw - 3))) print("-" * (65 + tagw)) else: print( "idx Tag{} Frame Date Propagator Man Cov" .format(" " * (tagw - 3))) print("-" * (65 + tagw)) for idx, orb in enumerate(CcsdsDb.list(sat, ext)): if sat.req.limit == "any" and idx == sat.req.offset: color = "* \033[32m" endcolor = "\033[39m" else: color = " " endcolor = "" if idx >= max_idx: break if ext == "oem": steps = set() for orb_i, orb_j in zip(orb[:-1], orb[1:]): steps.add(orb_j.date - orb_i.date) if len(steps) == 1: (steps, ) = steps elif (max(steps) - min(steps)).total_seconds() < 1e-5: steps = max(steps) else: steps = "[{}, {}]".format(min(steps), max(steps)) print( "{color}{idx:<2} {tag:{tagw}} {orb.frame.name:8} {orb.start:{fmt}} {orb.stop:{fmt}} {steps}{endcolor}" .format( idx=idx, orb=orb, tag=rtags[orb.filepath] if orb.filepath in rtags else "", fmt="%Y-%m-%dT%H:%M:%S", steps=steps, color=color, endcolor=endcolor, tagw=tagw, )) else: print( "{color}{idx:<2} {tag:{tagw}} {orb.frame.name:8} {orb.date:{fmt}} {propagator:10} {man} {cov}{endcolor}" .format( idx=idx, tag=rtags[orb.filepath] if orb.filepath in rtags else "", orb=orb, fmt="%Y-%m-%dT%H:%M:%S", propagator=orb.propagator.__class__.__name__ if orb.propagator else "None", man=len(orb.maneuvers), cov="Yes" if orb.cov.any() else "None", color=color, endcolor=endcolor, tagw=tagw, )) print() except ValueError as e: log.error(e) sys.exit(1) elif args["get"]: ephems = [] try: for sat in Sat.from_selectors(*args["<selector>"], src=ext, orb=False): ephem = CcsdsDb.get(sat, raw=True) ephems.append(ephem) else: log.error("No {} file corresponding to {}".format( sat.req.src.upper(), sat.req)) except ValueError as e: log.error(e) sys.exit(1) # print(dumps(ephems)) print(ephems[0]) elif args["purge"]: log.info("Starting deletion of {} files".format(ext.upper())) try: until = parse_timedelta(args["--until"]) except ValueError: until = parse_date(args["--until"]) else: until = Date.now() - until for sat in Sat.from_selectors(*args["<selector>"], src=ext, orb=False): rtags = CcsdsDb.rtags(sat) sublist = [] for file in CcsdsDb._list(sat, ext): mtime = Date.strptime( file.stem.partition("_")[2], "%Y%m%d_%H%M%S") if mtime < until: sublist.append(file) if not sublist: log.info("No file to delete") sys.exit(0) print("You are about to delete {} files".format(len(sublist))) for file in sublist: print(" {}".format(file.name)) ans = input("Are you sure ? yes/[no] ") if ans.lower() != "yes": log.info("Deletion canceled") sys.exit(0) for filepath in sublist: if filepath in rtags: log.warning( "{} can't be destroyed due to the tag '{}'".format( filepath.name, rtags[filepath])) continue log.debug("{} {}".format(filepath.name, "destroyed")) filepath.unlink() elif args["list-tags"]: for sat in Sat.from_selectors(*args["<selector>"], src=ext, orb=False): tags = CcsdsDb.tags(sat, ext) if tags: tagw = len(max(tags.keys(), key=len)) for tag, filepath in tags.items(): print("{:{}} {}".format(tag, tagw, filepath.name)) elif args["tag"]: sat = Sat.from_selector(*args["<selector>"], src=ext) try: CcsdsDb.tag(sat, args["<tag>"], force=args["--force"]) except ValueError as e: log.error(e) sys.exit(1)
def __call__(self, frame): plot_list = [] date = self.date() if self.multiplier is None: text = "real time" else: if abs(self.multiplier) == 1: adj = "" value = abs(self.multiplier) elif abs(self.multiplier) > 1: adj = "faster" value = abs(self.multiplier) else: adj = "slower" value = 1 / abs(self.multiplier) sign = "" if self.multiplier > 0 else "-" text = "{}x{:0.0f} {}".format(sign, value, adj) self.date_text.set_text("{:%Y-%m-%d %H:%M:%S}\n{}".format(date, text)) plot_list.append(self.date_text) for i, sat in enumerate(self.sats): color = self.COLORS[i % len(self.COLORS)] # Updating position of the satellite orb = sat.orb.propagate(date) orb_sph = orb.copy(form="spherical", frame="ITRF") lon, lat = self.lonlat(orb_sph) sat.point.set_data([lon], [lat]) plot_list.append(sat.point) # Updating the label sat.text.set_position((lon + 0.75, lat + 0.75)) plot_list.append(sat.text) # Updating the circle of visibility lonlat = np.degrees(circle(*orb_sph[:3])) lonlat[:, 0] = ((lonlat[:, 0] + 180) % 360) - 180 sat.circle.set_data(lonlat[:, 0], lonlat[:, 1]) plot_list.append(sat.circle) # Ground track if sat.win_ephem is None: try: sat.win_ephem = WindowEphem(orb, sat.orb) except ValueError: # In case of faulty windowed ephemeris, disable groundtrack # altogether sat.win_ephem = False if sat.win_ephem: sat.win_ephem.propagate(date) lons, lats = [], [] segments = [] prev_lon, prev_lat = None, None for win_orb in sat.win_ephem: lon, lat = self.lonlat( win_orb.copy(form="spherical", frame="ITRF")) # Creation of multiple segments in order to not have a ground track # doing impossible paths if prev_lon is None: lons = [] lats = [] segments.append((lons, lats)) elif orb.infos.kep.i < np.pi / 2 and ( np.sign(prev_lon) == 1 and np.sign(lon) == -1): lons.append(lon + 360) lats.append(lat) lons = [prev_lon - 360] lats = [prev_lat] segments.append((lons, lats)) elif orb.infos.kep.i > np.pi / 2 and ( np.sign(prev_lon) == -1 and np.sign(lon) == 1): lons.append(lon - 360) lats.append(lat) lons = [prev_lon + 360] lats = [prev_lat] segments.append((lons, lats)) elif abs(prev_lon) > 150 and (np.sign(prev_lon) != np.sign(lon)): lons.append(lon - 360) lats.append(lat) lons = [prev_lon + 360] lats = [prev_lat] segments.append((lons, lats)) lons.append(lon) lats.append(lat) prev_lon = lon prev_lat = lat sat.gt = [] for lons, lats in segments: sat.gt.append( self.ax.plot(lons, lats, color=color, alpha=0.5, lw=2, animated=True)[0]) plot_list.append(sat.gt[-1]) # Updating the sun sun = get_body("Sun").propagate(date).copy(form="spherical", frame="ITRF") lon, lat = self.lonlat(sun) self.sun.set_data([lon], [lat]) plot_list.append(self.sun) # Updating the night lonlat = np.degrees(circle(*sun[:3])) lonlat[:, 0] = ((lonlat[:, 0] + 180) % 360) - 180 season = -95 if lat > 0 else 95 lonlat = lonlat[ lonlat[:, 0].argsort()] # Sorting array by ascending longitude lonlat = np.concatenate([ [[-185, season], [-185, lonlat[0, 1]]], lonlat, [[185, lonlat[-1, 1]], [185, season]], ]) verts = [lonlat] # Eclipse (part of the orbit when the satellite is not illuminated by # the sun) if len(self.sats) == 1: virt_alt = Earth.r * orb_sph.r / np.sqrt(orb_sph.r**2 - Earth.r**2) theta = sun.theta + np.pi phi = -sun.phi lonlat = np.degrees(circle(virt_alt, theta, phi)) lonlat[:, 0] = ((lonlat[:, 0] + 180) % 360) - 180 if all(abs(lonlat[:, 0]) < 175): # This deals with the case when the umbra is between -180 and 180° of # longitude verts.append(lonlat) else: pos_lonlat = lonlat.copy() neg_lonlat = lonlat.copy() pos_lonlat[pos_lonlat[:, 0] < 0, 0] += 360 neg_lonlat[neg_lonlat[:, 0] > 0, 0] -= 360 min_lon = min(pos_lonlat[:, 0]) max_lon = max(neg_lonlat[:, 0]) lonlat = np.concatenate([neg_lonlat, pos_lonlat]) if abs(min_lon - max_lon) > 30: # This deals with the case when the umbra is spread between # the east-west edges of the map, but not the north and south # ones verts.append(lonlat) else: # This deals with the case when the umbra is spread between # east west and also north or south # sort by ascending longitude lonlat = lonlat[lonlat[:, 0].argsort()] west_lat = lonlat[0, 1] east_lat = lonlat[-1, 1] v = np.concatenate([ [[-360, season], [-360, west_lat]], lonlat, [[360, east_lat], [360, season]], ]) verts.append(v) self.night.set_verts(verts) plot_list.insert(0, self.night) # Updating the moon moon = get_body("Moon").propagate(date).copy(frame="ITRF", form="spherical") lon, lat = self.lonlat(moon) self.moon.set_data([lon], [lat]) plot_list.append(self.moon) return plot_list
def test_unknown(): with raises(UnknownBodyError): get_body("Mars")
def test_unknown(): with raises(UnknownBodyError): get_body("Mars")
def update_bodies(self): plot_list = [] # Updating the sun sun = get_body("Sun").propagate(self.date).copy(form="spherical", frame="ITRF") lon, lat = orb2lonlat(sun) self.sun.set_data([lon], [lat]) plot_list.append(self.sun) # Updating the night lonlat = np.degrees(orb2circle(sun)) lonlat[:, 0] = ((lonlat[:, 0] + 180) % 360) - 180 season = -95 if lat > 0 else 95 lonlat = lonlat[ lonlat[:, 0].argsort()] # Sorting array by ascending longitude lonlat = np.concatenate([ [[-185, season], [-185, lonlat[0, 1]]], lonlat, [[185, lonlat[-1, 1]], [185, season]], ]) verts = [lonlat] # Eclipse (part of the orbit when the satellite is not illuminated by # the sun) if len(self.sats) == 1: orb_sph = self.sats[0].propagated.copy(form="spherical", frame="ITRF") virt_alt = Earth.r * orb_sph.r / np.sqrt(orb_sph.r**2 - Earth.r**2) theta = sun.theta + np.pi phi = -sun.phi lonlat = np.degrees(circle(virt_alt, theta, phi)) lonlat[:, 0] = ((lonlat[:, 0] + 180) % 360) - 180 if all(abs(lonlat[:, 0]) < 175): # This deals with the case when the umbra is between -180 and 180° of # longitude verts.append(lonlat) else: pos_lonlat = lonlat.copy() neg_lonlat = lonlat.copy() pos_lonlat[pos_lonlat[:, 0] < 0, 0] += 360 neg_lonlat[neg_lonlat[:, 0] > 0, 0] -= 360 min_lon = min(pos_lonlat[:, 0]) max_lon = max(neg_lonlat[:, 0]) lonlat = np.concatenate([neg_lonlat, pos_lonlat]) if abs(min_lon - max_lon) > 30: # This deals with the case when the umbra is spread between # the east-west edges of the map, but not the north and south # ones verts.append(lonlat) else: # This deals with the case when the umbra is spread between # east west and also north or south # sort by ascending longitude lonlat = lonlat[lonlat[:, 0].argsort()] west_lat = lonlat[0, 1] east_lat = lonlat[-1, 1] v = np.concatenate([ [[-360, season], [-360, west_lat]], lonlat, [[360, east_lat], [360, season]], ]) verts.append(v) self.night.set_verts(verts) plot_list.append(self.night) # Updating the moon moon = (get_body("Moon").propagate(self.date).copy(frame="ITRF", form="spherical")) lon, lat = orb2lonlat(moon) self.moon.set_data([lon], [lat]) plot_list.append(self.moon) return plot_list