def setUp(self): """Populates an active mission to test a cached view.""" pos = GpsPosition() pos.latitude = 10 pos.longitude = 10 pos.save() self.pos = pos obst = StationaryObstacle() obst.gps_position = pos obst.cylinder_radius = 10 obst.cylinder_height = 10 obst.save() self.obst = obst config = MissionConfig() config.is_active = True config.home_pos = pos config.emergent_last_known_pos = pos config.off_axis_odlc_pos = pos config.air_drop_pos = pos config.save() self.config = config config.stationary_obstacles.add(obst) config.save() self.login_url = reverse('auvsi_suas:login') self.obst_url = reverse('auvsi_suas:obstacles') self.clear_cache_url = reverse('auvsi_suas:clear_cache')
def setUp(self): self.user = User.objects.create_user('testuser', '*****@*****.**', 'testpass') self.user.save() self.client.force_login(self.user) # Create an active mission. pos = GpsPosition() pos.latitude = 10 pos.longitude = 10 pos.save() config = MissionConfig() config.is_active = True config.home_pos = pos config.emergent_last_known_pos = pos config.off_axis_odlc_pos = pos config.air_drop_pos = pos config.save() # Add a couple of stationary obstacles obst = self.create_stationary_obstacle(lat=38.142233, lon=-76.434082, radius=300, height=500) config.stationary_obstacles.add(obst) obst = self.create_stationary_obstacle(lat=38.442233, lon=-76.834082, radius=100, height=750) config.stationary_obstacles.add(obst) config.save()
def setUp(self): """Populates an active mission to test a cached view.""" pos = GpsPosition() pos.latitude = 10 pos.longitude = 10 pos.save() self.pos = pos obst = StationaryObstacle() obst.gps_position = pos obst.cylinder_radius = 10 obst.cylinder_height = 10 obst.save() self.obst = obst config = MissionConfig() config.is_active = True config.home_pos = pos config.emergent_last_known_pos = pos config.off_axis_target_pos = pos config.air_drop_pos = pos config.save() self.config = config config.stationary_obstacles.add(obst) config.save() self.login_url = reverse('auvsi_suas:login') self.obst_url = reverse('auvsi_suas:obstacles') self.clear_cache_url = reverse('auvsi_suas:clear_cache')
def setUp(self): self.user = User.objects.create_user('testuser', '*****@*****.**', 'testpass') self.user.save() # Create an active mission. pos = GpsPosition() pos.latitude = 10 pos.longitude = 10 pos.save() config = MissionConfig() config.is_active = True config.home_pos = pos config.emergent_last_known_pos = pos config.off_axis_target_pos = pos config.air_drop_pos = pos config.save() # Add a couple of stationary obstacles obst = self.create_stationary_obstacle(lat=38.142233, lon=-76.434082, radius=300, height=500) config.stationary_obstacles.add(obst) obst = self.create_stationary_obstacle(lat=38.442233, lon=-76.834082, radius=100, height=750) config.stationary_obstacles.add(obst) # And a couple of moving obstacles obst = self.create_moving_obstacle([ # (lat, lon, alt) (38.142233, -76.434082, 300), (38.141878, -76.425198, 700), ]) config.moving_obstacles.add(obst) obst = self.create_moving_obstacle([ # (lat, lon, alt) (38.145405, -76.428310, 100), (38.146582, -76.424099, 200), (38.144662, -76.427634, 300), (38.147729, -76.419185, 200), (38.147573, -76.420832, 100), (38.148522, -76.419507, 750), ]) config.moving_obstacles.add(obst) config.save() # Login response = self.client.post(login_url, { 'username': '******', 'password': '******' }) self.assertEqual(200, response.status_code)
class TestOdlcsCommon(TestCase): """Common functionality for ODLC tests.""" def setUp(self): # Mission pos = GpsPosition() pos.latitude = 10 pos.longitude = 100 pos.save() apos = AerialPosition() apos.altitude_msl = 1000 apos.gps_position = pos apos.save() wpt = Waypoint() wpt.position = apos wpt.order = 10 wpt.save() self.mission = MissionConfig() self.mission.home_pos = pos self.mission.emergent_last_known_pos = pos self.mission.off_axis_odlc_pos = pos self.mission.air_drop_pos = pos self.mission.save() self.mission.mission_waypoints.add(wpt) self.mission.search_grid_points.add(wpt) self.mission.save() # Mission 2 self.mission2 = MissionConfig() self.mission2.home_pos = pos self.mission2.emergent_last_known_pos = pos self.mission2.off_axis_odlc_pos = pos self.mission2.air_drop_pos = pos self.mission2.save() self.mission2.mission_waypoints.add(wpt) self.mission2.search_grid_points.add(wpt) self.mission2.save()
def setUp(self): """Setup a single mission to test live kml with.""" super(TestGenerateLiveKMLNoFixture, self).setUp() pos = GpsPosition() pos.latitude = 10 pos.longitude = 10 pos.save() config = MissionConfig() config.home_pos = pos config.emergent_last_known_pos = pos config.off_axis_odlc_pos = pos config.air_drop_pos = pos config.save() self.config = config
def setUp(self): """Setup a single active mission to test live kml with.""" super(TestGenerateLiveKMLNoFixture, self).setUp() pos = GpsPosition() pos.latitude = 10 pos.longitude = 10 pos.save() config = MissionConfig() config.is_active = True config.home_pos = pos config.emergent_last_known_pos = pos config.off_axis_target_pos = pos config.air_drop_pos = pos config.save() self.config = config
def setUp(self): pos = GpsPosition() pos.latitude = 10 pos.longitude = 100 pos.save() wpt = Waypoint() wpt.order = 10 wpt.latitude = 10 wpt.longitude = 100 wpt.altitude_msl = 1000 wpt.save() config = MissionConfig() config.home_pos = pos config.lost_comms_pos = pos config.emergent_last_known_pos = pos config.off_axis_odlc_pos = pos config.map_center_pos = pos config.map_height_ft = 1 config.air_drop_pos = pos config.ugv_drive_pos = pos config.save() config.mission_waypoints.add(wpt) config.search_grid_points.add(wpt) config.save() user = User.objects.create_user('user', '*****@*****.**', 'pass') self.feedback = MissionJudgeFeedback( mission=config, user=user, flight_time=datetime.timedelta(seconds=1), post_process_time=datetime.timedelta(seconds=2), used_timeout=True, min_auto_flight_time=True, safety_pilot_takeovers=3, out_of_bounds=6, unsafe_out_of_bounds=7, things_fell_off_uas=False, crashed=False, air_drop_accuracy=interop_admin_api_pb2.MissionJudgeFeedback. WITHIN_05_FT, ugv_drove_to_location=False, operational_excellence_percent=9) self.feedback.save()
def setUp(self): pos = GpsPosition() pos.latitude = 10 pos.longitude = 100 pos.save() apos = AerialPosition() apos.altitude_msl = 1000 apos.gps_position = pos apos.save() wpt = Waypoint() wpt.position = apos wpt.order = 10 wpt.save() config = MissionConfig() config.home_pos = pos config.emergent_last_known_pos = pos config.off_axis_odlc_pos = pos config.air_drop_pos = pos config.save() config.mission_waypoints.add(wpt) config.search_grid_points.add(wpt) config.save() user = User.objects.create_user('user', '*****@*****.**', 'pass') self.feedback = MissionJudgeFeedback( mission=config, user=user, flight_time=datetime.timedelta(seconds=1), post_process_time=datetime.timedelta(seconds=2), used_timeout=True, min_auto_flight_time=True, safety_pilot_takeovers=3, waypoints_captured=5, out_of_bounds=6, unsafe_out_of_bounds=7, things_fell_off_uas=False, crashed=False, air_delivery_accuracy_ft=8, operational_excellence_percent=9) self.feedback.save()
class TestMapCommon(TestCase): """Common functionality for map tests.""" def setUp(self): # Mission pos = GpsPosition() pos.latitude = 10 pos.longitude = 100 pos.save() wpt = Waypoint() wpt.order = 10 wpt.latitude = 10 wpt.longitude = 100 wpt.altitude_msl = 0 wpt.save() self.mission = MissionConfig() self.mission.home_pos = pos self.mission.lost_comms_pos = pos self.mission.emergent_last_known_pos = pos self.mission.off_axis_odlc_pos = pos self.mission.map_center_pos = pos self.mission.map_height_ft = 1 self.mission.air_drop_pos = pos self.mission.ugv_drive_pos = pos self.mission.save() self.mission.mission_waypoints.add(wpt) self.mission.search_grid_points.add(wpt) self.mission.save() # Mission 2 self.mission2 = MissionConfig() self.mission2.home_pos = pos self.mission2.lost_comms_pos = pos self.mission2.emergent_last_known_pos = pos self.mission2.off_axis_odlc_pos = pos self.mission2.map_center_pos = pos self.mission2.map_height_ft = 1 self.mission2.air_drop_pos = pos self.mission2.ugv_drive_pos = pos self.mission2.save() self.mission2.mission_waypoints.add(wpt) self.mission2.search_grid_points.add(wpt) self.mission2.save() self.user = User.objects.create_user('testuser', '*****@*****.**', 'testpass') def filled_url(self): """Returns a filled map URL.""" return map_url(args=[self.mission.pk, self.user.username])
def test_unicode(self): """Tests the unicode method executes.""" pos = GpsPosition() pos.latitude = 10 pos.longitude = 100 pos.save() apos = AerialPosition() apos.altitude_msl = 1000 apos.gps_position = pos apos.save() wpt = Waypoint() wpt.position = apos wpt.order = 10 wpt.save() config = MissionConfig() config.home_pos = pos config.emergent_last_known_pos = pos config.off_axis_target_pos = pos config.air_drop_pos = pos config.save() config.mission_waypoints.add(wpt) config.search_grid_points.add(wpt) config.save() self.assertTrue(config.__unicode__())
class TestOdlcsCommon(TestCase): """Common functionality for ODLC tests.""" def setUp(self): # Mission pos = GpsPosition() pos.latitude = 10 pos.longitude = 100 pos.save() wpt = Waypoint() wpt.order = 10 wpt.latitude = 10 wpt.longitude = 100 wpt.altitude_msl = 0 wpt.save() self.mission = MissionConfig() self.mission.home_pos = pos self.mission.lost_comms_pos = pos self.mission.emergent_last_known_pos = pos self.mission.off_axis_odlc_pos = pos self.mission.map_center_pos = pos self.mission.map_height_ft = 1 self.mission.air_drop_pos = pos self.mission.ugv_drive_pos = pos self.mission.save() self.mission.mission_waypoints.add(wpt) self.mission.search_grid_points.add(wpt) self.mission.save() # Mission 2 self.mission2 = MissionConfig() self.mission2.home_pos = pos self.mission2.lost_comms_pos = pos self.mission2.emergent_last_known_pos = pos self.mission2.off_axis_odlc_pos = pos self.mission2.map_center_pos = pos self.mission2.map_height_ft = 1 self.mission2.air_drop_pos = pos self.mission2.ugv_drive_pos = pos self.mission2.save() self.mission2.mission_waypoints.add(wpt) self.mission2.search_grid_points.add(wpt) self.mission2.save()
class TestUasTelemetryBase(TestCase): """Base for the UasTelemetry tests.""" def setUp(self): self.user = User.objects.create_user('testuser', '*****@*****.**', 'testpass') self.user.save() # Mission pos = GpsPosition() pos.latitude = 10 pos.longitude = 100 pos.save() apos = AerialPosition() apos.latitude = 10 apos.longitude = 100 apos.altitude_msl = 1000 apos.save() wpt = Waypoint() wpt.latitude = 10 wpt.longitude = 100 wpt.altitude_msl = 1000 wpt.order = 10 wpt.save() self.mission = MissionConfig() self.mission.home_pos = pos self.mission.emergent_last_known_pos = pos self.mission.off_axis_odlc_pos = pos self.mission.air_drop_pos = pos self.mission.save() self.mission.mission_waypoints.add(wpt) self.mission.search_grid_points.add(wpt) self.mission.save() self.now = timezone.now() def create_log_element(self, timestamp, lat, lon, alt, heading, user=None): if user is None: user = self.user log = UasTelemetry(user=user, latitude=lat, longitude=lon, altitude_msl=alt, uas_heading=heading) log.save() log.timestamp = self.now + datetime.timedelta(seconds=timestamp) log.save() return log def create_uas_logs(self, entries, user=None): """Create a list of uas telemetry logs. Args: entries: List of (t, lat, lon, alt, heading) tuples for each entry. user: User to create logs for. Returns: List of UasTelemetry objects """ if user is None: user = self.user ret = [] for (t, lat, lon, alt, head) in entries: ret.append(self.create_log_element(t, lat, lon, alt, head, user)) return ret def waypoints_from_data(self, waypoints_data): """Converts tuples of lat/lon/alt to a waypoint.""" waypoints = [] for i, waypoint in enumerate(waypoints_data): (lat, lon, alt) = waypoint wpt = Waypoint() wpt.order = i wpt.latitude = lat wpt.longitude = lon wpt.altitude_msl = alt wpt.save() waypoints.append(wpt) return waypoints def assertTelemetryEqual(self, expect, got): """Assert two telemetry are equal.""" msg = '%s != %s' % (expect, got) self.assertAlmostEqual(expect.latitude, got.latitude, places=6, msg=msg) self.assertAlmostEqual(expect.longitude, got.longitude, places=6, msg=msg) self.assertAlmostEqual(expect.altitude_msl, got.altitude_msl, places=3, msg=msg) self.assertAlmostEqual(expect.uas_heading, got.uas_heading, places=3, msg=msg) def assertTelemetriesEqual(self, expect, got): "Assert two lists of telemetry are equal." "" expect = [i for i in expect] got = [i for i in got] self.assertEqual(len(expect), len(got)) for ix in range(len(expect)): self.assertTelemetryEqual(expect[ix], got[ix]) def assertSatisfiedWaypoints(self, expect, got): """Assert two satisfied_waypoints return values are equal.""" msg = '%s != %s' % (expect, got) self.assertEqual(len(expect), len(got), msg=msg) for i in range(len(expect)): e = expect[i] g = got[i] self.assertEqual(e.id, g.id, msg=msg) self.assertAlmostEqual(e.score_ratio, g.score_ratio, places=2, msg=msg) self.assertAlmostEqual(e.closest_for_scored_approach_ft, g.closest_for_scored_approach_ft, delta=5, msg=msg) self.assertAlmostEqual(e.closest_for_mission_ft, g.closest_for_mission_ft, delta=5, msg=msg)
def test_satisfied_waypoints(self): """Tests the evaluation of waypoints method.""" # Create mission config gpos = GpsPosition() gpos.latitude = 10 gpos.longitude = 10 gpos.save() config = MissionConfig() config.home_pos = gpos config.emergent_last_known_pos = gpos config.off_axis_target_pos = gpos config.air_drop_pos = gpos config.save() # Create waypoints for config waypoints = [(38, -76, 100), (39, -77, 200), (40, -78, 0)] for i, waypoint in enumerate(waypoints): (lat, lon, alt) = waypoint pos = GpsPosition() pos.latitude = lat pos.longitude = lon pos.save() apos = AerialPosition() apos.altitude_msl = alt apos.gps_position = pos apos.save() wpt = Waypoint() wpt.position = apos wpt.order = i wpt.save() config.mission_waypoints.add(wpt) config.save() # Create UAS telemetry logs user = User.objects.create_user('testuser', '*****@*****.**', 'testpass') # Only first is valid. entries = [(38, -76, 140), (40, -78, 600), (37, -75, 40)] expect = ({0: 40}, {0: 0.6, 1: 0.0, 2: 0.0}, {0: 40, 1: 460785.17}) logs = self.create_uas_logs(user, entries) self.assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # First and last are valid, but missed second, so third doesn't count. entries = [(38, -76, 140), (40, -78, 600), (40, -78, 40)] expect = ({0: 40}, {0: 0.6, 1: 0.0, 2: 0.0}, {0: 40, 1: 460785.03}) logs = self.create_uas_logs(user, entries) self.assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Hit all. entries = [(38, -76, 140), (39, -77, 180), (40, -78, 40)] expect = ({0: 40, 1: 20, 2: 40}, {0: 0.6, 1: 0.8, 2: 0.6}, {0: 40, 1: 20, 2: 40}) # yapf: disable logs = self.create_uas_logs(user, entries) self.assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Only hit the first waypoint on run one, hit all on run two. entries = [ (38, -76, 140), (40, -78, 600), (37, -75, 40), # Run two: (38, -76, 140), (39, -77, 180), (40, -78, 40) ] expect = ({0: 40, 1: 20, 2: 40}, {0: 0.6, 1: 0.8, 2: 0.6}, {0: 40, 1: 20, 2: 40}) # yapf: disable logs = self.create_uas_logs(user, entries) self.assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Hit all on run one, only hit the first waypoint on run two. entries = [ (38, -76, 140), (39, -77, 180), (40, -78, 40), # Run two: (38, -76, 140), (40, -78, 600), (37, -75, 40) ] expect = ({0: 40, 1: 20, 2: 40}, {0: 0.6, 1: 0.8, 2: 0.6}, {0: 40, 1: 20, 2: 40}) # yapf: disable logs = self.create_uas_logs(user, entries) self.assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Keep flying after hitting all waypoints. entries = [(38, -76, 140), (39, -77, 180), (40, -78, 40), (30.1, -78.1, 100)] expect = ({0: 40, 1: 20, 2: 40}, {0: 0.6, 1: 0.8, 2: 0.6}, {0: 40, 1: 20, 2: 40}) # yapf: disable logs = self.create_uas_logs(user, entries) self.assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Hit all in first run, but second is higher scoring. entries = [ (38, -76, 140), (39, -77, 180), (40, -78, 60), # Run two: (38, -76, 100), (39, -77, 200), (40, -78, 110) ] expect = ({0: 0, 1: 0}, {0: 1, 1: 1, 2: 0}, {0: 0, 1: 0, 2: 60}) logs = self.create_uas_logs(user, entries) self.assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Restart waypoint path in the middle. waypoints = [(38, -76, 100), (39, -77, 200), (40, -78, 0)] entries = [ (38, -76, 140), (39, -77, 180), # Restart: (38, -76, 70), (39, -77, 150), (40, -78, 10) ] expect = ({0: 30, 1: 50, 2: 10}, {0: 0.7, 1: 0.5, 2: 0.9}, {0: 30, 1: 20, 2: 10}) # yapf: disable logs = self.create_uas_logs(user, entries) self.assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs))
class TestTeamsView(TestCase): """Tests the teams view.""" def setUp(self): self.superuser = User.objects.create_superuser('superuser', '*****@*****.**', 'superpass') self.superuser.save() self.client.force_login(self.superuser) def create_data(self): """Create a basic sample dataset.""" self.user1 = User.objects.create_user('user1', '*****@*****.**', 'testpass') self.user1.save() self.user2 = User.objects.create_user('user2', '*****@*****.**', 'testpass') self.user2.save() # Mission pos = GpsPosition() pos.latitude = 10 pos.longitude = 100 pos.save() wpt = Waypoint() wpt.order = 10 wpt.latitude = 10 wpt.longitude = 100 wpt.altitude_msl = 1000 wpt.save() self.mission = MissionConfig() self.mission.home_pos = pos self.mission.lost_comms_pos = pos self.mission.emergent_last_known_pos = pos self.mission.off_axis_odlc_pos = pos self.mission.map_center_pos = pos self.mission.map_height_ft = 1 self.mission.air_drop_pos = pos self.mission.ugv_drive_pos = pos self.mission.save() self.mission.mission_waypoints.add(wpt) self.mission.search_grid_points.add(wpt) self.mission.save() # user1 is flying event = TakeoffOrLandingEvent(user=self.user1, mission=self.mission, uas_in_air=True) event.save() # user2 has landed event = TakeoffOrLandingEvent(user=self.user2, mission=self.mission, uas_in_air=True) event.save() event = TakeoffOrLandingEvent(user=self.user2, mission=self.mission, uas_in_air=False) event.save() # user2 is active self.timestamp = timezone.now() self.telem = UasTelemetry(user=self.user2, latitude=38.6462, longitude=-76.2452, altitude_msl=0, uas_heading=90) self.telem.save() self.telem.timestamp = dateutil.parser.parse( u'2016-10-01T00:00:00.0+00:00') self.telem.save() def test_normal_user(self): """Normal users allowed access.""" user = User.objects.create_user('testuser', '*****@*****.**', 'testpass') user.save() self.client.force_login(user) response = self.client.get(teams_url) self.assertEqual(200, response.status_code) def test_no_users(self): """No users results in empty list, no superusers.""" response = self.client.get(teams_url) self.assertEqual(200, response.status_code) self.assertEqual([], json.loads(response.content)) def test_post(self): """POST not allowed""" response = self.client.post(teams_url) self.assertEqual(405, response.status_code) def test_correct_json(self): """Response JSON is properly formatted.""" self.create_data() response = self.client.get(teams_url) self.assertEqual(200, response.status_code) data = json.loads(response.content) self.assertEqual(2, len(data)) for user in data: self.assertIn('team', user) self.assertIn('id', user['team']) self.assertIn('username', user['team']) self.assertIn('inAir', user) if 'telemetry' in user: self.assertIn('telemetryId', user) self.assertIn('telemetryAgeSec', user) self.assertIn('telemetryTimestamp', user) def test_users_correct(self): """User names and status correct.""" self.create_data() response = self.client.get(teams_url) self.assertEqual(200, response.status_code) data = json.loads(response.content) names = [d['team']['username'] for d in data] self.assertIn('user1', names) self.assertIn('user2', names) user1 = data[names.index('user1')] self.assertEqual(True, user1['inAir']) self.assertNotIn('telemetry', user1) user2 = data[names.index('user2')] self.assertEqual(False, user2['inAir']) self.assertEqual( { u'latitude': 38.6462, u'longitude': -76.2452, u'altitude': 0.0, u'heading': 90.0, }, user2['telemetry']) self.assertEqual(int(user2['telemetryId']), self.telem.pk) self.assertGreater(user2['telemetryAgeSec'], 0) self.assertEqual(user2['telemetryTimestamp'], u'2016-10-01T00:00:00+00:00')
class TestTakeoffOrLandingEventModel(TestAccessLogCommon): """Tests the TakeoffOrLandingEvent model.""" def setUp(self): super(TestTakeoffOrLandingEventModel, self).setUp() # Mission pos = GpsPosition() pos.latitude = 10 pos.longitude = 100 pos.save() apos = AerialPosition() apos.altitude_msl = 1000 apos.gps_position = pos apos.save() wpt = Waypoint() wpt.position = apos wpt.order = 10 wpt.save() self.mission = MissionConfig() self.mission.home_pos = pos self.mission.emergent_last_known_pos = pos self.mission.off_axis_odlc_pos = pos self.mission.air_drop_pos = pos self.mission.save() self.mission.mission_waypoints.add(wpt) self.mission.search_grid_points.add(wpt) self.mission.save() # Mission 2 self.mission2 = MissionConfig() self.mission2.home_pos = pos self.mission2.emergent_last_known_pos = pos self.mission2.off_axis_odlc_pos = pos self.mission2.air_drop_pos = pos self.mission2.save() self.mission2.mission_waypoints.add(wpt) self.mission2.search_grid_points.add(wpt) self.mission2.save() self.ten_minutes = datetime.timedelta(minutes=10) def create_event(self, time, uas_in_air, mission=None): """Create a TakeoffOrLandingEvent for test user.""" if mission is None: mission = self.mission event = TakeoffOrLandingEvent( user=self.user1, mission=mission, uas_in_air=uas_in_air) event.save() event.timestamp = time event.save() return event def evaluate_periods(self, expected): """Check actual periods against expected.""" periods = TakeoffOrLandingEvent.flights(self.mission, self.user1) self.assertSequenceEqual(expected, periods) def test_basic_flight_period(self): """Single flight reported as period.""" self.create_event(self.year2000, True) self.create_event(self.year2000 + self.ten_minutes, False) self.evaluate_periods([ TimePeriod(self.year2000, self.year2000 + self.ten_minutes), ]) def test_flight_period_missing_takeoff(self): """Missing takeoff reported as None.""" self.create_event(self.year2000, False) self.evaluate_periods([ TimePeriod(None, self.year2000), ]) # yapf: disable def test_flight_period_missing_landing(self): """Missing landing reported as None.""" self.create_event(self.year2000, True) self.evaluate_periods([ TimePeriod(self.year2000, None), ]) # yapf: disable def test_flight_period_multiple_flights(self): """Multiple flight reported together.""" self.create_event(self.year2000, True) self.create_event(self.year2000 + self.ten_minutes, False) self.create_event(self.year2001, True) self.create_event(self.year2001 + self.ten_minutes, False) self.evaluate_periods([ TimePeriod(self.year2000, self.year2000 + self.ten_minutes), TimePeriod(self.year2001, self.year2001 + self.ten_minutes), ]) def test_flight_period_multiple_flights_order(self): """Multiple flights listed in chronological order.""" self.create_event(self.year2001, True) self.create_event(self.year2001 + self.ten_minutes, False) self.create_event(self.year2000, True) self.create_event(self.year2000 + self.ten_minutes, False) self.evaluate_periods([ TimePeriod(self.year2000, self.year2000 + self.ten_minutes), TimePeriod(self.year2001, self.year2001 + self.ten_minutes), ]) def test_flight_period_specific_mission(self): """Tests that it only includes flights for specified mission.""" self.create_event(self.year2000, True) self.create_event(self.year2000 + self.ten_minutes, False) self.create_event(self.year2001, True, self.mission2) self.create_event(self.year2001 + self.ten_minutes, False, self.mission2) self.evaluate_periods([ TimePeriod(self.year2000, self.year2000 + self.ten_minutes), ]) def test_flight_period_missing_multiple(self): """Multiple flight can be missing details.""" # Forgot takeoff self.create_event(self.year2000, False) # Normal self.create_event(self.year2001, True) self.create_event(self.year2001 + self.ten_minutes, False) # Forgot landing self.create_event(self.year2002, True) self.evaluate_periods([ TimePeriod(None, self.year2000), TimePeriod(self.year2001, self.year2001 + self.ten_minutes), TimePeriod(self.year2002, None), ]) def test_flight_period_multiple_landing(self): """Double landing ignored""" self.create_event(self.year2000, True) self.create_event(self.year2000 + self.ten_minutes, False) # Land again? Ignored self.create_event(self.year2000 + 2 * self.ten_minutes, False) self.create_event(self.year2001, True) self.create_event(self.year2001 + self.ten_minutes, False) self.evaluate_periods([ TimePeriod(self.year2000, self.year2000 + self.ten_minutes), TimePeriod(self.year2001, self.year2001 + self.ten_minutes), ]) def test_flight_period_multiple_takeoff(self): """Double takeoff ignored.""" self.create_event(self.year2000, True) # Take off again? Ignored self.create_event(self.year2000 + self.ten_minutes, True) # Land self.create_event(self.year2000 + 2 * self.ten_minutes, False) self.create_event(self.year2001, True) self.create_event(self.year2001 + self.ten_minutes, False) self.evaluate_periods([ TimePeriod(self.year2000, self.year2000 + 2 * self.ten_minutes), TimePeriod(self.year2001, self.year2001 + self.ten_minutes), ]) def test_user_in_air_no_logs(self): """Not in-air without logs.""" self.assertFalse(TakeoffOrLandingEvent.user_in_air(self.user1)) def test_user_in_air_before_landing(self): """In-air before landing.""" self.create_event(self.year2000, True) self.assertTrue(TakeoffOrLandingEvent.user_in_air(self.user1)) def test_user_in_air_after_landing(self): """Not in-air after landing.""" self.create_event(self.year2000, True) self.create_event(self.year2000 + self.ten_minutes, False) self.assertFalse(TakeoffOrLandingEvent.user_in_air(self.user1)) def test_user_in_air_second_flight(self): """In-air during second flight.""" self.create_event(self.year2000, True) self.create_event(self.year2000 + self.ten_minutes, False) self.create_event(self.year2001, True) self.assertTrue(TakeoffOrLandingEvent.user_in_air(self.user1)) def test_user_in_air_time(self): """In-air base time check.""" self.create_event(self.year2000, True) self.create_event(self.year2000 + 2 * self.ten_minutes, False) time = self.year2000 + self.ten_minutes self.assertTrue( TakeoffOrLandingEvent.user_in_air(self.user1, time=time))
def test_satisfied_waypoints(self): """Tests the evaluation of waypoints method.""" # Create mission config gpos = GpsPosition() gpos.latitude = 10 gpos.longitude = 10 gpos.save() config = MissionConfig() config.home_pos = gpos config.emergent_last_known_pos = gpos config.off_axis_target_pos = gpos config.air_drop_pos = gpos config.save() # Create waypoints for config waypoints = [(38, -76, 100), (39, -77, 200), (40, -78, 0)] for i, waypoint in enumerate(waypoints): (lat, lon, alt) = waypoint pos = GpsPosition() pos.latitude = lat pos.longitude = lon pos.save() apos = AerialPosition() apos.altitude_msl = alt apos.gps_position = pos apos.save() wpt = Waypoint() wpt.position = apos wpt.order = i wpt.save() config.mission_waypoints.add(wpt) config.save() # Create UAS telemetry logs user = User.objects.create_user('testuser', '*****@*****.**', 'testpass') def assertSatisfiedWaypoints(expect, got): """Assert two satisfied_waypoints return values are equal.""" self.assertEqual(expect[0], got[0]) self.assertEqual(expect[1], got[1]) for k in expect[2].keys(): self.assertIn(k, got[2]) self.assertAlmostEqual(expect[2][k], got[2][k], delta=0.1) for k in got[2].keys(): self.assertIn(k, expect[2]) # Only first is valid. entries = [(38, -76, 140), (40, -78, 600), (37, -75, 40)] expect = (1, 1, {0: 40, 1: 460785.17}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # First and last are valid, but missed second, so third doesn't count. entries = [(38, -76, 140), (40, -78, 600), (40, -78, 40)] expect = (1, 1, {0: 40, 1: 460785.03}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Hit all. entries = [(38, -76, 140), (39, -77, 180), (40, -78, 40)] expect = (3, 3, {0: 40, 1: 20, 2: 40}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Hit all, but don't stay within waypoint track. entries = [(38, -76, 140), (39, -77, 180), (41, -78, 40), (40, -78, 40)] expect = (3, 2, {0: 40, 1: 20, 2: 40}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Only hit the first waypoint on run one, hit all on run two. entries = [ (38, -76, 140), (40, -78, 600), (37, -75, 40), # Run two: (38, -76, 140), (39, -77, 180), (40, -78, 40) ] expect = (3, 3, {0: 40, 1: 20, 2: 40}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Hit all on run one, only hit the first waypoint on run two. entries = [ (38, -76, 140), (39, -77, 180), (40, -78, 40), # Run two: (38, -76, 140), (40, -78, 600), (37, -75, 40) ] expect = (3, 3, {0: 40, 1: 20, 2: 40}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Remain on the waypoint track only on the second run. entries = [ (38, -76, 140), (39, -77, 180), (41, -78, 40), (40, -78, 40), # Run two: (38, -76, 140), (39, -77, 180), (40, -78, 40) ] expect = (3, 3, {0: 40, 1: 20, 2: 40}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Keep flying after hitting all waypoints. entries = [(38, -76, 140), (39, -77, 180), (40, -78, 40), (30.1, -78.1, 100)] expect = (3, 3, {0: 40, 1: 20, 2: 40}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Miss last target by a sane distance. entries = [(38, -76, 140), (39, -77, 180), (40, -78, 60)] expect = (2, 2, {0: 40, 1: 20, 2: 60}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs))
def test_satisfied_waypoints(self): """Tests the evaluation of waypoints method.""" # Create mission config gpos = GpsPosition() gpos.latitude = 10 gpos.longitude = 10 gpos.save() config = MissionConfig() config.home_pos = gpos config.emergent_last_known_pos = gpos config.off_axis_target_pos = gpos config.air_drop_pos = gpos config.save() # Create waypoints for config waypoints = [(38, -76, 100), (39, -77, 200), (40, -78, 0)] for i, waypoint in enumerate(waypoints): (lat, lon, alt) = waypoint pos = GpsPosition() pos.latitude = lat pos.longitude = lon pos.save() apos = AerialPosition() apos.altitude_msl = alt apos.gps_position = pos apos.save() wpt = Waypoint() wpt.position = apos wpt.order = i wpt.save() config.mission_waypoints.add(wpt) config.save() # Create UAS telemetry logs user = User.objects.create_user('testuser', '*****@*****.**', 'testpass') def assertSatisfiedWaypoints(expect, got): """Assert two satisfied_waypoints return values are equal.""" self.assertEqual(expect[0], got[0]) self.assertEqual(expect[1], got[1]) for k in expect[2].keys(): self.assertIn(k, got[2]) self.assertAlmostEqual(expect[2][k], got[2][k], delta=0.1) for k in got[2].keys(): self.assertIn(k, expect[2]) # Only first is valid. entries = [(38, -76, 140), (40, -78, 600), (37, -75, 40)] expect = (1, 1, {0: 40, 1: 460785.17}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # First and last are valid, but missed second, so third doesn't count. entries = [(38, -76, 140), (40, -78, 600), (40, -78, 40)] expect = (1, 1, {0: 40, 1: 460785.03}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Hit all. entries = [(38, -76, 140), (39, -77, 180), (40, -78, 40)] expect = (3, 3, {0: 40, 1: 20, 2: 40}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Hit all, but don't stay within waypoint track. entries = [(38, -76, 140), (39, -77, 180), (41, -78, 40), (40, -78, 40)] expect = (3, 2, {0: 40, 1: 20, 2: 40}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Only hit the first waypoint on run one, hit all on run two. entries = [(38, -76, 140), (40, -78, 600), (37, -75, 40), # Run two: (38, -76, 140), (39, -77, 180), (40, -78, 40)] expect = (3, 3, {0: 40, 1: 20, 2: 40}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Hit all on run one, only hit the first waypoint on run two. entries = [(38, -76, 140), (39, -77, 180), (40, -78, 40), # Run two: (38, -76, 140), (40, -78, 600), (37, -75, 40)] expect = (3, 3, {0: 40, 1: 20, 2: 40}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Remain on the waypoint track only on the second run. entries = [(38, -76, 140), (39, -77, 180), (41, -78, 40), (40, -78, 40), # Run two: (38, -76, 140), (39, -77, 180), (40, -78, 40)] expect = (3, 3, {0: 40, 1: 20, 2: 40}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Keep flying after hitting all waypoints. entries = [(38, -76, 140), (39, -77, 180), (40, -78, 40), (30.1, -78.1, 100)] expect = (3, 3, {0: 40, 1: 20, 2: 40}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Miss last target by a sane distance. entries = [(38, -76, 140), (39, -77, 180), (40, -78, 60)] expect = (2, 2, {0: 40, 1: 20, 2: 60}) logs = self.create_uas_logs(user, entries) assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs))
def test_satisfied_waypoints(self): """Tests the evaluation of waypoints method.""" # Create mission config gpos = GpsPosition() gpos.latitude = 10 gpos.longitude = 10 gpos.save() config = MissionConfig() config.home_pos = gpos config.emergent_last_known_pos = gpos config.off_axis_target_pos = gpos config.air_drop_pos = gpos config.save() # Create waypoints for config waypoints = [(38, -76, 100), (39, -77, 200), (40, -78, 0)] for i, waypoint in enumerate(waypoints): (lat, lon, alt) = waypoint pos = GpsPosition() pos.latitude = lat pos.longitude = lon pos.save() apos = AerialPosition() apos.altitude_msl = alt apos.gps_position = pos apos.save() wpt = Waypoint() wpt.position = apos wpt.order = i wpt.save() config.mission_waypoints.add(wpt) config.save() # Create UAS telemetry logs user = User.objects.create_user('testuser', '*****@*****.**', 'testpass') # Only first is valid. entries = [(38, -76, 140), (40, -78, 600), (37, -75, 40)] expect = ({0: 40}, {0: 0.6, 1: 0.0, 2: 0.0}, {0: 40, 1: 460785.17}) logs = self.create_uas_logs(user, entries) self.assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # First and last are valid, but missed second, so third doesn't count. entries = [(38, -76, 140), (40, -78, 600), (40, -78, 40)] expect = ({0: 40}, {0: 0.6, 1: 0.0, 2: 0.0}, {0: 40, 1: 460785.03}) logs = self.create_uas_logs(user, entries) self.assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Hit all. entries = [(38, -76, 140), (39, -77, 180), (40, -78, 40)] expect = ({0: 40, 1: 20, 2: 40}, {0: 0.6, 1: 0.8, 2: 0.6}, {0: 40, 1: 20, 2: 40}) # yapf: disable logs = self.create_uas_logs(user, entries) self.assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Only hit the first waypoint on run one, hit all on run two. entries = [(38, -76, 140), (40, -78, 600), (37, -75, 40), # Run two: (38, -76, 140), (39, -77, 180), (40, -78, 40)] expect = ({0: 40, 1: 20, 2: 40}, {0: 0.6, 1: 0.8, 2: 0.6}, {0: 40, 1: 20, 2: 40}) # yapf: disable logs = self.create_uas_logs(user, entries) self.assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Hit all on run one, only hit the first waypoint on run two. entries = [(38, -76, 140), (39, -77, 180), (40, -78, 40), # Run two: (38, -76, 140), (40, -78, 600), (37, -75, 40)] expect = ({0: 40, 1: 20, 2: 40}, {0: 0.6, 1: 0.8, 2: 0.6}, {0: 40, 1: 20, 2: 40}) # yapf: disable logs = self.create_uas_logs(user, entries) self.assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Keep flying after hitting all waypoints. entries = [(38, -76, 140), (39, -77, 180), (40, -78, 40), (30.1, -78.1, 100)] expect = ({0: 40, 1: 20, 2: 40}, {0: 0.6, 1: 0.8, 2: 0.6}, {0: 40, 1: 20, 2: 40}) # yapf: disable logs = self.create_uas_logs(user, entries) self.assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Hit all in first run, but second is higher scoring. entries = [(38, -76, 140), (39, -77, 180), (40, -78, 60), # Run two: (38, -76, 100), (39, -77, 200), (40, -78, 110)] expect = ({0: 0, 1: 0}, {0: 1, 1: 1, 2: 0}, {0: 0, 1: 0, 2: 60}) logs = self.create_uas_logs(user, entries) self.assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs)) # Restart waypoint path in the middle. waypoints = [(38, -76, 100), (39, -77, 200), (40, -78, 0)] entries = [(38, -76, 140), (39, -77, 180), # Restart: (38, -76, 70), (39, -77, 150), (40, -78, 10)] expect = ({0: 30, 1: 50, 2: 10}, {0: 0.7, 1: 0.5, 2: 0.9}, {0: 30, 1: 20, 2: 10}) # yapf: disable logs = self.create_uas_logs(user, entries) self.assertSatisfiedWaypoints(expect, config.satisfied_waypoints(logs))
class TestOdlc(TestCase): """Tests for the Odlc model.""" def setUp(self): """Sets up the tests.""" super(TestOdlc, self).setUp() self.user = User.objects.create_user('user', '*****@*****.**', 'pass') # Mission pos = GpsPosition() pos.latitude = 10 pos.longitude = 100 pos.save() wpt = Waypoint() wpt.latitude = 10 wpt.longitude = 100 wpt.altitude_msl = 1000 wpt.order = 10 wpt.save() self.mission = MissionConfig() self.mission.home_pos = pos self.mission.lost_comms_pos = pos self.mission.emergent_last_known_pos = pos self.mission.off_axis_odlc_pos = pos self.mission.map_center_pos = pos self.mission.map_height_ft = 1 self.mission.air_drop_pos = pos self.mission.ugv_drive_pos = pos self.mission.save() self.mission.mission_waypoints.add(wpt) self.mission.search_grid_points.add(wpt) self.mission.save() def test_valid(self): """Test creating a valid odlc.""" with open(os.path.join(settings.BASE_DIR, 'auvsi_suas/testdata/S.jpg'), 'rb') as f: thumb = SimpleUploadedFile('thumb.jpg', f.read()) l = GpsPosition(latitude=38, longitude=-76) l.save() t = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD, location=l, orientation=interop_api_pb2.Odlc.S, shape=interop_api_pb2.Odlc.SQUARE, shape_color=interop_api_pb2.Odlc.WHITE, alphanumeric='ABC', alphanumeric_color=interop_api_pb2.Odlc.BLACK, description='Test odlc', thumbnail=thumb) t.save() def test_null_fields(self): """Only user and odlc type.""" t = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD) t.save() def test_creation_time(self): """Creation time is set on creation and doesn't change on update.""" t = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD) t.save() orig = t.creation_time self.assertIsNotNone(orig) t.alphanumeric = 'A' t.save() self.assertEqual(orig, t.creation_time) def test_last_modified_time(self): """Last modified time is set on creation and changes every update.""" t = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD) t.save() orig = t.last_modified_time self.assertIsNotNone(orig) t.alphanumeric = 'A' t.update_last_modified() t.save() self.assertGreater(t.last_modified_time, orig) def test_similar_orientation(self): """Test similar orientations are computed correctly.""" l = GpsPosition(latitude=38, longitude=-76) l.save() t1 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD, location=l, orientation=interop_api_pb2.Odlc.S, shape=interop_api_pb2.Odlc.SQUARE, shape_color=interop_api_pb2.Odlc.WHITE, alphanumeric='ABC', alphanumeric_color=interop_api_pb2.Odlc.BLACK, description='Test odlc', description_approved=True, autonomous=True) t1.save() t2 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD, location=l, orientation=interop_api_pb2.Odlc.S, shape=interop_api_pb2.Odlc.SQUARE, shape_color=interop_api_pb2.Odlc.WHITE, alphanumeric='ABC', alphanumeric_color=interop_api_pb2.Odlc.BLACK, description='Test other odlc', description_approved=False, autonomous=True) t2.save() # Requires exact same orientation. for alpha in ['A', 'a']: t1.alphanumeric = alpha t1.orientation = interop_api_pb2.Odlc.S t2.orientation = interop_api_pb2.Odlc.S self.assertTrue(t1.similar_orientation(t2)) t2.orientation = interop_api_pb2.Odlc.N self.assertFalse(t1.similar_orientation(t2)) t2.orientation = interop_api_pb2.Odlc.E self.assertFalse(t1.similar_orientation(t2)) # Accepts rotation. for alpha in ['I', 'H', '8']: t1.alphanumeric = alpha t1.orientation = interop_api_pb2.Odlc.S t2.orientation = interop_api_pb2.Odlc.S self.assertTrue(t1.similar_orientation(t2)) t2.orientation = interop_api_pb2.Odlc.N self.assertTrue(t1.similar_orientation(t2)) t2.orientation = interop_api_pb2.Odlc.E self.assertFalse(t1.similar_orientation(t2)) # Accepts any. for alpha in ['O', 'o', '0']: t1.alphanumeric = alpha t1.orientation = interop_api_pb2.Odlc.S t2.orientation = interop_api_pb2.Odlc.S self.assertTrue(t1.similar_orientation(t2)) t2.orientation = interop_api_pb2.Odlc.N self.assertTrue(t1.similar_orientation(t2)) t2.orientation = interop_api_pb2.Odlc.E self.assertTrue(t1.similar_orientation(t2)) def test_similar_classifications_ratio(self): """Tests similar classification ratios are computed correctly.""" # Test equal standard odlcs. l = GpsPosition(latitude=38, longitude=-76) l.save() t1 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD, location=l, orientation=interop_api_pb2.Odlc.S, shape=interop_api_pb2.Odlc.SQUARE, shape_color=interop_api_pb2.Odlc.WHITE, alphanumeric='ABC', alphanumeric_color=interop_api_pb2.Odlc.BLACK, description='Test odlc', description_approved=True, autonomous=True) t1.save() t2 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD, location=l, orientation=interop_api_pb2.Odlc.S, shape=interop_api_pb2.Odlc.SQUARE, shape_color=interop_api_pb2.Odlc.WHITE, alphanumeric='ABC', alphanumeric_color=interop_api_pb2.Odlc.BLACK, description='Test other odlc', description_approved=False, autonomous=True) t2.save() self.assertAlmostEqual(1.0, t1.similar_classifications_ratio(t2)) # Test unequal standard odlcs. t1.alphanumeric = 'DEF' t1.alphanumeric_color = interop_api_pb2.Odlc.BLUE t1.save() self.assertAlmostEqual(3.0 / 5.0, t1.similar_classifications_ratio(t2)) t1.shape = interop_api_pb2.Odlc.CIRCLE t1.shape_color = interop_api_pb2.Odlc.ORANGE t1.save() self.assertAlmostEqual(1.0 / 5.0, t1.similar_classifications_ratio(t2)) # Test emergent type based on description approval. t1.odlc_type = interop_api_pb2.Odlc.EMERGENT t1.save() t2.odlc_type = interop_api_pb2.Odlc.EMERGENT t2.save() self.assertAlmostEqual(0.0, t1.similar_classifications_ratio(t2)) t2.description_approved = True t2.save() self.assertAlmostEqual(1.0, t1.similar_classifications_ratio(t2)) def test_actionable_submission(self): """Tests actionable_submission correctly filters submissions.""" # t1 created and updated before take off. t1 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD) t1.save() t1.alphanumeric = 'A' t1.update_last_modified() t1.save() # t2 created before take off and updated in flight. t2 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD) t2.save() event = TakeoffOrLandingEvent(user=self.user, mission=self.mission, uas_in_air=True) event.save() t2.alphanumeric = 'A' t2.update_last_modified() t2.save() # t3 created and updated in flight. t3 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD) t3.save() t3.alphanumeric = 'A' t3.update_last_modified() t3.save() # t4 created in flight and updated after landing. t4 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD) t4.save() event = TakeoffOrLandingEvent(user=self.user, mission=self.mission, uas_in_air=False) event.save() t4.alphanumeric = 'A' t4.update_last_modified() t4.save() # t5 created and updated after landing. t5 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD) t5.save() t5.alphanumeric = 'A' t5.update_last_modified() t5.save() # t6 created and updated in second flight. event = TakeoffOrLandingEvent(user=self.user, mission=self.mission, uas_in_air=True) event.save() t6 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD) t6.save() t6.alphanumeric = 'A' t6.update_last_modified() t6.save() event = TakeoffOrLandingEvent(user=self.user, mission=self.mission, uas_in_air=False) event.save() # t7 which is not actionable. event = TakeoffOrLandingEvent(user=self.user, mission=self.mission, uas_in_air=True) event.save() t7 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD) event = TakeoffOrLandingEvent(user=self.user, mission=self.mission, uas_in_air=False) event.save() flights = TakeoffOrLandingEvent.flights(self.mission, self.user) self.assertFalse(t1.actionable_submission(flights)) self.assertFalse(t2.actionable_submission(flights)) self.assertTrue(t3.actionable_submission(flights)) self.assertFalse(t4.actionable_submission(flights)) self.assertFalse(t5.actionable_submission(flights)) self.assertFalse(t6.actionable_submission(flights)) self.assertFalse(t7.actionable_submission(flights))
class TestOdlcEvaluator(TestCase): """Tests for the OdlcEvaluator.""" def setUp(self): """Setup the test case.""" super(TestOdlcEvaluator, self).setUp() self.maxDiff = None self.user = User.objects.create_user('user', '*****@*****.**', 'pass') l1 = GpsPosition(latitude=38, longitude=-76) l1.save() l2 = GpsPosition(latitude=38.0003, longitude=-76) l2.save() l3 = GpsPosition(latitude=-38, longitude=76) l3.save() l4 = GpsPosition(latitude=0, longitude=0) l4.save() # Mission pos = GpsPosition() pos.latitude = 10 pos.longitude = 100 pos.save() wpt = Waypoint() wpt.order = 10 wpt.latitude = 10 wpt.longitude = 100 wpt.altitude_msl = 1000 wpt.save() self.mission = MissionConfig() self.mission.home_pos = pos self.mission.lost_comms_pos = pos self.mission.emergent_last_known_pos = pos self.mission.off_axis_odlc_pos = pos self.mission.map_center_pos = pos self.mission.map_height_ft = 1 self.mission.air_drop_pos = pos self.mission.ugv_drive_pos = pos self.mission.save() self.mission.mission_waypoints.add(wpt) self.mission.search_grid_points.add(wpt) self.mission.save() event = TakeoffOrLandingEvent(user=self.user, mission=self.mission, uas_in_air=True) event.save() # A odlc worth full points. self.submit1 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD, location=l1, orientation=interop_api_pb2.Odlc.S, shape=interop_api_pb2.Odlc.SQUARE, shape_color=interop_api_pb2.Odlc.WHITE, alphanumeric='ABC', alphanumeric_color=interop_api_pb2.Odlc.BLACK, description='Submit test odlc 1', description_approved=True, autonomous=True, thumbnail_approved=True) self.submit1.save() self.real1 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD, location=l1, orientation=interop_api_pb2.Odlc.S, shape=interop_api_pb2.Odlc.SQUARE, shape_color=interop_api_pb2.Odlc.WHITE, alphanumeric='ABC', alphanumeric_color=interop_api_pb2.Odlc.BLACK, description='Real odlc 1') self.real1.save() # A odlc worth less than full points. self.submit2 = Odlc( mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD, location=l1, orientation=interop_api_pb2.Odlc.N, shape=interop_api_pb2.Odlc.CIRCLE, shape_color=interop_api_pb2.Odlc.WHITE, # alphanumeric set below alphanumeric_color=interop_api_pb2.Odlc.BLACK, description='Submit test odlc 2', autonomous=False, thumbnail_approved=True) self.submit2.save() self.real2 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD, location=l2, orientation=interop_api_pb2.Odlc.S, shape=interop_api_pb2.Odlc.TRIANGLE, shape_color=interop_api_pb2.Odlc.WHITE, alphanumeric='ABC', alphanumeric_color=interop_api_pb2.Odlc.BLACK, description='Real test odlc 2') self.real2.save() # A odlc worth no points, so unmatched. self.submit3 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD, location=l4, orientation=interop_api_pb2.Odlc.NW, shape=interop_api_pb2.Odlc.PENTAGON, shape_color=interop_api_pb2.Odlc.GRAY, alphanumeric='XYZ', alphanumeric_color=interop_api_pb2.Odlc.ORANGE, description='Incorrect description', autonomous=False, thumbnail_approved=True) self.submit3.save() self.real3 = Odlc( mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD, orientation=interop_api_pb2.Odlc.E, shape=interop_api_pb2.Odlc.SEMICIRCLE, shape_color=interop_api_pb2.Odlc.YELLOW, alphanumeric='LMN', # alphanumeric_color set below location=l3, description='Test odlc 3') self.real3.save() # Odlcs without approved image has no match value. self.submit4 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.EMERGENT, location=l1, description='Test odlc 4', autonomous=False, thumbnail_approved=False) self.submit4.save() self.real4 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.EMERGENT, location=l1, description='Test odlc 4') self.real4.save() # A odlc without location worth fewer points. self.submit5 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD, orientation=interop_api_pb2.Odlc.N, shape=interop_api_pb2.Odlc.TRAPEZOID, shape_color=interop_api_pb2.Odlc.PURPLE, alphanumeric='PQR', alphanumeric_color=interop_api_pb2.Odlc.BLUE, description='Test odlc 5', autonomous=False, thumbnail_approved=True) self.submit5.save() self.real5 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD, location=l1, orientation=interop_api_pb2.Odlc.N, shape=interop_api_pb2.Odlc.TRAPEZOID, shape_color=interop_api_pb2.Odlc.PURPLE, alphanumeric='PQR', alphanumeric_color=interop_api_pb2.Odlc.BLUE, description='Test odlc 5') self.real5.save() # Emergent odlc with correct description. self.submit6 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.EMERGENT, location=l1, description='Submit test odlc 6', description_approved=True, autonomous=True, thumbnail_approved=True) self.submit6.save() self.real6 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.EMERGENT, location=l1, description='Real odlc 1', description_approved=True) self.real6.save() event = TakeoffOrLandingEvent(user=self.user, mission=self.mission, uas_in_air=False) event.save() # submit2 updated after landing. self.submit2.alphanumeric = 'ABC' self.submit2.update_last_modified() self.submit2.save() self.submit2.update_last_modified() self.submit3.alphanumeric_color = interop_api_pb2.Odlc.YELLOW self.submit3.update_last_modified() self.submit3.save() # Unused but not unmatched odlc. self.submit7 = Odlc(mission=self.mission, user=self.user, odlc_type=interop_api_pb2.Odlc.STANDARD, location=l4, alphanumeric_color=interop_api_pb2.Odlc.BLACK, description='Submit unused test odlc 1', autonomous=False, thumbnail_approved=True) self.submit7.save() self.submitted_odlcs = [ self.submit7, self.submit6, self.submit5, self.submit4, self.submit3, self.submit2, self.submit1 ] self.real_odlcs = [ self.real1, self.real2, self.real3, self.real4, self.real5, self.real6 ] self.flights = TakeoffOrLandingEvent.flights(self.mission, self.user) def test_match_value(self): """Tests the match value for two odlcs.""" e = OdlcEvaluator(self.submitted_odlcs, self.real_odlcs, self.flights) self.assertAlmostEqual(1.0, e.evaluate_match(self.submit1, self.real1).score_ratio, places=3) self.assertAlmostEqual(0.3481, e.evaluate_match(self.submit2, self.real2).score_ratio, places=3) self.assertAlmostEqual(0.0, e.evaluate_match(self.submit3, self.real3).score_ratio, places=3) self.assertAlmostEqual(0.0, e.evaluate_match(self.submit4, self.real4).score_ratio, places=3) self.assertAlmostEqual(0.5, e.evaluate_match(self.submit5, self.real5).score_ratio, places=3) self.assertAlmostEqual(1.0, e.evaluate_match(self.submit6, self.real6).score_ratio, places=3) self.assertAlmostEqual(0.08, e.evaluate_match(self.submit7, self.real1).score_ratio, places=3) self.assertAlmostEqual(0.628, e.evaluate_match(self.submit1, self.real2).score_ratio, places=3) self.assertAlmostEqual(0.64, e.evaluate_match(self.submit2, self.real1).score_ratio, places=3) def test_match_odlcs(self): """Tests that matching odlcs produce maximal matches.""" e = OdlcEvaluator(self.submitted_odlcs, self.real_odlcs, self.flights) self.assertDictEqual( { self.submit1: self.real1, self.submit2: self.real2, self.submit5: self.real5, self.submit6: self.real6, self.real1: self.submit1, self.real2: self.submit2, self.real5: self.submit5, self.real6: self.submit6, }, e.match_odlcs(self.submitted_odlcs, self.real_odlcs)) def test_evaluate(self): """Tests that the evaluation is generated correctly.""" e = OdlcEvaluator(self.submitted_odlcs, self.real_odlcs, self.flights) d = e.evaluate() td = {t.real_odlc: t for t in d.odlcs} self.assertEqual(self.submit1.pk, td[self.real1.pk].submitted_odlc) self.assertEqual(True, td[self.real1.pk].image_approved) self.assertEqual(1.0, td[self.real1.pk].classifications_ratio) self.assertEqual(0.0, td[self.real1.pk].geolocation_accuracy_ft) self.assertEqual(True, td[self.real1.pk].actionable_submission) self.assertEqual(1.0, td[self.real1.pk].classifications_score_ratio) self.assertEqual(1.0, td[self.real1.pk].geolocation_score_ratio) self.assertEqual(1.0, td[self.real1.pk].actionable_score_ratio) self.assertEqual(1.0, td[self.real1.pk].autonomous_score_ratio) self.assertAlmostEqual(1.0, td[self.real1.pk].score_ratio) self.assertEqual(self.submit2.pk, td[self.real2.pk].submitted_odlc) self.assertEqual(True, td[self.real2.pk].image_approved) self.assertEqual(0.6, td[self.real2.pk].classifications_ratio) self.assertAlmostEqual(109.444, td[self.real2.pk].geolocation_accuracy_ft, places=3) self.assertEqual(False, td[self.real2.pk].actionable_submission) self.assertEqual(0.6, td[self.real2.pk].classifications_score_ratio) self.assertAlmostEqual(0.270, td[self.real2.pk].geolocation_score_ratio, places=3) self.assertEqual(0.0, td[self.real2.pk].actionable_score_ratio) self.assertAlmostEqual(0.3481, td[self.real2.pk].score_ratio, places=3) self.assertEqual(True, td[self.real6.pk].description_approved) self.assertAlmostEqual(0.375, d.score_ratio, places=3) self.assertEqual(2, d.unmatched_odlc_count) def test_evaluate_no_submitted_odlcs(self): """Tests that evaluation works with no submitted odlcs.""" e = OdlcEvaluator([], self.real_odlcs, self.flights) d = e.evaluate() self.assertEqual(0, d.matched_score_ratio) self.assertEqual(0, d.unmatched_odlc_count) self.assertEqual(6, len(d.odlcs)) def test_evaluate_no_real_odlcs(self): """Tests that evaluation works with no real odlcs.""" e = OdlcEvaluator(self.submitted_odlcs, [], self.flights) d = e.evaluate() self.assertEqual(0, d.matched_score_ratio) self.assertEqual(7, d.unmatched_odlc_count) self.assertAlmostEqual(-0.35, d.score_ratio, places=3) self.assertEqual(0, len(d.odlcs))
def create_sample_mission(superuser): """Creates a sample mission. Args: superuser: A superuser account to create mission under. Returns: MissionConfig for the created mission. """ mission = MissionConfig() gpos = GpsPosition(latitude=38.145103, longitude=-76.427856) gpos.save() mission.home_pos = gpos gpos = GpsPosition(latitude=38.145111, longitude=-76.427861) gpos.save() mission.emergent_last_known_pos = gpos gpos = GpsPosition(latitude=38.145111, longitude=-76.427861) gpos.save() mission.off_axis_odlc_pos = gpos gpos = GpsPosition(latitude=38.1458416666667, longitude=-76.426375) gpos.save() mission.air_drop_pos = gpos # All foreign keys must be defined before the first save. # All many-to-many must be defined after the first save. mission.save() bounds = FlyZone(altitude_msl_min=100, altitude_msl_max=750) bounds.save() # yapf: disable pts = [(38.1462694444444, -76.4281638888889), (38.1516250000000, -76.4286833333333), (38.1518888888889, -76.4314666666667), (38.1505944444444, -76.4353611111111), (38.1475666666667, -76.4323416666667), (38.1446666666667, -76.4329472222222), (38.1432555555556, -76.4347666666667), (38.1404638888889, -76.4326361111111), (38.1407194444444, -76.4260138888889), (38.1437611111111, -76.4212055555556), (38.1473472222222, -76.4232111111111), (38.1461305555556, -76.4266527777778)] # yapf: enable for ix, (lat, lon) in enumerate(pts): wpt = Waypoint(latitude=lat, longitude=lon, altitude_msl=0, order=ix + 1) wpt.save() bounds.boundary_pts.add(wpt) bounds.save() mission.fly_zones.add(bounds) # yapf: disable pts = [(38.146689, -76.426475, 150, 750), (38.142914, -76.430297, 300, 300), (38.149504, -76.433110, 100, 750), (38.148711, -76.429061, 300, 750), (38.144203, -76.426155, 50, 400), (38.146003, -76.430733, 225, 500)] # yapf: enable for lat, lon, radius, height in pts: obst = StationaryObstacle(latitude=lat, longitude=lon, cylinder_radius=radius, cylinder_height=height) obst.save() mission.stationary_obstacles.add(obst) # yapf: disable pts = [(38.1446916666667, -76.4279944444445, 200), (38.1461944444444, -76.4237138888889, 300), (38.1438972222222, -76.4225500000000, 400), (38.1417722222222, -76.4251083333333, 400), (38.1453500000000, -76.4286750000000, 300), (38.1508972222222, -76.4292972222222, 300), (38.1514944444444, -76.4313833333333, 300), (38.1505333333333, -76.4341750000000, 300), (38.1479472222222, -76.4316055555556, 200), (38.1443333333333, -76.4322888888889, 200), (38.1433166666667, -76.4337111111111, 300), (38.1410944444444, -76.4321555555556, 400), (38.1415777777778, -76.4252472222222, 400), (38.1446083333333, -76.4282527777778, 200)] # yapf: enable for ix, (lat, lon, alt) in enumerate(pts): wpt = Waypoint(latitude=lat, longitude=lon, altitude_msl=alt, order=ix + 1) wpt.save() mission.mission_waypoints.add(wpt) # yapf: disable pts = [(38.1444444444444, -76.4280916666667), (38.1459444444444, -76.4237944444445), (38.1439305555556, -76.4227444444444), (38.1417138888889, -76.4253805555556), (38.1412111111111, -76.4322361111111), (38.1431055555556, -76.4335972222222), (38.1441805555556, -76.4320111111111), (38.1452611111111, -76.4289194444444), (38.1444444444444, -76.4280916666667)] # yapf: enable for ix, (lat, lon) in enumerate(pts): wpt = Waypoint(latitude=lat, longitude=lon, altitude_msl=0, order=ix + 1) wpt.save() mission.search_grid_points.add(wpt) gpos = GpsPosition(latitude=38.143844, longitude=-76.426469) gpos.save() odlc = Odlc(mission=mission, user=superuser, odlc_type=interop_api_pb2.Odlc.STANDARD, location=gpos, orientation=interop_api_pb2.Odlc.N, shape=interop_api_pb2.Odlc.STAR, shape_color=interop_api_pb2.Odlc.RED, alphanumeric='A', alphanumeric_color=interop_api_pb2.Odlc.WHITE) odlc.save() mission.odlcs.add(odlc) gpos = GpsPosition(latitude=38.141872, longitude=-76.426183) gpos.save() odlc = Odlc(mission=mission, user=superuser, odlc_type=interop_api_pb2.Odlc.STANDARD, location=gpos, orientation=interop_api_pb2.Odlc.NE, shape=interop_api_pb2.Odlc.CROSS, shape_color=interop_api_pb2.Odlc.BLUE, alphanumeric='I', alphanumeric_color=interop_api_pb2.Odlc.BLACK) odlc.save() mission.odlcs.add(odlc) gpos = GpsPosition(latitude=38.142828, longitude=-76.427644) gpos.save() odlc = Odlc(mission=mission, user=superuser, odlc_type=interop_api_pb2.Odlc.STANDARD, location=gpos, orientation=interop_api_pb2.Odlc.E, shape=interop_api_pb2.Odlc.QUARTER_CIRCLE, shape_color=interop_api_pb2.Odlc.YELLOW, alphanumeric='R', alphanumeric_color=interop_api_pb2.Odlc.ORANGE) odlc.save() mission.odlcs.add(odlc) gpos = GpsPosition(latitude=38.144925, longitude=-76.425100) gpos.save() odlc = Odlc(mission=mission, user=superuser, odlc_type=interop_api_pb2.Odlc.STANDARD, location=gpos, orientation=interop_api_pb2.Odlc.SE, shape=interop_api_pb2.Odlc.CIRCLE, shape_color=interop_api_pb2.Odlc.BROWN, alphanumeric='V', alphanumeric_color=interop_api_pb2.Odlc.RED) odlc.save() mission.odlcs.add(odlc) gpos = GpsPosition(latitude=38.146747, longitude=-76.422131) gpos.save() odlc = Odlc(mission=mission, user=superuser, odlc_type=interop_api_pb2.Odlc.STANDARD, location=gpos, orientation=interop_api_pb2.Odlc.S, shape=interop_api_pb2.Odlc.TRAPEZOID, shape_color=interop_api_pb2.Odlc.WHITE, alphanumeric='E', alphanumeric_color=interop_api_pb2.Odlc.GRAY) odlc.save() mission.odlcs.add(odlc) gpos = GpsPosition(latitude=38.144097, longitude=-76.431089) gpos.save() odlc = Odlc(mission=mission, user=superuser, odlc_type=interop_api_pb2.Odlc.STANDARD, location=gpos, orientation=interop_api_pb2.Odlc.SW, shape=interop_api_pb2.Odlc.SQUARE, shape_color=interop_api_pb2.Odlc.GREEN, alphanumeric='H', alphanumeric_color=interop_api_pb2.Odlc.BLUE) odlc.save() mission.odlcs.add(odlc) gpos = GpsPosition(latitude=38.144878, longitude=-76.423681) gpos.save() odlc = Odlc(mission=mission, user=superuser, odlc_type=interop_api_pb2.Odlc.STANDARD, location=gpos, orientation=interop_api_pb2.Odlc.W, shape=interop_api_pb2.Odlc.RECTANGLE, shape_color=interop_api_pb2.Odlc.PURPLE, alphanumeric='I', alphanumeric_color=interop_api_pb2.Odlc.GREEN) odlc.save() mission.odlcs.add(odlc) gpos = GpsPosition(latitude=38.142819, longitude=-76.432375) gpos.save() odlc = Odlc(mission=mission, user=superuser, odlc_type=interop_api_pb2.Odlc.STANDARD, location=gpos, orientation=interop_api_pb2.Odlc.NW, shape=interop_api_pb2.Odlc.SEMICIRCLE, shape_color=interop_api_pb2.Odlc.ORANGE, alphanumeric='C', alphanumeric_color=interop_api_pb2.Odlc.YELLOW) odlc.save() mission.odlcs.add(odlc) gpos = GpsPosition(latitude=38.141639, longitude=-76.429347) gpos.save() odlc = Odlc(mission=mission, user=superuser, odlc_type=interop_api_pb2.Odlc.STANDARD, location=gpos, orientation=interop_api_pb2.Odlc.N, shape=interop_api_pb2.Odlc.TRIANGLE, shape_color=interop_api_pb2.Odlc.BLACK, alphanumeric='L', alphanumeric_color=interop_api_pb2.Odlc.PURPLE) odlc.save() mission.odlcs.add(odlc) gpos = GpsPosition(latitude=38.142478, longitude=-76.424967) gpos.save() odlc = Odlc(mission=mission, user=superuser, odlc_type=interop_api_pb2.Odlc.STANDARD, location=gpos, orientation=interop_api_pb2.Odlc.NE, shape=interop_api_pb2.Odlc.PENTAGON, shape_color=interop_api_pb2.Odlc.GRAY, alphanumeric='E', alphanumeric_color=interop_api_pb2.Odlc.BROWN) odlc.save() mission.odlcs.add(odlc) gpos = GpsPosition(latitude=38.143411, longitude=-76.424489) gpos.save() odlc = Odlc(mission=mission, user=superuser, odlc_type=interop_api_pb2.Odlc.EMERGENT, location=gpos, description='Randy the backpacker.') odlc.save() mission.odlcs.add(odlc) mission.save() return mission