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()
def test_valid(self): """Test creating a valid odlc.""" with open( os.path.join(settings.BASE_DIR, 'auvsi_suas/fixtures/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, background_color=interop_api_pb2.Odlc.WHITE, alphanumeric='ABC', alphanumeric_color=interop_api_pb2.Odlc.BLACK, description='Test odlc', thumbnail=thumb) t.save()
def test_valid(self): """Test creating a valid odlc.""" with open( os.path.join(settings.BASE_DIR, 'auvsi_suas/fixtures/testdata/S.jpg')) as f: thumb = SimpleUploadedFile('thumb.jpg', f.read()) l = GpsPosition(latitude=38, longitude=-76) l.save() t = Odlc( user=self.user, odlc_type=OdlcType.standard, location=l, orientation=Orientation.s, shape=Shape.square, background_color=Color.white, alphanumeric='ABC', alphanumeric_color=Color.black, description='Test odlc', thumbnail=thumb) t.save()
def test_contains_pos(self): """Tests the inside obstacle method.""" # Form the test obstacle obst = MovingObstacle() obst.sphere_radius = TESTDATA_MOVOBST_CONTAINSPOS_OBJ[2] # Run test points against obstacle test_data = [(TESTDATA_MOVOBST_CONTAINSPOS_INSIDE, True), (TESTDATA_MOVOBST_CONTAINSPOS_OUTSIDE, False)] for (cur_data, cur_contains) in test_data: for (lat, lon, alt) in cur_data: gpos = GpsPosition() gpos.latitude = lat gpos.longitude = lon gpos.save() apos = AerialPosition() apos.gps_position = gpos apos.altitude_msl = alt self.assertEqual( obst.contains_pos(TESTDATA_MOVOBST_CONTAINSPOS_OBJ[0], TESTDATA_MOVOBST_CONTAINSPOS_OBJ[1], TESTDATA_MOVOBST_CONTAINSPOS_OBJ[3], apos), cur_contains)
def test_json(self): """Tests the JSON serialization method.""" TEST_LAT = 100.10 TEST_LONG = 200.20 TEST_RADIUS = 150.50 TEST_HEIGHT = 75.30 gpos = GpsPosition(latitude=TEST_LAT, longitude=TEST_LONG) gpos.save() obstacle = StationaryObstacle(gps_position=gpos, cylinder_radius=TEST_RADIUS, cylinder_height=TEST_HEIGHT) json_data = obstacle.json() self.assertTrue('latitude' in json_data) self.assertEqual(json_data['latitude'], TEST_LAT) self.assertTrue('longitude' in json_data) self.assertEqual(json_data['longitude'], TEST_LONG) self.assertTrue('cylinder_radius' in json_data) self.assertEqual(json_data['cylinder_radius'], TEST_RADIUS) self.assertTrue('cylinder_height' in json_data) self.assertEqual(json_data['cylinder_height'], TEST_HEIGHT)
def test_unicode(self): """Test unicode conversion.""" with open(os.path.join(settings.BASE_DIR, 'auvsi_suas/fixtures/testdata/S.jpg')) as f: thumb = SimpleUploadedFile('thumb.jpg', f.read()) l = GpsPosition(latitude=38, longitude=-76) l.save() t = Target(user=self.user, target_type=TargetType.standard, location=l, orientation=Orientation.s, shape=Shape.square, background_color=Color.white, alphanumeric='ABC', alphanumeric_color=Color.black, description='Test target', thumbnail=thumb) t.save() self.assertTrue(t.__unicode__())
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_logs(self, user, num=10, start=None, delta=None): if start is None: start = timezone.now() if delta is None: delta = datetime.timedelta(seconds=1) logs = [] for i in xrange(num): gps_position = GpsPosition(latitude=0, longitude=0) gps_position.save() uas_position = AerialPosition(gps_position=gps_position, altitude_msl=0) uas_position.save() log = UasTelemetry(user=user, uas_position=uas_position, uas_heading=0.0) log.save() log.timestamp = start + i * delta log.save() logs.append(log) return logs
def test_put_clear_shape(self): """PUT clear a field with None.""" 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, background_color=interop_api_pb2.Odlc.WHITE, alphanumeric='ABC', alphanumeric_color=interop_api_pb2.Odlc.BLACK, description='Test odlc') t.save() updated = { 'mission': self.mission.pk, 'type': 'STANDARD', } response = self.client.put( odlcs_id_url(args=[t.pk]), data=json.dumps(updated)) self.assertEqual(200, response.status_code) t.refresh_from_db() self.assertEqual(self.user, t.user) self.assertEqual(interop_api_pb2.Odlc.STANDARD, t.odlc_type) self.assertIsNone(t.location) self.assertIsNone(t.orientation) self.assertIsNone(t.shape) self.assertIsNone(t.background_color) self.assertEqual('', t.alphanumeric) self.assertIsNone(t.alphanumeric_color) self.assertEqual('', t.description)
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() # user1 is flying event = TakeoffOrLandingEvent(user=self.user1, uas_in_air=True) event.save() # user2 has landed event = TakeoffOrLandingEvent(user=self.user2, uas_in_air=True) event.save() event = TakeoffOrLandingEvent(user=self.user2, uas_in_air=False) event.save() # user2 is active self.timestamp = timezone.now() gps = GpsPosition(latitude=38.6462, longitude=-76.2452) gps.save() pos = AerialPosition(gps_position=gps, altitude_msl=0) pos.save() self.telem = UasTelemetry(user=self.user2, uas_position=pos, 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_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__())
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.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() self.now = timezone.now()
def test_satisfied_waypoints(self): """Tests the evaluation of waypoints method.""" # Create mission config gpos = GpsPosition() gpos.latitude = 10 gpos.longitude = 10 gpos.save() # Create waypoints for testing. waypoints = self.waypoints_from_data([(38, -76, 100), (39, -77, 200), ( 40, -78, 0)]) # Create UAS telemetry logs # Only first is valid. entries = [(38, -76, 140), (40, -78, 600), (37, -75, 40)] logs = self.create_uas_logs(self.user, entries) expect = [WaypointEvaluation(id=0, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40), WaypointEvaluation(id=1, score_ratio=0, closest_for_mission_ft=460785.17), WaypointEvaluation(id=2, score_ratio=0)] self.assertSatisfiedWaypoints(expect, UasTelemetry.satisfied_waypoints( gpos, 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)] logs = self.create_uas_logs(self.user, entries) expect = [WaypointEvaluation(id=0, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40), WaypointEvaluation(id=1, score_ratio=0, closest_for_mission_ft=460785.03), WaypointEvaluation(id=2, score_ratio=0)] self.assertSatisfiedWaypoints(expect, UasTelemetry.satisfied_waypoints( gpos, waypoints, logs)) # Hit all. entries = [(38, -76, 140), (39, -77, 180), (40, -78, 40)] expect = [WaypointEvaluation(id=0, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40), WaypointEvaluation(id=1, score_ratio=0.8, closest_for_scored_approach_ft=20, closest_for_mission_ft=20), WaypointEvaluation(id=2, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40)] logs = self.create_uas_logs(self.user, entries) self.assertSatisfiedWaypoints(expect, UasTelemetry.satisfied_waypoints( gpos, 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)] logs = self.create_uas_logs(self.user, entries) expect = [WaypointEvaluation(id=0, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40), WaypointEvaluation(id=1, score_ratio=0.8, closest_for_scored_approach_ft=20, closest_for_mission_ft=20), WaypointEvaluation(id=2, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40)] self.assertSatisfiedWaypoints(expect, UasTelemetry.satisfied_waypoints( gpos, 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 = [WaypointEvaluation(id=0, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40), WaypointEvaluation(id=1, score_ratio=0.8, closest_for_scored_approach_ft=20, closest_for_mission_ft=20), WaypointEvaluation(id=2, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40)] logs = self.create_uas_logs(self.user, entries) self.assertSatisfiedWaypoints(expect, UasTelemetry.satisfied_waypoints( gpos, waypoints, logs)) # Keep flying after hitting all waypoints. entries = [(38, -76, 140), (39, -77, 180), (40, -78, 40), (30.1, -78.1, 100)] logs = self.create_uas_logs(self.user, entries) expect = [WaypointEvaluation(id=0, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40), WaypointEvaluation(id=1, score_ratio=0.8, closest_for_scored_approach_ft=20, closest_for_mission_ft=20), WaypointEvaluation(id=2, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40)] self.assertSatisfiedWaypoints(expect, UasTelemetry.satisfied_waypoints( gpos, 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)] logs = self.create_uas_logs(self.user, entries) expect = [WaypointEvaluation(id=0, score_ratio=1, closest_for_scored_approach_ft=0, closest_for_mission_ft=0), WaypointEvaluation(id=1, score_ratio=1, closest_for_scored_approach_ft=0, closest_for_mission_ft=0), WaypointEvaluation(id=2, score_ratio=0, closest_for_mission_ft=60)] self.assertSatisfiedWaypoints(expect, UasTelemetry.satisfied_waypoints( gpos, waypoints, logs)) # Restart waypoint path in the middle. waypoints = self.waypoints_from_data([(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)] logs = self.create_uas_logs(self.user, entries) expect = [WaypointEvaluation(id=0, score_ratio=0.7, closest_for_scored_approach_ft=30, closest_for_mission_ft=30), WaypointEvaluation(id=1, score_ratio=0.5, closest_for_scored_approach_ft=50, closest_for_mission_ft=20), WaypointEvaluation(id=2, score_ratio=0.9, closest_for_scored_approach_ft=10, closest_for_mission_ft=10)] self.assertSatisfiedWaypoints(expect, UasTelemetry.satisfied_waypoints( gpos, waypoints, logs))
def test_evaluate_collision_with_uas(self): """Tests the collision with UAS method.""" # Get test data user = User.objects.create_user('testuser', '*****@*****.**', 'testpass') user.save() testdata = TESTDATA_MOVOBST_EVALCOLLISION (obst_rad, obst_speed, obst_pos, log_details) = testdata # Create the obstacle obst = MovingObstacle() obst.speed_avg = obst_speed obst.sphere_radius = obst_rad obst.save() for pos_id in xrange(len(obst_pos)): (lat, lon, alt) = obst_pos[pos_id] gpos = GpsPosition() gpos.latitude = lat gpos.longitude = lon gpos.save() apos = AerialPosition() apos.gps_position = gpos apos.altitude_msl = alt apos.save() wpt = Waypoint() wpt.order = pos_id wpt.position = apos wpt.save() obst.waypoints.add(wpt) obst.save() # Create sets of logs epoch = timezone.now().replace(year=1970, month=1, day=1, hour=0, minute=0, second=0, microsecond=0) inside_logs = [] outside_logs = [] for (time_sec, inside_pos, outside_pos) in log_details: log_time = epoch + datetime.timedelta(seconds=time_sec) logs_pos = [(inside_pos, inside_logs), (outside_pos, outside_logs)] for (positions, log_list) in logs_pos: for (lat, lon, alt) in positions: gpos = GpsPosition() gpos.latitude = lat gpos.longitude = lon gpos.save() apos = AerialPosition() apos.gps_position = gpos apos.altitude_msl = alt apos.save() log = UasTelemetry() log.user = user log.uas_position = apos log.uas_heading = 0 log.save() log.timestamp = log_time log.save() log_list.append(log) # Assert the obstacle correctly computes collisions log_collisions = [(True, inside_logs), (False, outside_logs)] for (inside, logs) in log_collisions: self.assertEqual(obst.evaluate_collision_with_uas(logs), inside) for log in logs: self.assertEqual(obst.evaluate_collision_with_uas([log]), inside)
from auvsi_suas.models.gps_position import GpsPosition from auvsi_suas.models.mission_config import MissionConfig # Print all mission objects. print MissionConfig.objects.all() # Create and save a GPS position. gpos = GpsPosition(latitudel=38.145335, longitude=-76.427512) gpos.save()
def test_similar_orientation(self): """Test similar orientations are computed correctly.""" l = GpsPosition(latitude=38, longitude=-76) l.save() t1 = Odlc(user=self.user, odlc_type=OdlcType.standard, location=l, orientation=Orientation.s, shape=Shape.square, background_color=Color.white, alphanumeric='ABC', alphanumeric_color=Color.black, description='Test odlc', description_approved=True, autonomous=True) t1.save() t2 = Odlc(user=self.user, odlc_type=OdlcType.standard, location=l, orientation=Orientation.s, shape=Shape.square, background_color=Color.white, alphanumeric='ABC', alphanumeric_color=Color.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 = Orientation.s t2.orientation = Orientation.s self.assertTrue(t1.similar_orientation(t2)) t2.orientation = Orientation.n self.assertFalse(t1.similar_orientation(t2)) t2.orientation = Orientation.e self.assertFalse(t1.similar_orientation(t2)) # Accepts rotation. for alpha in ['I', 'H', '8']: t1.alphanumeric = alpha t1.orientation = Orientation.s t2.orientation = Orientation.s self.assertTrue(t1.similar_orientation(t2)) t2.orientation = Orientation.n self.assertTrue(t1.similar_orientation(t2)) t2.orientation = Orientation.e self.assertFalse(t1.similar_orientation(t2)) # Accepts any. for alpha in ['O', 'o', '0']: t1.alphanumeric = alpha t1.orientation = Orientation.s t2.orientation = Orientation.s self.assertTrue(t1.similar_orientation(t2)) t2.orientation = Orientation.n self.assertTrue(t1.similar_orientation(t2)) t2.orientation = Orientation.e self.assertTrue(t1.similar_orientation(t2))
def post(self, request): try: data = json.loads(request.body) except ValueError: return HttpResponseBadRequest('Request body is not valid JSON.') # Must be a json dictionary. if not isinstance(data, dict): return HttpResponseBadRequest('Request body not a JSON dict.') # Odlc type is required. if 'type' not in data: return HttpResponseBadRequest('Odlc type required.') # Team id can only be specified if superuser. user = request.user if 'team_id' in data: if request.user.is_superuser: user = User.objects.get(username=data['team_id']) else: return HttpResponseForbidden( 'Non-admin users cannot send team_id') latitude = data.get('latitude') longitude = data.get('longitude') # Require zero or both of latitude and longitude. if (latitude is not None and longitude is None) or \ (latitude is None and longitude is not None): return HttpResponseBadRequest( 'Either none or both of latitude and longitude required.') # Cannot submit JSON with actionable_override if not superuser. if 'actionable_override' in data and not request.user.is_superuser: return HttpResponseForbidden( 'Non-admin users cannot submit actionable override.') try: data = normalize_data(data) except ValueError as e: return HttpResponseBadRequest(str(e)) l = None if latitude is not None and longitude is not None: l = GpsPosition(latitude=data['latitude'], longitude=data['longitude']) l.save() # Use the dictionary get() method to default non-existent values to None. t = Odlc(user=user, odlc_type=data['type'], location=l, orientation=data.get('orientation'), shape=data.get('shape'), background_color=data.get('background_color'), alphanumeric=data.get('alphanumeric', ''), alphanumeric_color=data.get('alphanumeric_color'), description=data.get('description', ''), autonomous=data.get('autonomous', False), actionable_override=data.get('actionable_override', False)) t.save() return JsonResponse(t.json(is_superuser=request.user.is_superuser), status=201)
def test_satisfied_waypoints(self): """Tests the evaluation of waypoints method.""" # Create mission config gpos = GpsPosition() gpos.latitude = 10 gpos.longitude = 10 gpos.save() waypoints = self.waypoints_from_data([ (38, -76, 100), (39, -77, 200), (40, -78, 0), ]) # Only first is valid. logs = self.create_uas_logs([ (0, 38, -76, 140, 0), (1, 40, -78, 600, 0), (2, 37, -75, 40, 0), ]) expect = [ WaypointEvaluation(id=0, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40), WaypointEvaluation(id=1, score_ratio=0, closest_for_mission_ft=170), WaypointEvaluation(id=2, score_ratio=0, closest_for_mission_ft=600) ] self.assertSatisfiedWaypoints( expect, UasTelemetry.satisfied_waypoints(gpos, waypoints, logs)) # First and last are valid. logs = self.create_uas_logs([ (0, 38, -76, 140, 0), (1, 40, -78, 600, 0), (2, 40, -78, 40, 0), ]) expect = [ WaypointEvaluation(id=0, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40), WaypointEvaluation(id=1, score_ratio=0, closest_for_mission_ft=170), WaypointEvaluation(id=2, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40) ] self.assertSatisfiedWaypoints( expect, UasTelemetry.satisfied_waypoints(gpos, waypoints, logs)) # Hit all. logs = self.create_uas_logs([ (0, 38, -76, 140, 0), (1, 39, -77, 180, 0), (2, 40, -78, 40, 0), ]) expect = [ WaypointEvaluation(id=0, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40), WaypointEvaluation(id=1, score_ratio=0.8, closest_for_scored_approach_ft=20, closest_for_mission_ft=20), WaypointEvaluation(id=2, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40) ] self.assertSatisfiedWaypoints( expect, UasTelemetry.satisfied_waypoints(gpos, waypoints, logs)) # Only hit the first waypoint on run one, hit all on run two. logs = self.create_uas_logs([ (0, 38, -76, 140, 0), (1, 40, -78, 600, 0), (2, 37, -75, 40, 0), # Run two: (3, 38, -76, 140, 0), (4, 39, -77, 180, 0), (5, 40, -78, 40, 0), ]) expect = [ WaypointEvaluation(id=0, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40), WaypointEvaluation(id=1, score_ratio=0.8, closest_for_scored_approach_ft=20, closest_for_mission_ft=20), WaypointEvaluation(id=2, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40) ] self.assertSatisfiedWaypoints( expect, UasTelemetry.satisfied_waypoints(gpos, waypoints, logs)) # Hit all on run one, only hit the first waypoint on run two. logs = self.create_uas_logs([ (0, 38, -76, 140, 0), (1, 39, -77, 180, 0), (2, 40, -78, 40, 0), # Run two: (3, 38, -76, 140, 0), (4, 40, -78, 600, 0), (5, 37, -75, 40, 0) ]) expect = [ WaypointEvaluation(id=0, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40), WaypointEvaluation(id=1, score_ratio=0.8, closest_for_scored_approach_ft=20, closest_for_mission_ft=20), WaypointEvaluation(id=2, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40) ] self.assertSatisfiedWaypoints( expect, UasTelemetry.satisfied_waypoints(gpos, waypoints, logs)) # Keep flying after hitting all waypoints. logs = self.create_uas_logs([ (0, 38, -76, 140, 0), (1, 39, -77, 180, 0), (2, 40, -78, 40, 0), (3, 30.1, -78.1, 100, 0), ]) expect = [ WaypointEvaluation(id=0, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40), WaypointEvaluation(id=1, score_ratio=0.8, closest_for_scored_approach_ft=20, closest_for_mission_ft=20), WaypointEvaluation(id=2, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=40) ] self.assertSatisfiedWaypoints( expect, UasTelemetry.satisfied_waypoints(gpos, waypoints, logs)) # Hit all in first run, but second is higher scoring. logs = self.create_uas_logs([ (0, 38, -76, 140, 0), (1, 39, -77, 180, 0), (2, 40, -78, 60, 0), # Run two: (3, 38, -76, 100, 0), (4, 39, -77, 200, 0), (5, 40, -78, 110, 0) ]) expect = [ WaypointEvaluation(id=0, score_ratio=1, closest_for_scored_approach_ft=0, closest_for_mission_ft=0), WaypointEvaluation(id=1, score_ratio=1, closest_for_scored_approach_ft=0, closest_for_mission_ft=0), WaypointEvaluation(id=2, score_ratio=0, closest_for_mission_ft=60) ] self.assertSatisfiedWaypoints( expect, UasTelemetry.satisfied_waypoints(gpos, waypoints, logs)) # Restart waypoint path in the middle, use path in between points. waypoints = self.waypoints_from_data([ (38, -76, 100), (39, -77, 200), (40, -78, 0), ]) logs = self.create_uas_logs([ (0, 38, -76, 140, 0), # Use (1, 39, -77, 180, 0), # Use # Restart: (2, 38, -76, 70, 0), (3, 39, -77, 150, 0), (4, 40, -78, 10, 0), # Use ]) expect = [ WaypointEvaluation(id=0, score_ratio=0.6, closest_for_scored_approach_ft=40, closest_for_mission_ft=30), WaypointEvaluation(id=1, score_ratio=0.8, closest_for_scored_approach_ft=20, closest_for_mission_ft=20), WaypointEvaluation(id=2, score_ratio=0.9, closest_for_scored_approach_ft=10, closest_for_mission_ft=10) ] self.assertSatisfiedWaypoints( expect, UasTelemetry.satisfied_waypoints(gpos, waypoints, logs)) # Sanity check waypoint scoring with interpolation. waypoints = self.waypoints_from_data([ (38, -76, 70), (38, -76, 110), ]) logs = self.create_uas_logs([ (0, 38, -76, 0, 0), (1, 38, -76, 200, 0), ]) expect = [ WaypointEvaluation(id=0, score_ratio=0.9, closest_for_scored_approach_ft=10, closest_for_mission_ft=10), WaypointEvaluation(id=1, score_ratio=0.9, closest_for_scored_approach_ft=10, closest_for_mission_ft=10) ] self.assertSatisfiedWaypoints( expect, UasTelemetry.satisfied_waypoints(gpos, waypoints, logs))
def test_duplicate_equal(self): """Tests the duplicate function for equal positiions.""" gps1 = GpsPosition(latitude=1, longitude=1) gps2 = GpsPosition(latitude=1, longitude=1) self.assertTrue(gps1.duplicate(gps2))
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 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
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_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( user=self.user, odlc_type=OdlcType.standard, location=l, orientation=Orientation.s, shape=Shape.square, background_color=Color.white, alphanumeric='ABC', alphanumeric_color=Color.black, description='Test odlc', description_approved=True, autonomous=True) t1.save() t2 = Odlc( user=self.user, odlc_type=OdlcType.standard, location=l, orientation=Orientation.s, shape=Shape.square, background_color=Color.white, alphanumeric='ABC', alphanumeric_color=Color.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 = Color.blue t1.save() self.assertAlmostEqual(3.0 / 5.0, t1.similar_classifications_ratio(t2)) t1.shape = Shape.circle t1.background_color = Color.orange t1.save() self.assertAlmostEqual(1.0 / 5.0, t1.similar_classifications_ratio(t2)) # Test different types. t1.odlc_type = OdlcType.off_axis t1.save() self.assertAlmostEqual(0, t1.similar_classifications_ratio(t2)) # Test off_axis is same as standard. t2.odlc_type = OdlcType.off_axis t2.alphanumeric = 'DEF' t2.save() self.assertAlmostEqual(2.0 / 5.0, t1.similar_classifications_ratio(t2)) # Test emergent type based on description approval. t1.odlc_type = OdlcType.emergent t1.save() t2.odlc_type = OdlcType.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_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))
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 post(self, request): """Posts the UAS position with a POST request. User must send a POST request with the following paramters: latitude: A latitude in decimal degrees. longitude: A logitude in decimal degrees. altitude_msl: An MSL altitude in decimal feet. uas_heading: The UAS (true north) heading in decimal degrees. """ try: # Get the parameters latitude = float(request.POST['latitude']) longitude = float(request.POST['longitude']) altitude_msl = float(request.POST['altitude_msl']) uas_heading = float(request.POST['uas_heading']) except KeyError: # Failed to get POST parameters logger.warning( 'User did not specify all params for uas telemetry request.') logger.debug(request) return HttpResponseBadRequest( 'Posting UAS position must contain POST parameters "latitude", ' '"longitude", "altitude_msl", and "uas_heading".') except ValueError: # Failed to convert parameters logger.warning( 'User specified a param which could not converted to an ' + 'appropriate type.') logger.debug(request) return HttpResponseBadRequest( 'Failed to convert provided POST parameters to correct form.') else: # Check the values make sense if latitude < -90 or latitude > 90: logger.warning('User specified latitude out of valid range.') logger.debug(request) return HttpResponseBadRequest( 'Must provide latitude between -90 and 90 degrees.') if longitude < -180 or longitude > 180: logger.warning('User specified longitude out of valid range.') logger.debug(request) return HttpResponseBadRequest( 'Must provide longitude between -180 and 180 degrees.') if uas_heading < 0 or uas_heading > 360: logger.warning('User specified altitude out of valid range.') logger.debug(request) return HttpResponseBadRequest( 'Must provide heading between 0 and 360 degrees.') # Store telemetry logger.info('User uploaded telemetry: %s' % request.user.username) gpos = GpsPosition(latitude=latitude, longitude=longitude) gpos.save() apos = AerialPosition(gps_position=gpos, altitude_msl=altitude_msl) apos.save() telemetry = UasTelemetry( user=request.user, uas_position=apos, uas_heading=uas_heading) telemetry.save() return HttpResponse('UAS Telemetry Successfully Posted.')
def setUp(self): """Setup the test case.""" super(TestTargetEvaluator, 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() event = MissionClockEvent(user=self.user, team_on_clock=True, team_on_timeout=False) event.save() event = TakeoffOrLandingEvent(user=self.user, uas_in_air=True) event.save() # A target worth full points. self.submit1 = Target(user=self.user, target_type=TargetType.standard, location=l1, orientation=Orientation.s, shape=Shape.square, background_color=Color.white, alphanumeric='ABC', alphanumeric_color=Color.black, description='Submit test target 1', autonomous=True, thumbnail_approved=True) self.submit1.save() self.real1 = Target(user=self.user, target_type=TargetType.standard, location=l1, orientation=Orientation.s, shape=Shape.square, background_color=Color.white, alphanumeric='ABC', alphanumeric_color=Color.black, description='Real target 1') self.real1.save() event = MissionClockEvent(user=self.user, team_on_clock=False, team_on_timeout=False) event.save() # A target worth less than full points. self.submit2 = Target(user=self.user, target_type=TargetType.standard, location=l1, orientation=Orientation.n, shape=Shape.circle, background_color=Color.white, # alphanumeric set below alphanumeric_color=Color.black, description='Submit test target 2', autonomous=False, thumbnail_approved=True) self.submit2.save() self.real2 = Target(user=self.user, target_type=TargetType.standard, location=l2, orientation=Orientation.s, shape=Shape.triangle, background_color=Color.white, alphanumeric='ABC', alphanumeric_color=Color.black, description='Real test target 2') self.real2.save() # A target worth no points, so unmatched. self.submit3 = Target(user=self.user, target_type=TargetType.standard, location=l1, orientation=Orientation.nw, shape=Shape.pentagon, background_color=Color.gray, alphanumeric='XYZ', alphanumeric_color=Color.orange, description='Incorrect description', autonomous=False, thumbnail_approved=True) self.submit3.save() self.real3 = Target(user=self.user, target_type=TargetType.standard, orientation=Orientation.e, shape=Shape.semicircle, background_color=Color.yellow, alphanumeric='LMN', # alphanumeric_color set below location=l3, description='Test target 3') self.real3.save() # Targets without approved image may still match. self.submit4 = Target(user=self.user, target_type=TargetType.emergent, location=l1, description='Test target 4', autonomous=False, thumbnail_approved=False) self.submit4.save() self.real4 = Target(user=self.user, target_type=TargetType.emergent, location=l1, description='Test target 4') self.real4.save() # A target without location worth fewer points. self.submit5 = Target(user=self.user, target_type=TargetType.standard, orientation=Orientation.n, shape=Shape.trapezoid, background_color=Color.purple, alphanumeric='PQR', alphanumeric_color=Color.blue, description='Test target 5', autonomous=False, thumbnail_approved=True) self.submit5.save() self.real5 = Target(user=self.user, target_type=TargetType.standard, location=l1, orientation=Orientation.n, shape=Shape.trapezoid, background_color=Color.purple, alphanumeric='PQR', alphanumeric_color=Color.blue, description='Test target 5') self.real5.save() event = TakeoffOrLandingEvent(user=self.user, uas_in_air=False) event.save() # submit2 updated after landing. self.submit2.alphanumeric = 'ABC' self.submit2.save() self.submit3.alphanumeric_color = Color.yellow self.submit3.save() self.submitted_targets = [self.submit5, self.submit4, self.submit3, self.submit2, self.submit1] self.real_targets = [self.real1, self.real2, self.real3, self.real4, self.real5] self.real_matched_targets = [self.real1, self.real2, self.real4, self.real5]
def test_out_of_bounds(self): """Tests the UAS out of bounds method.""" (zone_details, uas_details) = TESTDATA_FLYZONE_EVALBOUNDS # Create FlyZone objects zones = [] for (alt_min, alt_max, wpts) in zone_details: zone = FlyZone() zone.altitude_msl_min = alt_min zone.altitude_msl_max = alt_max zone.save() for wpt_id in xrange(len(wpts)): (lat, lon) = wpts[wpt_id] gpos = GpsPosition() gpos.latitude = lat gpos.longitude = lon gpos.save() apos = AerialPosition() apos.gps_position = gpos apos.altitude_msl = 0 apos.save() wpt = Waypoint() wpt.order = wpt_id wpt.position = apos wpt.save() zone.boundary_pts.add(wpt) zone.save() zones.append(zone) # For each user, validate time out of bounds user_id = 0 epoch = timezone.now().replace(year=1970, month=1, day=1, hour=0, minute=0, second=0, microsecond=0) for exp_violations, exp_out_of_bounds_time, log_details in uas_details: # Create the logs user = User.objects.create_user('testuser%d' % user_id, '*****@*****.**', 'testpass') user_id += 1 uas_logs = [] for (lat, lon, alt, timestamp) in log_details: gpos = GpsPosition() gpos.latitude = lat gpos.longitude = lon gpos.save() apos = AerialPosition() apos.gps_position = gpos apos.altitude_msl = alt apos.save() log = UasTelemetry() log.user = user log.uas_position = apos log.uas_heading = 0 log.save() log.timestamp = epoch + datetime.timedelta(seconds=timestamp) log.save() uas_logs.append(log) # Assert out of bounds time matches expected num_violations, out_of_bounds_time = \ FlyZone.out_of_bounds(zones, uas_logs) self.assertEqual(num_violations, exp_violations) self.assertAlmostEqual(out_of_bounds_time.total_seconds(), exp_out_of_bounds_time)
def put(self, request, pk): try: odlc = find_odlc(request, int(pk)) except Odlc.DoesNotExist: return HttpResponseNotFound('Odlc %s not found' % pk) except ValueError as e: return HttpResponseForbidden(str(e)) try: data = json.loads(request.body) except ValueError: return HttpResponseBadRequest('Request body is not valid JSON.') # Must be a json dictionary. if not isinstance(data, dict): return HttpResponseBadRequest('Request body not a JSON dict.') # Cannot submit JSON with actionable_override if not superuser. if 'actionable_override' in data and not request.user.is_superuser: return HttpResponseForbidden( 'Non-admin users cannot submit actionable override.') try: data = normalize_data(data) except ValueError as e: return HttpResponseBadRequest(str(e)) # We update any of the included values, except id and user if 'type' in data: odlc.odlc_type = data['type'] if 'orientation' in data: odlc.orientation = data['orientation'] if 'shape' in data: odlc.shape = data['shape'] if 'background_color' in data: odlc.background_color = data['background_color'] if 'alphanumeric' in data: odlc.alphanumeric = data['alphanumeric'] if 'alphanumeric_color' in data: odlc.alphanumeric_color = data['alphanumeric_color'] if 'description' in data: odlc.description = data['description'] if 'autonomous' in data: odlc.autonomous = data['autonomous'] if 'actionable_override' in data: odlc.actionable_override = data['actionable_override'] # Location is special because it is in a GpsPosition model # If lat/lon exist and are None, the user wants to clear them. # If they exist and are not None, the user wants to update/add them. # If they don't exist, the user wants to leave them alone. clear_lat = False clear_lon = False update_lat = False update_lon = False if 'latitude' in data: if data['latitude'] is None: clear_lat = True else: update_lat = True if 'longitude' in data: if data['longitude'] is None: clear_lon = True else: update_lon = True if (clear_lat and not clear_lon) or (not clear_lat and clear_lon): # Location must be cleared entirely, we can't clear just lat or # just lon. return HttpResponseBadRequest( 'Only none or both of latitude and longitude can be cleared.') if clear_lat and clear_lon: odlc.location = None elif update_lat or update_lon: if odlc.location is not None: # We can directly update individual components if update_lat: odlc.location.latitude = data['latitude'] if update_lon: odlc.location.longitude = data['longitude'] odlc.location.save() else: # We need a new GpsPosition, this requires both lat and lon if not update_lat or not update_lon: return HttpResponseBadRequest( 'Either none or both of latitude and longitude required.' ) l = GpsPosition(latitude=data['latitude'], longitude=data['longitude']) l.save() odlc.location = l odlc.description_approved = None if not request.user.is_superuser: odlc.update_last_modified() odlc.save() return JsonResponse(odlc.json(is_superuser=request.user.is_superuser))
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() event = TakeoffOrLandingEvent(user=self.user, uas_in_air=True) event.save() # A odlc worth full points. self.submit1 = Odlc(user=self.user, odlc_type=OdlcType.standard, location=l1, orientation=Orientation.s, shape=Shape.square, background_color=Color.white, alphanumeric='ABC', alphanumeric_color=Color.black, description='Submit test odlc 1', description_approved=True, autonomous=True, thumbnail_approved=True) self.submit1.save() self.real1 = Odlc(user=self.user, odlc_type=OdlcType.standard, location=l1, orientation=Orientation.s, shape=Shape.square, background_color=Color.white, alphanumeric='ABC', alphanumeric_color=Color.black, description='Real odlc 1') self.real1.save() # A odlc worth less than full points. self.submit2 = Odlc( user=self.user, odlc_type=OdlcType.standard, location=l1, orientation=Orientation.n, shape=Shape.circle, background_color=Color.white, # alphanumeric set below alphanumeric_color=Color.black, description='Submit test odlc 2', autonomous=False, thumbnail_approved=True) self.submit2.save() self.real2 = Odlc(user=self.user, odlc_type=OdlcType.standard, location=l2, orientation=Orientation.s, shape=Shape.triangle, background_color=Color.white, alphanumeric='ABC', alphanumeric_color=Color.black, description='Real test odlc 2') self.real2.save() # A odlc worth no points, so unmatched. self.submit3 = Odlc(user=self.user, odlc_type=OdlcType.standard, location=l4, orientation=Orientation.nw, shape=Shape.pentagon, background_color=Color.gray, alphanumeric='XYZ', alphanumeric_color=Color.orange, description='Incorrect description', autonomous=False, thumbnail_approved=True) self.submit3.save() self.real3 = Odlc( user=self.user, odlc_type=OdlcType.standard, orientation=Orientation.e, shape=Shape.semicircle, background_color=Color.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(user=self.user, odlc_type=OdlcType.emergent, location=l1, description='Test odlc 4', autonomous=False, thumbnail_approved=False) self.submit4.save() self.real4 = Odlc(user=self.user, odlc_type=OdlcType.emergent, location=l1, description='Test odlc 4') self.real4.save() # A odlc without location worth fewer points. self.submit5 = Odlc(user=self.user, odlc_type=OdlcType.standard, orientation=Orientation.n, shape=Shape.trapezoid, background_color=Color.purple, alphanumeric='PQR', alphanumeric_color=Color.blue, description='Test odlc 5', autonomous=False, thumbnail_approved=True) self.submit5.save() self.real5 = Odlc(user=self.user, odlc_type=OdlcType.standard, location=l1, orientation=Orientation.n, shape=Shape.trapezoid, background_color=Color.purple, alphanumeric='PQR', alphanumeric_color=Color.blue, description='Test odlc 5') self.real5.save() # Emergent odlc with correct description. self.submit6 = Odlc(user=self.user, odlc_type=OdlcType.emergent, location=l1, description='Submit test odlc 6', description_approved=True, autonomous=True, thumbnail_approved=True) self.submit6.save() self.real6 = Odlc(user=self.user, odlc_type=OdlcType.emergent, location=l1, description='Real odlc 1', description_approved=True) self.real6.save() event = TakeoffOrLandingEvent(user=self.user, 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 = Color.yellow self.submit3.update_last_modified() self.submit3.save() # Unused but not unmatched odlc. self.submit7 = Odlc(user=self.user, odlc_type=OdlcType.standard, location=l4, alphanumeric_color=Color.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 ]