Exemple #1
0
 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()
Exemple #2
0
    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.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()

        # 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()
Exemple #3
0
    def get(self, request):
        kml = Kml(name='AUVSI SUAS LIVE Flight Data')
        kml_mission = kml.newfolder(name='Missions')

        (mission, err) = active_mission()
        if err:
            return err
        MissionConfig.kml_all(kml_mission, [mission])

        kml_flyzone = kml.newfolder(name='Fly Zones')
        FlyZone.kml_all(kml_flyzone)

        parameters = '?sessionid={}'.format(request.COOKIES['sessionid'])
        uri = request.build_absolute_uri(
            '/auvsi_admin/update.kml') + parameters

        netlink = kml.newnetworklink(name="Live Data")
        netlink.link.href = uri
        netlink.link.refreshmode = RefreshMode.oninterval
        netlink.link.refreshinterval = 0.5

        response = HttpResponse(kml.kml())
        response['Content-Type'] = 'application/vnd.google-earth.kml+xml'
        response['Content-Disposition'] = 'attachment; filename=live.kml'
        response['Content-Length'] = str(len(response.content))
        return response
Exemple #4
0
    def get(self, request):
        kml = Kml(name='AUVSI SUAS LIVE Flight Data')
        kml_mission = kml.newfolder(name='Missions')

        (mission, err) = active_mission()
        if err:
            return err
        MissionConfig.kml_all(kml_mission, [mission])

        kml_flyzone = kml.newfolder(name='Fly Zones')
        FlyZone.kml_all(kml_flyzone)

        parameters = '?sessionid={}'.format(request.COOKIES['sessionid'])
        uri = request.build_absolute_uri(
            '/auvsi_admin/update.kml') + parameters

        netlink = kml.newnetworklink(name="Live Data")
        netlink.link.href = uri
        netlink.link.refreshmode = RefreshMode.oninterval
        netlink.link.refreshinterval = 0.5

        response = HttpResponse(kml.kml())
        response['Content-Type'] = 'application/vnd.google-earth.kml+xml'
        response['Content-Disposition'] = 'attachment; filename=live.kml'
        response['Content-Length'] = str(len(response.content))
        return response
Exemple #5
0
    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 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 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')
Exemple #8
0
    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)
Exemple #9
0
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.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):
        super(TestTakeoffOrLandingEventModel, self).setUp()

        # 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()

        # 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.ten_minutes = datetime.timedelta(minutes=10)
 def test_toKML(self):
     """Test Generation of kml for all mission data"""
     kml = Kml()
     MissionConfig.kml_all(kml=kml)
     data = kml.kml()
     names = [
         'Off Axis',
         'Home Position',
         'Air Drop',
         'Emergent LKP',
         'Search Area',
         'Waypoints',
     ]
     for name in names:
         tag = '<name>{}</name>'.format(name)
         err = 'tag "{}" not found in {}'.format(tag, data)
         self.assertIn(tag, data, err)
 def test_toKML(self):
     """Test Generation of kml for all mission data"""
     kml = Kml()
     MissionConfig.kml_all(kml=kml)
     data = kml.kml()
     names = [
         'Off Axis',
         'Home Position',
         'Air Drop',
         'Emergent LKP',
         'Search Area',
         'Waypoints',
     ]
     for name in names:
         tag = '<name>{}</name>'.format(name)
         err = 'tag "{}" not found in {}'.format(tag, data)
         self.assertIn(tag, data, err)
Exemple #13
0
    def setUp(self):
        """Setup a single mission to test live kml with."""
        super(TestGenerateLiveKML, self).setUp()

        pos = GpsPosition()
        pos.latitude = 10
        pos.longitude = 10
        pos.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.air_drop_pos = pos
        config.ugv_drive_pos = pos
        config.save()
        self.config = config
    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_drop_accuracy=interop_admin_api_pb2.MissionJudgeFeedback.
            WITHIN_05_FT,
            ugv_drove_to_location=False,
            operational_excellence_percent=9)
        self.feedback.save()
Exemple #15
0
    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')
Exemple #16
0
    def get(self, request):
        kml = Kml(name='AUVSI SUAS Flight Data')
        kml_teams = kml.newfolder(name='Teams')
        kml_mission = kml.newfolder(name='Missions')
        users = User.objects.all()
        for user in users:
            # Ignore admins
            if user.is_superuser:
                continue
            UasTelemetry.kml(user=user,
                             logs=UasTelemetry.by_user(user),
                             kml=kml_teams,
                             kml_doc=kml.document)
        MissionConfig.kml_all(kml_mission, kml.document)

        response = HttpResponse(kml.kml())
        response['Content-Type'] = 'application/vnd.google-earth.kml+xml'
        response['Content-Disposition'] = 'attachment; filename=mission.kml'
        response['Content-Length'] = str(len(response.content))
        return response
Exemple #17
0
    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
Exemple #18
0
    def get(self, request):
        kml = Kml(name='AUVSI SUAS Flight Data')
        kml_teams = kml.newfolder(name='Teams')
        kml_mission = kml.newfolder(name='Missions')
        users = User.objects.all()
        for user in users:
            # Ignore admins
            if user.is_superuser:
                continue
            UasTelemetry.kml(user=user,
                             logs=UasTelemetry.by_user(user),
                             kml=kml_teams,
                             kml_doc=kml.document)
        MissionConfig.kml_all(kml_mission)
        kml_flyzone = kml.newfolder(name='Fly Zones')
        FlyZone.kml_all(kml_flyzone)

        response = HttpResponse(kml.kml())
        response['Content-Type'] = 'application/vnd.google-earth.kml+xml'
        response['Content-Disposition'] = 'attachment; filename=mission.kml'
        response['Content-Length'] = str(len(response.content))
        return response
Exemple #19
0
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])
Exemple #20
0
    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 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__())
Exemple #22
0
    def create_config(self):
        """Creates a dummy config for testing."""
        pos = GpsPosition()
        pos.latitude = 10
        pos.longitude = 10
        pos.save()

        config = MissionConfig()
        config.is_active = False
        config.home_pos = pos
        config.emergent_last_known_pos = pos
        config.off_axis_odlc_pos = pos
        config.air_drop_pos = pos
        return config
Exemple #23
0
    def create_config(self):
        """Creates a dummy config for testing."""
        pos = GpsPosition()
        pos.latitude = 10
        pos.longitude = 10
        pos.save()

        config = MissionConfig()
        config.is_active = False
        config.home_pos = pos
        config.emergent_last_known_pos = pos
        config.off_axis_target_pos = pos
        config.air_drop_pos = pos
        return config
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')
Exemple #25
0
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)
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 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 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_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 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 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))
Exemple #32
0
    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_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)

        # 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)
Exemple #33
0
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
Exemple #34
0
    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()
Exemple #35
0
    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')
Exemple #36
0
    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 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)
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))