class AccuratePredictorCalculationErrorTests(TestCase): """Check that we can learn from calculation errors and provide patches for corner cases""" def setUp(self): # Source self.db = MemoryTLESource() self.db.add_tle(BUGSAT_SATE_ID, BUGSAT1_TLE_LINES, dt.datetime.utcnow()) # Predictor self.predictor = TLEPredictor(BUGSAT_SATE_ID, self.db) self.is_ascending_mock = self._patch( 'orbit_predictor.predictors.base.LocationPredictor._is_ascending') self.start = dt.datetime(2017, 3, 6, 7, 51) logassert.setup(self, 'orbit_predictor.predictors.pass_iterators') def _patch(self, *args, **kwargs): patcher = mock.patch(*args, **kwargs) self.addCleanup(patcher.stop) return patcher.start() def test_ascending_failure(self): self.is_ascending_mock.return_value = False with self.assertRaises(PropagationError): self.predictor.get_next_pass( ARG, self.start, location_predictor_class=LocationPredictor) self.assertLoggedError(str(ARG), str(self.start), *BUGSAT1_TLE_LINES) def test_descending_failure(self): self.is_ascending_mock.return_value = True with self.assertRaises(PropagationError): self.predictor.get_next_pass( ARG, self.start, location_predictor_class=LocationPredictor) self.assertLoggedError(str(ARG), str(self.start), *BUGSAT1_TLE_LINES)
class AccurateVsGpredictTests(TestCase): def setUp(self): # Source self.db = MemoryTLESource() self.db.add_tle(BUGSAT_SATE_ID, BUGSAT1_TLE_LINES, dt.datetime.utcnow()) # Predictor self.predictor = TLEPredictor(BUGSAT_SATE_ID, self.db) def test_get_next_pass_with_stk_data(self): STK_DATA = """ ------------------------------------------------------------------------------------------------ AOS TCA LOS Duration Max El ------------------------------------------------------------------------------------------------ 2014/10/23 01:27:33.224 2014/10/23 01:32:41.074 2014/10/23 01:37:47.944 00:10:14.720 12.76 2014/10/23 03:01:37.007 2014/10/23 03:07:48.890 2014/10/23 03:14:01.451 00:12:24.000 39.32 2014/10/23 14:49:34.783 2014/10/23 14:55:44.394 2014/10/23 15:01:51.154 00:12:16.000 41.75 2014/10/23 16:25:54.939 2014/10/23 16:30:50.152 2014/10/23 16:35:44.984 00:09:50.000 11.45 2014/10/24 01:35:47.889 2014/10/24 01:41:13.181 2014/10/24 01:46:37.548 00:10:50.000 16.07 2014/10/24 03:10:23.486 2014/10/24 03:16:27.230 2014/10/24 03:22:31.865 00:12:08.000 30.62 2014/10/24 14:58:07.378 2014/10/24 15:04:21.721 2014/10/24 15:10:33.546 00:12:26.000 54.83 2014/10/24 16:34:48.635 2014/10/24 16:39:20.960 2014/10/24 16:43:53.204 00:09:04.000 8.78 2014/10/25 01:44:05.771 2014/10/25 01:49:45.487 2014/10/25 01:55:24.414 00:11:18.000 20.07 2014/10/25 03:19:12.611 2014/10/25 03:25:05.674 2014/10/25 03:30:59.815 00:11:47.000 24.09""" # NOQA for line in STK_DATA.splitlines()[4:]: line_parts = line.split() aos = dt.datetime.strptime(" ".join(line_parts[:2]), '%Y/%m/%d %H:%M:%S.%f') max_elevation_date = dt.datetime.strptime( " ".join(line_parts[2:4]), '%Y/%m/%d %H:%M:%S.%f') los = dt.datetime.strptime(" ".join(line_parts[4:6]), '%Y/%m/%d %H:%M:%S.%f') duration = dt.datetime.strptime(line_parts[6], '%H:%M:%S.%f') duration_s = dt.timedelta(minutes=duration.minute, seconds=duration.second).total_seconds() max_elev_deg = float(line_parts[7]) try: date = pass_.los # NOQA except UnboundLocalError: date = dt.datetime.strptime("2014-10-22 20:18:11.921921", '%Y-%m-%d %H:%M:%S.%f') pass_ = self.predictor.get_next_pass(ARG, date) self.assertAlmostEqual(pass_.aos, aos, delta=ONE_SECOND) self.assertAlmostEqual(pass_.los, los, delta=ONE_SECOND) self.assertAlmostEqual(pass_.max_elevation_date, max_elevation_date, delta=ONE_SECOND) self.assertAlmostEqual(pass_.duration_s, duration_s, delta=2 * 1) self.assertAlmostEqual(pass_.max_elevation_deg, max_elev_deg, delta=0.05)
class AccuratePredictorTests(TestCase): def setUp(self): # Source self.db = MemoryTLESource() self.start = datetime(2017, 3, 6, 7, 51) self.db.add_tle(SATE_ID, LINES, self.start) # Predictor self.predictor = HighAccuracyTLEPredictor(SATE_ID, self.db) self.old_predictor = TLEPredictor(SATE_ID, self.db) self.end = self.start + timedelta(days=5) def all_passes_old_predictor(self, location, start, end): while True: try: pass_ = self.old_predictor.get_next_pass(location, when_utc=start, limit_date=end) start = pass_.los yield pass_ except Exception: break def assertEqualOrGreaterPassesAmount(self, location): """Compare propagators and check no passes lost and no performance degradation""" t0 = time.time() old_passes = list( self.all_passes_old_predictor(location, self.start, self.end)) t1 = time.time() predicted_passes = list( self.predictor.passes_over(location, self.start, self.end)) t2 = time.time() self.assertGreaterEqual(len(predicted_passes), len(old_passes), 'We are loosing passes') self.assertLessEqual(t2 - t1, t1 - t0, 'Performance is degraded') 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 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_have_elevation_positive_and_visible_on_date( self): end = self.start + timedelta(days=60) for pass_ in self.predictor.passes_over(svalbard, self.start, end): self.assertGreater(pass_.max_elevation_deg, 0) position = self.old_predictor.get_position( pass_.max_elevation_date) svalbard.is_visible(position) self.assertGreaterEqual(pass_.off_nadir_deg, -90) self.assertLessEqual(pass_.off_nadir_deg, 90) 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) @given(start=datetimes( min_value=datetime(2017, 1, 1), max_value=datetime(2020, 12, 31), ), location=tuples(floats(min_value=-90, max_value=90), floats(min_value=0, max_value=180), floats(min_value=-200, max_value=9000))) @settings(max_examples=10000, deadline=None) @example(start=datetime(2017, 1, 26, 11, 51, 51), location=(-37.69358328273305, 153.96875, 0.0)) 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 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_predicted_passes_whit_aos(self): end = self.start + timedelta(days=60) for pass_ in self.predictor.passes_over(svalbard, self.start, end, aos_at_dg=5): self.assertGreater(pass_.max_elevation_deg, 5) position = self.old_predictor.get_position(pass_.aos) _, elev = svalbard.get_azimuth_elev_deg(position) self.assertAlmostEqual(elev, 5, delta=0.1)
class LocationTestCase(unittest.TestCase): def setUp(self): # Source self.db = MemoryTLESource() self.db.add_tle(SATE_ID, BUGSAT1_TLE_LINES, datetime.datetime.now()) # Predictor self.predictor = TLEPredictor(SATE_ID, self.db) date = datetime.datetime.strptime("2014-10-22 20:18:11.921921", '%Y-%m-%d %H:%M:%S.%f') self.next_pass = self.predictor.get_next_pass(ARG, when_utc=date) def test_compare_eq(self): l1 = Location(latitude_deg=1, longitude_deg=2, elevation_m=3, name="location1") l2 = Location(latitude_deg=1, longitude_deg=2, elevation_m=3, name="location1") self.assertEqual(l1, l2) self.assertEqual(l2, l1) 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_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_get_azimuth_elev(self): date = datetime.datetime.strptime("2014-10-21 22:47:29.147740", '%Y-%m-%d %H:%M:%S.%f') azimuth, elevation = ARG.get_azimuth_elev( self.predictor.get_position(date)) self.assertAlmostEqual(degrees(azimuth), 249.7, delta=0.1) self.assertAlmostEqual(degrees(elevation), -52.1, delta=0.1) def test_get_azimuth_elev_deg(self): date = datetime.datetime.strptime("2014-10-21 22:47:29.147740", '%Y-%m-%d %H:%M:%S.%f') azimuth, elevation = ARG.get_azimuth_elev_deg( self.predictor.get_position(date)) self.assertAlmostEqual(azimuth, 249.7, delta=0.1) self.assertAlmostEqual(elevation, -52.1, delta=0.1) def test_is_visible(self): position = self.predictor.get_position(self.next_pass.aos) self.assertTrue(ARG.is_visible(position)) def test_no_visible(self): position = self.predictor.get_position(self.next_pass.los + datetime.timedelta(minutes=10)) self.assertFalse(ARG.is_visible(position)) def test_is_visible_with_deg(self): position = self.predictor.get_position(self.next_pass.aos + datetime.timedelta(minutes=4)) # 21 deg self.assertTrue(ARG.is_visible(position, elevation=4)) def test_no_visible_with_deg(self): position = self.predictor.get_position(self.next_pass.aos + datetime.timedelta(minutes=4)) # 21 deg self.assertFalse(ARG.is_visible(position, elevation=30)) def test_doppler_factor(self): date = datetime.datetime.strptime("2014-10-21 23:06:11.132438", '%Y-%m-%d %H:%M:%S.%f') position = self.predictor.get_position(date) doppler_factor = ARG.doppler_factor(position) self.assertAlmostEqual((2 - doppler_factor) * 437.445e6, 437.445632e6, delta=100)
class AccuratePredictorTests(TestCase): def setUp(self): # Source self.db = MemoryTLESource() self.start = dt.datetime(2017, 3, 6, 7, 51) self.db.add_tle(SATE_ID, LINES, self.start) # Predictor self.predictor = TLEPredictor(SATE_ID, self.db) self.end = self.start + dt.timedelta(days=5) 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 + dt.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 test_predicted_passes_have_elevation_positive_and_visible_on_date( self): end = self.start + dt.timedelta(days=60) for pass_ in self.predictor.passes_over(ARG, self.start, end): self.assertGreater(pass_.max_elevation_deg, 0) position = self.predictor.get_position(pass_.max_elevation_date) ARG.is_visible(position) self.assertGreaterEqual(pass_.off_nadir_deg, -90) self.assertLessEqual(pass_.off_nadir_deg, 90) def test_predicted_passes_off_nadir_angle_works(self): start = dt.datetime(2017, 3, 6, 13, 30) end = start + dt.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) @given(start=datetimes( min_value=dt.datetime(2017, 1, 1), max_value=dt.datetime(2020, 12, 31), ), location=tuples(floats(min_value=-90, max_value=90), floats(min_value=0, max_value=180), floats(min_value=-200, max_value=9000))) @settings(max_examples=10000, deadline=None) @example(start=dt.datetime(2017, 1, 26, 11, 51, 51), location=(-37.69358328273305, 153.96875, 0.0)) 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 test_aos_deg_can_be_used_in_get_next_pass(self): start = dt.datetime(2017, 3, 6, 13, 30) end = start + dt.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=dt.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.predictor.get_position(pass_with_aos.aos) _, elev = location.get_azimuth_elev_deg(position) self.assertAlmostEqual(elev, 5, delta=0.1) position = self.predictor.get_position(pass_with_aos.los) _, elev = location.get_azimuth_elev_deg(position) self.assertAlmostEqual(elev, 5, delta=0.1) def test_predicted_passes_whit_aos(self): end = self.start + dt.timedelta(days=60) for pass_ in self.predictor.passes_over(ARG, self.start, end, aos_at_dg=5): self.assertGreater(pass_.max_elevation_deg, 5) position = self.predictor.get_position(pass_.aos) _, elev = ARG.get_azimuth_elev_deg(position) self.assertAlmostEqual(elev, 5, delta=0.1)