def test_aos_deg_can_be_used_in_get_next_pass(self): start = datetime(2017, 3, 6, 13, 30) end = start + timedelta(hours=1) location = Location('bad-case-1', 11.937501570612568, -55.35189435098657, 1780.674044538666) complete_pass = self.predictor.get_next_pass(location, when_utc=start, limit_date=end) pass_with_aos = self.predictor.get_next_pass(location, when_utc=start, limit_date=end, aos_at_dg=5) self.assertGreater(pass_with_aos.aos, complete_pass.aos) self.assertLess(pass_with_aos.aos, complete_pass.max_elevation_date) self.assertAlmostEqual(pass_with_aos.max_elevation_date, complete_pass.max_elevation_date, delta=timedelta(seconds=1)) self.assertGreater(pass_with_aos.los, complete_pass.max_elevation_date) self.assertLess(pass_with_aos.los, complete_pass.los) position = self.old_predictor.get_position(pass_with_aos.aos) _, elev = location.get_azimuth_elev_deg(position) self.assertAlmostEqual(elev, 5, delta=0.1) position = self.old_predictor.get_position(pass_with_aos.los) _, elev = location.get_azimuth_elev_deg(position) self.assertAlmostEqual(elev, 5, delta=0.1)
def test_sun_elevation_on_earth_works(self): # https://github.com/satellogic/orbit-predictor/issues/52 l1 = Location(latitude_deg=1, longitude_deg=2, elevation_m=3, name="location1") l1.sun_elevation_on_earth()
def test_compare_no_eq(self): l1 = Location(latitude_deg=1, longitude_deg=2, elevation_m=3, name="location_other") l2 = Location(latitude_deg=1, longitude_deg=2, elevation_m=3, name="location1") self.assertNotEqual(l1, l2) self.assertNotEqual(l2, l1)
def test_accurate_predictor_find_more_or_equal_passes_amount(self): self.assertEqualOrGreaterPassesAmount( Location('bad-case-1', 11.937501570612568, -55.35189435098657, 1780.674044538666)) self.assertEqualOrGreaterPassesAmount(tortu1) self.assertEqualOrGreaterPassesAmount(svalbard) self.assertEqualOrGreaterPassesAmount( Location('bad-case-2', -11.011509137116818, 123.29554733688798, 1451.5695915302097)) self.assertEqualOrGreaterPassesAmount( Location('bad-case-3', 10.20803236163988, 138.01236517021056, 4967.661890730469)) self.assertEqualOrGreaterPassesAmount( Location('less passes', -82.41515032683046, -33.712555446065664, 4417.427841452149))
def __init__(self, args): self.slack_url = args.slackUrl self.location = Location("", latitude_deg=args.locationLat, longitude_deg=args.locationLng, elevation_m=args.locationElevation) self.satellite_tle_url = f'https://celestrak.com/satcat/tle.php?CATNR={args.satellite}' self.notification = args.notificationMsg
def test_compare_eq_subclass(self): class SubLocation(Location): pass l1 = Location(latitude_deg=1, longitude_deg=2, elevation_m=3, name="location1") l2 = SubLocation(latitude_deg=1, longitude_deg=2, elevation_m=3, name="location1") self.assertEqual(l1, l2) self.assertEqual(l2, l1)
def test_predicted_passes_off_nadir_angle_works(self): start = datetime(2017, 3, 6, 13, 30) end = start + timedelta(hours=1) location = Location('bad-case-1', 11.937501570612568, -55.35189435098657, 1780.674044538666) pass_ = self.predictor.get_next_pass(location, when_utc=start, limit_date=end) self.assertGreaterEqual(0, pass_.off_nadir_deg)
def test_predicted_passes_are_equal_between_executions(self): location = Location('bad-case-1', 11.937501570612568, -55.35189435098657, 1780.674044538666) first_set = list( self.predictor.passes_over(location, self.start, self.end)) second_set = list( self.predictor.passes_over(location, self.start + timedelta(seconds=3), self.end)) self.assertEqual(first_set, second_set)
def test_predicted_passes_are_equal_between_executions(self): location = Location('bad-case-1', 11.937501570612568, -55.35189435098657, 1780.674044538666) first_set = list( self.predictor.passes_over(location, self.start, self.end)) second_set = list( self.predictor.passes_over(location, self.start + timedelta(seconds=3), self.end)) # We use delta=ONE_SECOND because # that's the hardcoded value for the precision self.assertAlmostEqual(first_set[0].aos, second_set[0].aos, delta=ONE_SECOND) self.assertAlmostEqual(first_set[0].los, second_set[0].los, delta=ONE_SECOND)
def get_passes(config: Configuration, from_: datetime.datetime, to: datetime.datetime): location = Location(*get_location(config)) satellties = filter(lambda s: not s.get("disabled", False), config["satellites"]) strategy_name: str = config.get("strategy", "max-elevation") # type: ignore orbit_db = OrbitDatabase(config["norad"]) strategy = strategy_factory(strategy_name) init = [] for sat in satellties: set_satellite_defaults(config, sat) aos_at = sat["aos_at"] max_elevation_greater_than = sat["max_elevation_greater_than"] predictor = orbit_db.get_predictor(sat["name"]) passes = predictor.passes_over(location, from_, to, max_elevation_greater_than, aos_at_dg=aos_at) init += [(sat["name"], p) for p in passes] selected = strategy(init) return selected
def test_pass_is_not_skipped_smart(self): loc = Location( name="loc", latitude_deg=-15.137152171507697, longitude_deg=-0.4276612055384211, elevation_m=1.665102900005877e-05, ) PASS_DATE = dt.datetime(2020, 9, 25, 9, 2, 6) LIMIT_DATE = dt.datetime(2020, 9, 25, 10, 36, 0) predicted_passes = list( self.predictor.passes_over( loc, when_utc=PASS_DATE, limit_date=LIMIT_DATE, aos_at_dg=0, max_elevation_gt=0, location_predictor_class=SmartLocationPredictor, )) assert predicted_passes
def test_tca_is_correctly_computed(self): start = dt.datetime(2020, 5, 9) end = start + dt.timedelta(days=1) location = Location(name="loc", latitude_deg=11, longitude_deg=0, elevation_m=0) predictor = J2Predictor( sma=475 + 6371, ecc=1.65e-3, inc=53.0, argp=90, raan=0, ta=300, epoch=start, ) expected_tca = dt.datetime(2020, 5, 9, 9, 19, 15) passes = list( predictor.passes_over( location, start, aos_at_dg=0, max_elevation_gt=0, limit_date=end, location_predictor_class=SmartLocationPredictor, tolerance_s=1e-3, ) ) self.assertAlmostEqual(passes[1].max_elevation_date, expected_tca, delta=ONE_SECOND)
def test_los_is_correctly_computed(self): loc = Location( name='loc', latitude_deg=-34.61315, longitude_deg=-58.37723, elevation_m=30, ) PASS_DATE = dt.datetime(2019, 1, 1, 0, 0) LIMIT_DATE = dt.datetime(2019, 1, 15, 0, 0) predicted_passes = list( self.predictor.passes_over( loc, when_utc=PASS_DATE, limit_date=LIMIT_DATE, aos_at_dg=0, max_elevation_gt=0, location_predictor_class=SmartLocationPredictor, )) assert predicted_passes
def __init__(self, lat, long, alt, sat_id, tle_file, refresh_period, verbose): gr.sync_block.__init__(self, name="Doppler", in_sig=None, out_sig=None) # Init block variables self.lat = lat self.long = long self.alt = alt self.sat_id = sat_id self.tle_file = tle_file self.refresh_period = refresh_period gndlocation = Location("GND", latitude_deg=float(self.lat), longitude_deg=float(self.long), elevation_m=float(self.alt)) source = MultipleEtcTLESource(filename=tle_file) predictor = source.get_predictor(self.sat_id) self.thread = doppler_runner(self, predictor, gndlocation, refresh_period, verbose) self.thread.start() self.message_port_register_out(pmt.intern("dop_factor")) self.message_port_register_out(pmt.intern("state"))
from orbit_predictor.locations import Location BCN = Location( name="Barcelona", latitude_deg=41.384740, longitude_deg=2.177813, elevation_m=0, # estimation ) DOWNLINK_FREQS = {"NOAA 15": 137.62, "NOAA 18": 137.9125, "NOAA 19": 137.1} NOAA_IDS = ["NOAA 15", "NOAA 18", "NOAA 19"] MAX_ELEV_GT = 20 # min elevation for the sat flyby trigger recording
def test_pass_is_always_returned(self, start, location): location = Location('bad-case-1', *location) pass_ = self.predictor.get_next_pass(location, start) self.assertGreater(pass_.max_elevation_deg, 0)
def loadConfig(file): global satellites, tle_update_interval, location, output_dir, rss_enabled, rss_port, rss_webserver, post_processing_hook_command, post_processing_hook_enabled, post_processing_hook_foreach, maximum_overlap # Open our file f = io.open(file, mode="r", encoding="utf-8") # Parse it config = yaml.load(f, Loader=yaml.FullLoader) # Software options tle_update_interval = int(config["config"]["tle_update_interval"]) output_dir = str(config["config"]["output_dir"]) maximum_overlap = int(config["config"]["max_overlap"]) # RSS rss_enabled = bool(config["config"]["rss"]["enabled"]) rss_webserver = bool(config["config"]["rss"]["webserver"]) rss_port = int(config["config"]["rss"]["port"]) # Post-Processing Hook post_processing_hook_command = str( config["config"]["post_processing_hook"]["command"]) post_processing_hook_enabled = bool( config["config"]["post_processing_hook"]["enabled"]) post_processing_hook_foreach = bool( config["config"]["post_processing_hook"]["run_foreach"]) print("TLE Update interval : " + str(tle_update_interval) + " hour(s)") print('\n') # Ground station latitude = config["config"]["station"]["latitude"] longitude = config["config"]["station"]["longitude"] elevation = config["config"]["station"]["elevation"] print("Groud station :") print(" Latitude : " + str(latitude)) print(" Longitude : " + str(longitude)) print(" Elevation : " + str(elevation)) print('\n') location = Location("Station", latitude, longitude, elevation) # Load satellites for sat in config["satellites"]: name = sat["name"] norad = sat["norad"] priority = sat["priority"] min_elevation = sat["min_elevation"] frequency = sat["frequency"] downlink = sat["downlink"] delete_processed_files = sat["delete_processed_files"] print("Adding " + name + " :") print(" NORAD : " + str(norad)) print(" Priority : " + str(priority)) print(" Minimum elevation : " + str(min_elevation)) print(" Frequency : " + str(frequency)) print(" Downlink type : " + downlink) print(" Delete processed files : " + str(delete_processed_files)) satellite = Satellite(name, norad, priority, min_elevation, frequency, downlink, delete_processed_files) satellites.append(satellite) print('\n')
from orbit_predictor.sources import NoradTLESource source = NoradTLESource.from_url( "http://www.celestrak.com/NORAD/elements/resource.txt") predictor = source.get_predictor("NUSAT-3") from orbit_predictor.locations import Location FBR = Location("Observatori Fabra", 41.4184, 2.1239, 408) predicted_pass = predictor.get_next_pass(FBR) predicted_pass predicted_pass.aos # Acquisition Of Signal predicted_pass.duration_s predictor.get_position(predicted_pass.aos).position_llh import matplotlib.pyplot as plt plt.ion() import cartopy.crs as ccrs import pandas as pd dates = pd.date_range(start="2017-12-11 00:00", periods=1000, freq="30S") latlon = pd.DataFrame(index=dates, columns=["lat", "lon"]) for date in dates: lat, lon, _ = predictor.get_position(date).position_llh latlon.loc[date] = (lat, lon) plt.figure(figsize=(15, 25)) ax = plt.axes(projection=ccrs.PlateCarree()) ax.stock_img()
def _calculate_series(sat_id: str, aos: datetime.datetime, los: datetime.datetime, location: Optional[Location]=None, time_step: Optional[datetime.timedelta]=None) \ -> Tuple[Sequence[datetime.datetime], Sequence[float], Sequence[float]]: ''' Produce data for plot charts Parameters ========== sat_id: str Satellite ID (name compatible with Celestrak) aos: datetime.datetime AOS date los: datetime.datetime LOS date location: orbit_predictor.locations.Location, optional Location of the station. If not provided it will be read from config time_step: datetime.timedelta, optional Time quantization factor for generate samples. Determines interval between samples. Default one minute. Too small value causes more accurate shape of chart, but plotille draws then thicker lines. Too big value causes low accurate (or false) chart. Returns ======= date_series: sequence of datetime.datetime Dates for which samples were designated. In UTC. azimuth_series: sequence of float Series of azimuths in degrees. elevation_series: sequence of float Series of elevation angles in degrees. ''' tzutc = tz.tzutc() if aos.tzinfo != tzutc: aos = aos.replace(tzinfo=tzutc) if los.tzinfo != tzutc: los = los.replace(tzinfo=tzutc) if time_step is None: time_step = datetime.timedelta(minutes=1) if location is None: config = open_config() location = Location(*get_location(config)) db = OrbitDatabase() predictor = db.get_predictor(sat_id) date_series: List[datetime.datetime] = [] azimuth_series: List[float] = [] elevation_series: List[float] = [] for date in DateTimeRange(aos, los).range(time_step): position = predictor.get_position(date) az, el = location.get_azimuth_elev_deg(position) date_series.append(date) # type: ignore azimuth_series.append(az) elevation_series.append(el) return date_series, azimuth_series, elevation_series
def setUp(self): self.location = Location("A random location", latitude_deg=0, longitude_deg=0, elevation_m=0)
def as_op_location(self): """ Return an equivalent orbit predictor Location. """ return Location('position', latitude_deg=self.latitude, longitude_deg=self.longitude, elevation_m=self.altitude)
#!/usr/bin/env python3 from glob import glob import datetime import numpy as np import matplotlib.pyplot as plt from orbit_predictor.sources import EtcTLESource from orbit_predictor.locations import Location from orbit_predictor.predictors import Position BNL_LOC = Location("BNL", latitude_deg=40.878180, longitude_deg=-72.856640, elevation_m=79) now_time = datetime.datetime(2018, 4, 2, 17, 58, 46, 22518) sources = EtcTLESource("gps/3le.txt") sources2 = EtcTLESource("gps/gps-ops.txt") for sate_id in sources.satellite_list(): if "USA" in sate_id: predictor = sources.get_predictor(sate_id, True) print(sate_id, predictor.get_position(now_time).position_llh) for sate_id in sources2.satellite_list(): predictor = sources2.get_predictor(sate_id, True) print(sate_id, predictor.get_position(now_time).position_ecef)
except StopIteration: raise LookupError("Job not found in CronTab.") else: sat_name = pass_target if args.aos is not None: aos: datetime.datetime = args.aos aos = aos.replace(tzinfo=tz.tzutc()) else: aos = datetime.datetime.utcnow() aos = aos.replace(tzinfo=tz.tzutc()) aos = aos.astimezone(tz.tzutc()) db = OrbitDatabase() config = open_config() from orbit_predictor.locations import Location location = Location(*get_location(config)) satellite = get_satellite(config, sat_name) predictor = db.get_predictor(sat_name) pass_ = predictor.get_next_pass(location, aos, 0, 0) target_tz = tz.tzutc() if args.print_utc else tz.tzlocal() print("Satellite:", sat_name) print("AOS:", str(pass_.aos.astimezone(target_tz))) print("LOS:", str(pass_.los.astimezone(target_tz))) print("Duration:", str(datetime.timedelta(seconds=pass_.duration_s))) print("Max elevation:", "%.2f" % (pass_.max_elevation_deg, ), "deg", str(pass_.max_elevation_date.astimezone(target_tz))) print("Off nadir", "%.2f" % (pass_.off_nadir_deg, ), "deg") import az_elev_chart
import datetime import unittest from datetimerange import DateTimeRange from orbit_predictor.predictors import PredictedPass from orbit_predictor.locations import Location import selectstrategy def get_pass(location, name, aos, los, max_elevation, when_max_elevation = 0.5): duration_s = (los - aos).total_seconds() max_elevation_date = aos + datetime.timedelta(seconds=duration_s * when_max_elevation) return PredictedPass(location, name, max_elevation, aos, los, duration_s, max_elevation_date=max_elevation_date) location = Location("TEST_LOC", 50, 20, 100) start = datetime.datetime(2020, 1, 1, 0, 0, 0) moments = {} for i in range(10): moments[i] = start + datetime.timedelta(minutes=i) class TestSelectStrategy(unittest.TestCase): # | 0 1 2 3 4 5 6 7 8 9 # 90 | |--- A ---| # 80 | |- B | # 70 | |-*- C --| # 60 | |--- D ---| # 50 | |--- E *--| # 40 | |-*- F ---| # 30 | |--G |-- H | def setUp(self): A = get_pass(location, "A", moments[0], moments[2], 90)