Пример #1
0
    def put(self, request, pk):
        """PUT allows updating status."""
        try:
            user = User.objects.get(pk=int(pk))
        except User.DoesNotExist:
            return HttpResponseBadRequest('Unknown team %s' % pk)
        try:
            data = json.loads(request.body)
        except ValueError:
            return HttpResponseBadRequest('Invalid JSON: %s' % request.body)

        # Potential events to update.
        takeoff_event = None
        clock_event = None
        # Update whether UAS is in air.
        if 'in_air' in data:
            in_air = data['in_air']
            if not isinstance(in_air, bool):
                return HttpResponseBadRequest('in_air must be boolean')

            currently_in_air = TakeoffOrLandingEvent.user_in_air(user)
            # New event only necessary if changing status
            if currently_in_air != in_air:
                takeoff_event = TakeoffOrLandingEvent(user=user,
                                                      uas_in_air=in_air)
        # Request was valid. Save updates.
        if takeoff_event:
            takeoff_event.save()
        if clock_event:
            clock_event.save()

        return HttpResponse(json.dumps(user_json(user)),
                            content_type="application/json")
Пример #2
0
    def put(self, request, pk):
        """PUT allows updating status."""
        try:
            user = User.objects.get(pk=int(pk))
        except User.DoesNotExist:
            return HttpResponseBadRequest('Unknown team %s' % pk)
        try:
            data = json.loads(request.body)
        except ValueError:
            return HttpResponseBadRequest('Invalid JSON: %s' % request.body)

        # Potential events to update.
        takeoff_event = None
        clock_event = None
        # Update whether UAS is in air.
        if 'in_air' in data:
            in_air = data['in_air']
            if not isinstance(in_air, bool):
                return HttpResponseBadRequest('in_air must be boolean')

            currently_in_air = TakeoffOrLandingEvent.user_in_air(user)
            # New event only necessary if changing status
            if currently_in_air != in_air:
                takeoff_event = TakeoffOrLandingEvent(
                    user=user, uas_in_air=in_air)
        # Update whether UAS in on clock or timeout.
        if 'on_clock' in data or 'on_timeout' in data:
            currently_on_clock = MissionClockEvent.user_on_clock(user)

            currently_on_timeout = MissionClockEvent.user_on_timeout(user)
            on_clock = data.get('on_clock', currently_on_clock)
            on_timeout = data.get('on_timeout', currently_on_timeout)
            if (not isinstance(on_clock, bool) or
                    not isinstance(on_timeout, bool)):
                return HttpResponseBadRequest(
                    'on_clock and on_timeout must be boolean.')
            if on_clock and on_timeout:
                return HttpResponseBadRequest(
                    'Cannot be on mission clock and on timeout.')
            # New event only necessary if changing status
            if (on_clock != currently_on_clock or
                    on_timeout != currently_on_timeout):
                clock_event = MissionClockEvent(
                    user=user,
                    team_on_clock=on_clock,
                    team_on_timeout=on_timeout)
        # Request was valid. Save updates.
        if takeoff_event:
            takeoff_event.save()
        if clock_event:
            clock_event.save()

        return HttpResponse(
            json.dumps(user_json(user)), content_type="application/json")
Пример #3
0
    def put(self, request, pk):
        """PUT allows updating status."""
        try:
            user = User.objects.get(pk=int(pk))
        except User.DoesNotExist:
            return HttpResponseBadRequest('Unknown team %s' % pk)
        try:
            data = json.loads(request.body)
        except ValueError:
            return HttpResponseBadRequest('Invalid JSON: %s' % request.body)

        # Potential events to update.
        takeoff_event = None
        clock_event = None
        # Update whether UAS is in air.
        if 'in_air' in data:
            in_air = data['in_air']
            if not isinstance(in_air, bool):
                return HttpResponseBadRequest('in_air must be boolean')

            currently_in_air = TakeoffOrLandingEvent.user_in_air(user)
            # New event only necessary if changing status
            if currently_in_air != in_air:
                takeoff_event = TakeoffOrLandingEvent(user=user,
                                                      uas_in_air=in_air)
        # Update whether UAS in on clock or timeout.
        if 'on_clock' in data or 'on_timeout' in data:
            currently_on_clock = MissionClockEvent.user_on_clock(user)

            currently_on_timeout = MissionClockEvent.user_on_timeout(user)
            on_clock = data.get('on_clock', currently_on_clock)
            on_timeout = data.get('on_timeout', currently_on_timeout)
            if (not isinstance(on_clock, bool) or
                    not isinstance(on_timeout, bool)):
                return HttpResponseBadRequest(
                    'on_clock and on_timeout must be boolean.')
            if on_clock and on_timeout:
                return HttpResponseBadRequest(
                    'Cannot be on mission clock and on timeout.')
            # New event only necessary if changing status
            if (on_clock != currently_on_clock or
                    on_timeout != currently_on_timeout):
                clock_event = MissionClockEvent(user=user,
                                                team_on_clock=on_clock,
                                                team_on_timeout=on_timeout)
        # Request was valid. Save updates.
        if takeoff_event:
            takeoff_event.save()
        if clock_event:
            clock_event.save()

        return HttpResponse(
            json.dumps(user_json(user)),
            content_type="application/json")
Пример #4
0
    def test_kml_simple(self):
        coordinates = [
            (-76.0, 38.0, 0.0),
            (-76.0, 38.0, 10.0),
            (-76.0, 38.0, 20.0),
            (-76.0, 38.0, 30.0),
            (-76.0, 38.0, 100.0),
            (-76.0, 38.0, 30.0),
            (-76.0, 38.0, 60.0),
        ]
        # Create Coordinates
        start = TakeoffOrLandingEvent(user=self.user, uas_in_air=True)
        start.save()
        for coord in coordinates:
            self.create_log_element(*coord)
        end = TakeoffOrLandingEvent(user=self.user, uas_in_air=False)
        end.save()

        kml = Kml()
        UasTelemetry.kml(user=self.user,
                         logs=UasTelemetry.by_user(self.user),
                         kml=kml,
                         kml_doc=kml)
        for coord in coordinates:
            tag = self.coord_format.format(coord[1], coord[0],
                                           units.feet_to_meters(coord[2]))
            self.assertTrue(tag in kml.kml())
Пример #5
0
    def get(self, request):
        kml = Kml(name='AUVSI SUAS Flight Data')
        kml_missions = kml.newfolder(name='Missions')
        users = User.objects.all()
        for mission in MissionConfig.objects.all():
            kml_mission = mission_kml(mission, kml_missions, kml.document)
            kml_flights = kml_mission.newfolder(name='Flights')
            for user in users:
                if user.is_superuser:
                    continue
                flights = TakeoffOrLandingEvent.flights(mission, user)
                if not flights:
                    continue
                uas_telemetry_kml(
                    user=user,
                    flights=flights,
                    logs=UasTelemetry.by_user(user),
                    kml=kml_flights,
                    kml_doc=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
Пример #6
0
    def __init__(self, submitted_objects, real_objects):
        """Creates an evaluation of submitted objects against real objects.

        Args:
            submitted_objects: List of submitted Odlc objects, all from
                               the same user.
            real_objects: List of real objects made by judges.

        Raises:
            AssertionError: not all submitted objects are from the same user.
        """
        self.submitted_objects = submitted_objects
        self.real_objects = real_objects

        if self.submitted_objects:
            self.user = self.submitted_objects[0].user
            for t in self.submitted_objects:
                if t.user != self.user:
                    raise AssertionError(
                        "All submitted objects must be from the same user")

            self.flights = TakeoffOrLandingEvent.flights(self.user)

        self.matches = self.match_odlcs(submitted_objects, real_objects)
        self.unmatched = self.find_unmatched(submitted_objects, real_objects,
                                             self.matches)
Пример #7
0
    def actionable_submission(self, flights=None):
        """Checks if Odlc meets Actionable Intelligence submission criteria.

        A object is "actionable" if one of the following conditions is met:
            (a) If it was submitted over interop and last updated during the
                aircraft's first flight.
            (b) If the object was submitted via USB, the object's
                actionable_override flag was set by an admin.

        Args:
            flights: Optional memoized flights for this object's user. If
                     omitted, the flights will be looked up.

        Returns:
            True if object may be considered an "actionable" submission.
        """
        if flights is None:
            flights = TakeoffOrLandingEvent.flights(self.user)

        actionable = False
        if len(flights) > 0:
            flight = flights[0]
            if flight.within(self.creation_time) and \
                flight.within(self.last_modified_time):
                actionable = True

        return self.actionable_override or actionable
Пример #8
0
    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_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))
Пример #10
0
 def create_event(self, time, uas_in_air):
     """Create a TakeoffOrLandingEvent for test user."""
     event = TakeoffOrLandingEvent(user=self.user1, uas_in_air=uas_in_air)
     event.save()
     event.timestamp = time
     event.save()
     return event
    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))
Пример #12
0
def generate_feedback(mission_config, user, team_eval):
    """Generates mission feedback for the given team and mission.

    Args:
        mission_config: The mission to evaluate the team against.
        user: The team user object for which to evaluate and provide feedback.
        team_eval: The team evaluation to fill.
    """
    feedback = team_eval.feedback

    # Find the user's flights.
    flight_periods = TakeoffOrLandingEvent.flights(mission_config, user)
    for period in flight_periods:
        if period.duration() is None:
            team_eval.warnings.append('Infinite flight period.')
    uas_period_logs = [
        UasTelemetry.dedupe(logs)
        for logs in UasTelemetry.by_time_period(user, flight_periods)
    ]
    uas_logs = list(itertools.chain.from_iterable(uas_period_logs))
    if not uas_logs:
        team_eval.warnings.append('No UAS telemetry logs.')

    # Determine interop telemetry rates.
    telem_max, telem_avg = UasTelemetry.rates(
        user, flight_periods, time_period_logs=uas_period_logs)
    if telem_max:
        feedback.uas_telemetry_time_max_sec = telem_max
    if telem_avg:
        feedback.uas_telemetry_time_avg_sec = telem_avg

    # Determine if the uas hit the waypoints.
    feedback.waypoints.extend(
        UasTelemetry.satisfied_waypoints(
            mission_config.home_pos,
            mission_config.mission_waypoints.order_by('order'), uas_logs))

    # Evaluate the object detections.
    user_odlcs = Odlc.objects.filter(user=user).filter(
        mission=mission_config.pk).all()
    evaluator = OdlcEvaluator(user_odlcs,
                              mission_config.odlcs.all(), flight_periods)
    feedback.odlc.CopyFrom(evaluator.evaluate())

    # Determine collisions with stationary.
    for obst in mission_config.stationary_obstacles.all():
        obst_eval = feedback.stationary_obstacles.add()
        obst_eval.id = obst.pk
        obst_eval.hit = obst.evaluate_collision_with_uas(uas_logs)

    # Add judge feedback.
    try:
        judge_feedback = MissionJudgeFeedback.objects.get(
            mission=mission_config.pk, user=user.pk)
        feedback.judge.CopyFrom(judge_feedback.proto())
    except MissionJudgeFeedback.DoesNotExist:
        team_eval.warnings.append('No MissionJudgeFeedback for team.')
Пример #13
0
    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))
Пример #14
0
def user_json(user):
    """Generate JSON-style dict for user."""
    telemetry = UasTelemetry.last_for_user(user)
    return {
        'name': user.username,
        'id': user.pk,
        'in_air': TakeoffOrLandingEvent.user_in_air(user),
        'telemetry': telemetry.json() if telemetry else None
    }
Пример #15
0
    def kml(cls, user, logs, kml, kml_doc):
        """
        Appends kml nodes describing the given user's flight as described
        by the log array given.

        Args:
            user: A Django User to get username from
            logs: A list of UasTelemetry elements
            kml: A simpleKML Container to which the flight data will be added
            kml_doc: The simpleKML Document to which schemas will be added
        Returns:
            None
        """
        kml_folder = kml.newfolder(name=user.username)

        flights = TakeoffOrLandingEvent.flights(user)
        if len(flights) == 0:
            return

        logs = UasTelemetry.dedupe(UasTelemetry.filter_bad(logs))
        for i, flight in enumerate(flights):
            name = '%s Flight %d' % (user.username, i + 1)
            flight_logs = filter(lambda x: flight.within(x.timestamp), logs)

            coords = []
            angles = []
            when = []
            for entry in logs:
                pos = entry.uas_position.gps_position
                # Spatial Coordinates
                coord = (pos.longitude, pos.latitude,
                         units.feet_to_meters(entry.uas_position.altitude_msl))
                coords.append(coord)

                # Time Elements
                time = entry.timestamp.strftime(KML_DATETIME_FORMAT)
                when.append(time)

                # Degrees heading, tilt, and roll
                angle = (entry.uas_heading, 0.0, 0.0)
                angles.append(angle)

            # Create a new track in the folder
            trk = kml_folder.newgxtrack(name=name)
            trk.altitudemode = AltitudeMode.absolute

            # Append flight data
            trk.newwhen(when)
            trk.newgxcoord(coords)
            trk.newgxangle(angles)

            # Set styling
            trk.extrude = 1  # Extend path to ground
            trk.style.linestyle.width = 2
            trk.style.linestyle.color = Color.blue
            trk.iconstyle.icon.href = KML_PLANE_ICON
Пример #16
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()

        # user1 is on mission
        event = MissionClockEvent(user=self.user1,
                                  team_on_clock=True,
                                  team_on_timeout=False)
        event.save()
        # user1 is flying
        event = TakeoffOrLandingEvent(user=self.user1, uas_in_air=True)
        event.save()

        # user2 has landed
        event = TakeoffOrLandingEvent(user=self.user2, uas_in_air=True)
        event.save()
        event = TakeoffOrLandingEvent(user=self.user2, uas_in_air=False)
        event.save()

        # user2 is active
        self.timestamp = timezone.now()

        gps = GpsPosition(latitude=38.6462, longitude=-76.2452)
        gps.save()

        pos = AerialPosition(gps_position=gps, altitude_msl=0)
        pos.save()

        self.telem = UasTelemetry(user=self.user2,
                                  uas_position=pos,
                                  uas_heading=90)
        self.telem.save()
        self.telem.timestamp = dateutil.parser.parse(
            u'2016-10-01T00:00:00.0+00:00')

        self.telem.save()
Пример #17
0
def user_json(user):
    """Generate JSON-style dict for user."""
    telemetry = UasTelemetry.last_for_user(user)
    return {
        'name': user.username,
        'id': user.pk,
        'on_clock': MissionClockEvent.user_on_clock(user),
        'on_timeout': MissionClockEvent.user_on_timeout(user),
        'in_air': TakeoffOrLandingEvent.user_in_air(user),
        'telemetry': telemetry.json() if telemetry else None
    }
 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 create_event(self, time, uas_in_air):
     """Create a TakeoffOrLandingEvent for test user."""
     event = TakeoffOrLandingEvent(user=self.user1, uas_in_air=uas_in_air)
     event.save()
     event.timestamp = time
     event.save()
     return event
Пример #20
0
    def test_kml_simple(self):
        coordinates = [
            (-76.0, 38.0, 0.0),
            (-76.0, 38.0, 10.0),
            (-76.0, 38.0, 20.0),
            (-76.0, 38.0, 30.0),
            (-76.0, 38.0, 100.0),
            (-76.0, 38.0, 30.0),
            (-76.0, 38.0, 60.0),
        ]
        # Create Coordinates
        start = TakeoffOrLandingEvent(user=self.user, uas_in_air=True)
        start.save()
        for coord in coordinates:
            self.create_log_element(*coord)
        end = TakeoffOrLandingEvent(user=self.user, uas_in_air=False)
        end.save()

        kml = Kml()
        UasTelemetry.kml(user=self.user,
                         logs=UasTelemetry.by_user(self.user),
                         kml=kml,
                         kml_doc=kml)
        for coord in coordinates:
            tag = self.coord_format.format(coord[1], coord[0],
                                           units.feet_to_meters(coord[2]))
            self.assertTrue(tag in kml.kml())
Пример #21
0
def team_proto(user):
    """Generate TeamStatus proto for team."""
    team_status_proto = interop_admin_api_pb2.TeamStatus()
    team_status_proto.team = user.username
    team_status_proto.in_air = TakeoffOrLandingEvent.user_in_air(user)

    telemetry = UasTelemetry.last_for_user(user)
    if telemetry is not None:
        apos = telemetry.uas_position
        gpos = apos.gps_position
        telemetry_proto = team_status_proto.telemetry
        telemetry_proto.latitude = gpos.latitude
        telemetry_proto.longitude = gpos.longitude
        telemetry_proto.altitude = apos.altitude_msl
        telemetry_proto.heading = telemetry.uas_heading
        team_status_proto.telemetry_timestamp = telemetry.timestamp.isoformat()

    return team_status_proto
Пример #22
0
def team_proto(user):
    """Generate TeamStatus proto for team."""
    team_status_proto = interop_admin_api_pb2.TeamStatus()
    team_status_proto.id = user.pk
    team_status_proto.team.username = user.username
    team_status_proto.team.name = user.first_name
    team_status_proto.team.university = user.last_name
    team_status_proto.in_air = TakeoffOrLandingEvent.user_in_air(user)

    telemetry = UasTelemetry.last_for_user(user)
    if telemetry is not None:
        telemetry_proto = team_status_proto.telemetry
        telemetry_proto.latitude = telemetry.latitude
        telemetry_proto.longitude = telemetry.longitude
        telemetry_proto.altitude = telemetry.altitude_msl
        telemetry_proto.heading = telemetry.uas_heading
        team_status_proto.telemetry_timestamp = telemetry.timestamp.isoformat()

    return team_status_proto
Пример #23
0
def evaluate_teams(mission_config, users=None):
    """Evaluates the teams (non admin users) of the competition.

    Args:
        mission_config: The mission to evaluate users against.
        users: Optional list of users to eval. If None will evaluate all.
    Returns:
        A auvsi_suas.proto.MultiUserMissionEvaluation.
    """
    # Start a results map from user to MissionEvaluation.
    mission_eval = interop_admin_api_pb2.MultiUserMissionEvaluation()

    # If not provided, eval all users.
    if users is None:
        users = User.objects.all()

    logger.info('Starting team evaluations.')
    for user in sorted(users, key=lambda u: u.username):
        # Ignore admins.
        if user.is_superuser:
            continue

        # Ignore users with no flights for mission.
        if not TakeoffOrLandingEvent.flights(mission_config, user):
            logger.info('Skipping user with no flights: %s' % user.username)
            continue

        # Start the evaluation data structure.
        logger.info('Evaluation starting for user: %s.' % user.username)
        team_eval = mission_eval.teams.add()
        team_eval.team = user.username
        # Generate feedback.
        generate_feedback(mission_config, user, team_eval)
        # Generate score from feedback.
        score_team(team_eval)

    return mission_eval
Пример #24
0
    def kml(cls, user, logs, kml, kml_doc):
        """
        Appends kml nodes describing the given user's flight as described
        by the log array given.

        Args:
            user: A Django User to get username from
            logs: A list of UasTelemetry elements
            kml: A simpleKML Container to which the flight data will be added
            kml_doc: The simpleKML Document to which schemas will be added
        Returns:
            None
        """
        # KML Compliant Datetime Formatter
        kml_datetime_format = "%Y-%m-%dT%H:%M:%S.%fZ"
        icon = 'http://maps.google.com/mapfiles/kml/shapes/airports.png'
        threshold = 1  # Degrees

        kml_folder = kml.newfolder(name=user.username)

        flights = TakeoffOrLandingEvent.flights(user)
        if len(flights) == 0:
            return

        logs = filter(lambda log: cls._is_bad_position(log, threshold), logs)
        for i, flight in enumerate(flights):
            label = 'Flight {}'.format(i + 1)  # Flights are one-indexed
            kml_flight = kml_folder.newfolder(name=label)

            flight_logs = filter(lambda x: flight.within(x.timestamp), logs)
            if len(flight_logs) < 2:
                continue

            coords = []
            angles = []
            when = []
            for entry in flight_logs:
                pos = entry.uas_position.gps_position
                # Spatial Coordinates
                coord = (pos.longitude, pos.latitude,
                         units.feet_to_meters(entry.uas_position.altitude_msl))
                coords.append(coord)

                # Time Elements
                time = entry.timestamp.strftime(kml_datetime_format)
                when.append(time)

                # Degrees heading, tilt, and roll
                angle = (entry.uas_heading, 0.0, 0.0)
                angles.append(angle)

            # Create a new track in the folder
            trk = kml_flight.newgxtrack(name='Flight Path')
            trk.altitudemode = AltitudeMode.absolute

            # Append flight data
            trk.newwhen(when)
            trk.newgxcoord(coords)
            trk.newgxangle(angles)

            # Set styling
            trk.extrude = 1  # Extend path to ground
            trk.style.linestyle.width = 2
            trk.style.linestyle.color = Color.blue
            trk.iconstyle.icon.href = icon

            for obstacle in MovingObstacle.objects.all():
                obstacle.kml(path=flight_logs, kml=kml_flight, kml_doc=kml_doc)
Пример #25
0
    def setUp(self):
        """Setup the test case."""
        super(TestOdlcEvaluator, self).setUp()
        self.maxDiff = None
        self.user = User.objects.create_user('user', '*****@*****.**',
                                             'pass')

        l1 = GpsPosition(latitude=38, longitude=-76)
        l1.save()
        l2 = GpsPosition(latitude=38.0003, longitude=-76)
        l2.save()
        l3 = GpsPosition(latitude=-38, longitude=76)
        l3.save()
        l4 = GpsPosition(latitude=0, longitude=0)
        l4.save()

        event = TakeoffOrLandingEvent(user=self.user, uas_in_air=True)
        event.save()

        # A odlc worth full points.
        self.submit1 = Odlc(user=self.user,
                            odlc_type=OdlcType.standard,
                            location=l1,
                            orientation=Orientation.s,
                            shape=Shape.square,
                            background_color=Color.white,
                            alphanumeric='ABC',
                            alphanumeric_color=Color.black,
                            description='Submit test odlc 1',
                            description_approved=True,
                            autonomous=True,
                            thumbnail_approved=True)
        self.submit1.save()
        self.real1 = Odlc(user=self.user,
                          odlc_type=OdlcType.standard,
                          location=l1,
                          orientation=Orientation.s,
                          shape=Shape.square,
                          background_color=Color.white,
                          alphanumeric='ABC',
                          alphanumeric_color=Color.black,
                          description='Real odlc 1')
        self.real1.save()

        # A odlc worth less than full points.
        self.submit2 = Odlc(
            user=self.user,
            odlc_type=OdlcType.standard,
            location=l1,
            orientation=Orientation.n,
            shape=Shape.circle,
            background_color=Color.white,
            # alphanumeric set below
            alphanumeric_color=Color.black,
            description='Submit test odlc 2',
            autonomous=False,
            thumbnail_approved=True)
        self.submit2.save()
        self.real2 = Odlc(user=self.user,
                          odlc_type=OdlcType.standard,
                          location=l2,
                          orientation=Orientation.s,
                          shape=Shape.triangle,
                          background_color=Color.white,
                          alphanumeric='ABC',
                          alphanumeric_color=Color.black,
                          description='Real test odlc 2')
        self.real2.save()

        # A odlc worth no points, so unmatched.
        self.submit3 = Odlc(user=self.user,
                            odlc_type=OdlcType.standard,
                            location=l4,
                            orientation=Orientation.nw,
                            shape=Shape.pentagon,
                            background_color=Color.gray,
                            alphanumeric='XYZ',
                            alphanumeric_color=Color.orange,
                            description='Incorrect description',
                            autonomous=False,
                            thumbnail_approved=True)
        self.submit3.save()
        self.real3 = Odlc(
            user=self.user,
            odlc_type=OdlcType.standard,
            orientation=Orientation.e,
            shape=Shape.semicircle,
            background_color=Color.yellow,
            alphanumeric='LMN',
            # alphanumeric_color set below
            location=l3,
            description='Test odlc 3')
        self.real3.save()

        # Odlcs without approved image has no match value.
        self.submit4 = Odlc(user=self.user,
                            odlc_type=OdlcType.emergent,
                            location=l1,
                            description='Test odlc 4',
                            autonomous=False,
                            thumbnail_approved=False)
        self.submit4.save()
        self.real4 = Odlc(user=self.user,
                          odlc_type=OdlcType.emergent,
                          location=l1,
                          description='Test odlc 4')
        self.real4.save()

        # A odlc without location worth fewer points.
        self.submit5 = Odlc(user=self.user,
                            odlc_type=OdlcType.standard,
                            orientation=Orientation.n,
                            shape=Shape.trapezoid,
                            background_color=Color.purple,
                            alphanumeric='PQR',
                            alphanumeric_color=Color.blue,
                            description='Test odlc 5',
                            autonomous=False,
                            thumbnail_approved=True)
        self.submit5.save()
        self.real5 = Odlc(user=self.user,
                          odlc_type=OdlcType.standard,
                          location=l1,
                          orientation=Orientation.n,
                          shape=Shape.trapezoid,
                          background_color=Color.purple,
                          alphanumeric='PQR',
                          alphanumeric_color=Color.blue,
                          description='Test odlc 5')
        self.real5.save()

        # Emergent odlc with correct description.
        self.submit6 = Odlc(user=self.user,
                            odlc_type=OdlcType.emergent,
                            location=l1,
                            description='Submit test odlc 6',
                            description_approved=True,
                            autonomous=True,
                            thumbnail_approved=True)
        self.submit6.save()
        self.real6 = Odlc(user=self.user,
                          odlc_type=OdlcType.emergent,
                          location=l1,
                          description='Real odlc 1',
                          description_approved=True)
        self.real6.save()

        event = TakeoffOrLandingEvent(user=self.user, uas_in_air=False)
        event.save()

        # submit2 updated after landing.
        self.submit2.alphanumeric = 'ABC'
        self.submit2.update_last_modified()
        self.submit2.save()
        self.submit2.update_last_modified()
        self.submit3.alphanumeric_color = Color.yellow
        self.submit3.update_last_modified()
        self.submit3.save()
        # Unused but not unmatched odlc.
        self.submit7 = Odlc(user=self.user,
                            odlc_type=OdlcType.standard,
                            location=l4,
                            alphanumeric_color=Color.black,
                            description='Submit unused test odlc 1',
                            autonomous=False,
                            thumbnail_approved=True)
        self.submit7.save()

        self.submitted_odlcs = [
            self.submit7, self.submit6, self.submit5, self.submit4,
            self.submit3, self.submit2, self.submit1
        ]
        self.real_odlcs = [
            self.real1, self.real2, self.real3, self.real4, self.real5,
            self.real6
        ]
Пример #26
0
    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))
Пример #27
0
    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))
Пример #28
0
 def test_user_in_air_no_logs(self):
     """Not in-air without logs."""
     self.assertFalse(TakeoffOrLandingEvent.user_in_air(self.user1))
Пример #29
0
    def setUp(self):
        """Setup the test case."""
        super(TestTargetEvaluator, self).setUp()
        self.maxDiff = None
        self.user = User.objects.create_user('user', '*****@*****.**',
                                             'pass')

        l1 = GpsPosition(latitude=38, longitude=-76)
        l1.save()
        l2 = GpsPosition(latitude=38.0003, longitude=-76)
        l2.save()
        l3 = GpsPosition(latitude=-38, longitude=76)
        l3.save()

        event = MissionClockEvent(user=self.user,
                                  team_on_clock=True,
                                  team_on_timeout=False)
        event.save()

        event = TakeoffOrLandingEvent(user=self.user, uas_in_air=True)
        event.save()

        # A target worth full points.
        self.submit1 = Target(user=self.user,
                              target_type=TargetType.standard,
                              location=l1,
                              orientation=Orientation.s,
                              shape=Shape.square,
                              background_color=Color.white,
                              alphanumeric='ABC',
                              alphanumeric_color=Color.black,
                              description='Submit test target 1',
                              autonomous=True,
                              thumbnail_approved=True)
        self.submit1.save()
        self.real1 = Target(user=self.user,
                            target_type=TargetType.standard,
                            location=l1,
                            orientation=Orientation.s,
                            shape=Shape.square,
                            background_color=Color.white,
                            alphanumeric='ABC',
                            alphanumeric_color=Color.black,
                            description='Real target 1')
        self.real1.save()

        event = MissionClockEvent(user=self.user,
                                  team_on_clock=False,
                                  team_on_timeout=False)
        event.save()

        # A target worth less than full points.
        self.submit2 = Target(user=self.user,
                              target_type=TargetType.standard,
                              location=l1,
                              orientation=Orientation.n,
                              shape=Shape.circle,
                              background_color=Color.white,
                              # alphanumeric set below
                              alphanumeric_color=Color.black,
                              description='Submit test target 2',
                              autonomous=False,
                              thumbnail_approved=True)
        self.submit2.save()
        self.real2 = Target(user=self.user,
                            target_type=TargetType.standard,
                            location=l2,
                            orientation=Orientation.s,
                            shape=Shape.triangle,
                            background_color=Color.white,
                            alphanumeric='ABC',
                            alphanumeric_color=Color.black,
                            description='Real test target 2')
        self.real2.save()

        # A target worth no points, so unmatched.
        self.submit3 = Target(user=self.user,
                              target_type=TargetType.standard,
                              location=l1,
                              orientation=Orientation.nw,
                              shape=Shape.pentagon,
                              background_color=Color.gray,
                              alphanumeric='XYZ',
                              alphanumeric_color=Color.orange,
                              description='Incorrect description',
                              autonomous=False,
                              thumbnail_approved=True)
        self.submit3.save()
        self.real3 = Target(user=self.user,
                            target_type=TargetType.standard,
                            orientation=Orientation.e,
                            shape=Shape.semicircle,
                            background_color=Color.yellow,
                            alphanumeric='LMN',
                            # alphanumeric_color set below
                            location=l3,
                            description='Test target 3')
        self.real3.save()

        # Targets without approved image may still match.
        self.submit4 = Target(user=self.user,
                              target_type=TargetType.emergent,
                              location=l1,
                              description='Test target 4',
                              autonomous=False,
                              thumbnail_approved=False)
        self.submit4.save()
        self.real4 = Target(user=self.user,
                            target_type=TargetType.emergent,
                            location=l1,
                            description='Test target 4')
        self.real4.save()

        # A target without location worth fewer points.
        self.submit5 = Target(user=self.user,
                              target_type=TargetType.standard,
                              orientation=Orientation.n,
                              shape=Shape.trapezoid,
                              background_color=Color.purple,
                              alphanumeric='PQR',
                              alphanumeric_color=Color.blue,
                              description='Test target 5',
                              autonomous=False,
                              thumbnail_approved=True)
        self.submit5.save()
        self.real5 = Target(user=self.user,
                            target_type=TargetType.standard,
                            location=l1,
                            orientation=Orientation.n,
                            shape=Shape.trapezoid,
                            background_color=Color.purple,
                            alphanumeric='PQR',
                            alphanumeric_color=Color.blue,
                            description='Test target 5')
        self.real5.save()

        event = TakeoffOrLandingEvent(user=self.user, uas_in_air=False)
        event.save()

        # submit2 updated after landing.
        self.submit2.alphanumeric = 'ABC'
        self.submit2.save()

        self.submit3.alphanumeric_color = Color.yellow
        self.submit3.save()

        self.submitted_targets = [self.submit5, self.submit4, self.submit3,
                                  self.submit2, self.submit1]
        self.real_targets = [self.real1, self.real2, self.real3, self.real4,
                             self.real5]
        self.real_matched_targets = [self.real1, self.real2, self.real4,
                                     self.real5]
 def test_unicode(self):
     """Tests the unicode method executes."""
     log = TakeoffOrLandingEvent(user=self.user1, uas_in_air=True)
     log.save()
     self.assertIsNotNone(log.__unicode__())
Пример #31
0
def generate_feedback(mission_config, user, team_eval):
    """Generates mission feedback for the given team and mission.

    Args:
        mission_config: The mission to evaluate the team against.
        user: The team user object for which to evaluate and provide feedback.
        team_eval: The team evaluation to fill.
    """
    feedback = team_eval.feedback

    # Calculate the total mission clock time.
    missions = MissionClockEvent.missions(user)
    mission_clock_time = datetime.timedelta(seconds=0)
    for mission in missions:
        duration = mission.duration()
        if duration is None:
            team_eval.warnings.append('Infinite mission clock.')
        else:
            mission_clock_time += duration
    feedback.mission_clock_time_sec = mission_clock_time.total_seconds()

    # Calculate total time in air.
    flight_periods = TakeoffOrLandingEvent.flights(user)
    if flight_periods:
        flight_time = reduce(lambda x, y: x + y, [p.duration()
                                                  for p in flight_periods])
        feedback.flight_time_sec = flight_time.total_seconds()
    else:
        feedback.flight_time_sec = 0
    # Find the user's flights.
    for period in flight_periods:
        if period.duration() is None:
            team_eval.warnings.append('Infinite flight period.')
    uas_period_logs = [
        UasTelemetry.dedupe(logs)
        for logs in UasTelemetry.by_time_period(user, flight_periods)
    ]
    uas_logs = list(itertools.chain.from_iterable(uas_period_logs))
    if not uas_logs:
        team_eval.warnings.append('No UAS telemetry logs.')

    # Determine interop telemetry rates.
    telem_max, telem_avg = UasTelemetry.rates(user,
                                              flight_periods,
                                              time_period_logs=uas_period_logs)
    if telem_max:
        feedback.uas_telemetry_time_max_sec = telem_max
    if telem_avg:
        feedback.uas_telemetry_time_avg_sec = telem_avg

    # Determine if the uas went out of bounds. This must be done for
    # each period individually so time between periods isn't counted as
    # out of bounds time. Note that this calculates reported time out
    # of bounds, not actual or possible time spent out of bounds.
    out_of_bounds = datetime.timedelta(seconds=0)
    feedback.boundary_violations = 0
    for logs in uas_period_logs:
        bv, bt = FlyZone.out_of_bounds(mission_config.fly_zones.all(), logs)
        feedback.boundary_violations += bv
        out_of_bounds += bt
    feedback.out_of_bounds_time_sec = out_of_bounds.total_seconds()

    # Determine if the uas hit the waypoints.
    feedback.waypoints.extend(UasTelemetry.satisfied_waypoints(
        mission_config.home_pos, mission_config.mission_waypoints.order_by(
            'order'), uas_logs))

    # Evaluate the targets.
    user_targets = Target.objects.filter(user=user).all()
    evaluator = TargetEvaluator(user_targets, mission_config.targets.all())
    feedback.target.CopyFrom(evaluator.evaluate())

    # Determine collisions with stationary and moving obstacles.
    for obst in mission_config.stationary_obstacles.all():
        obst_eval = feedback.stationary_obstacles.add()
        obst_eval.id = obst.pk
        obst_eval.hit = obst.evaluate_collision_with_uas(uas_logs)
    for obst in mission_config.moving_obstacles.all():
        obst_eval = feedback.moving_obstacles.add()
        obst_eval.id = obst.pk
        obst_eval.hit = obst.evaluate_collision_with_uas(uas_logs)

    # Add judge feedback.
    try:
        judge_feedback = MissionJudgeFeedback.objects.get(
            mission=mission_config.pk,
            user=user.pk)
        feedback.judge.CopyFrom(judge_feedback.proto())
    except MissionJudgeFeedback.DoesNotExist:
        team_eval.warnings.append('No MissionJudgeFeedback for team.')

    # Sanity check mission time.
    judge_mission_clock = (
        feedback.judge.flight_time_sec + feedback.judge.post_process_time_sec)
    if abs(feedback.mission_clock_time_sec - judge_mission_clock) > 30:
        team_eval.warnings.append(
            'Mission clock differs between interop and judge.')
Пример #32
0
    def kml(cls, user, logs, kml, kml_doc):
        """
        Appends kml nodes describing the given user's flight as described
        by the log array given.

        Args:
            user: A Django User to get username from
            logs: A list of UasTelemetry elements
            kml: A simpleKML Container to which the flight data will be added
            kml_doc: The simpleKML Document to which schemas will be added
        Returns:
            None
        """
        # KML Compliant Datetime Formatter
        kml_datetime_format = "%Y-%m-%dT%H:%M:%S.%fZ"
        icon = 'http://maps.google.com/mapfiles/kml/shapes/airports.png'

        kml_folder = kml.newfolder(name=user.username)

        flights = TakeoffOrLandingEvent.flights(user)
        if len(flights) == 0:
            return

        logs = UasTelemetry.dedupe(UasTelemetry.filter_bad(logs))
        for i, flight in enumerate(flights):
            label = 'Flight {}'.format(i + 1)  # Flights are one-indexed
            kml_flight = kml_folder.newfolder(name=label)

            flight_logs = filter(lambda x: flight.within(x.timestamp), logs)
            if len(flight_logs) < 2:
                continue

            coords = []
            angles = []
            when = []
            for entry in flight_logs:
                pos = entry.uas_position.gps_position
                # Spatial Coordinates
                coord = (pos.longitude, pos.latitude,
                         units.feet_to_meters(entry.uas_position.altitude_msl))
                coords.append(coord)

                # Time Elements
                time = entry.timestamp.strftime(kml_datetime_format)
                when.append(time)

                # Degrees heading, tilt, and roll
                angle = (entry.uas_heading, 0.0, 0.0)
                angles.append(angle)

            # Create a new track in the folder
            trk = kml_flight.newgxtrack(name='Flight Path')
            trk.altitudemode = AltitudeMode.absolute

            # Append flight data
            trk.newwhen(when)
            trk.newgxcoord(coords)
            trk.newgxangle(angles)

            # Set styling
            trk.extrude = 1  # Extend path to ground
            trk.style.linestyle.width = 2
            trk.style.linestyle.color = Color.blue
            trk.iconstyle.icon.href = icon
Пример #33
0
def simulate_team_mission(test, mission, superuser, user):
    """Simulates a team's mission demonstration.

    Args:
        test: The test case.
        mission: The mission to simulate for.
        superuser: The superuser for admin actions.
        user: The user for the team.
    """
    total_telem = 100
    waypoints_hit = len(mission.mission_waypoints.all())
    odlcs_correct = len(mission.odlcs.all())
    odlcs_incorrect = 1

    c = Client()
    c.force_login(user)

    # Send telemetry during flight.
    TakeoffOrLandingEvent(user=user, mission=mission, uas_in_air=True).save()
    for i in range(total_telem):
        simulate_telemetry(test,
                           client=c,
                           mission=mission,
                           hit_waypoint=i < waypoints_hit)
    TakeoffOrLandingEvent(user=user, mission=mission, uas_in_air=False).save()

    # Submit ODLCs.
    for _ in range(odlcs_correct):
        simulate_odlc(test, c, mission, actual=True)
    for _ in range(odlcs_incorrect):
        simulate_odlc(test, c, mission, actual=False)

    # Judge feedback submitted.
    feedback = MissionJudgeFeedback()
    feedback.mission = mission
    feedback.user = user
    feedback.flight_time = datetime.timedelta(seconds=1)
    feedback.post_process_time = datetime.timedelta(seconds=1)
    feedback.used_timeout = False
    feedback.min_auto_flight_time = True
    feedback.safety_pilot_takeovers = 1
    feedback.waypoints_captured = len(mission.mission_waypoints.all()) / 2
    feedback.out_of_bounds = 1
    feedback.unsafe_out_of_bounds = 0
    feedback.things_fell_off_uas = 0
    feedback.crashed = False
    feedback.air_drop_accuracy = interop_admin_api_pb2.MissionJudgeFeedback.WITHIN_25_FT
    feedback.ugv_drove_to_location = True
    feedback.operational_excellence_percent = 90
    feedback.save()

    # ODLCs thumbnails reviewed.
    c = Client()
    c.force_login(superuser)
    review_data = json.loads(c.get(odlcs_review_url).content)
    for odlc_review in review_data:
        pk = int(odlc_review['odlc']['id'])
        review = interop_admin_api_pb2.OldcReview()
        review.odlc.id = pk
        review.thumbnail_approved = True
        review.description_approved = True
        r = client.put(odlc_review_id_url(args=[pk]),
                       data=json_format.MessageToJson(review),
                       content_type='application/json')
        test.assertEqual(r.status_code, 200, r.content)
    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))
Пример #36
0
    def evaluate_periods(self, expected):
        """Check actual periods against expected."""
        periods = TakeoffOrLandingEvent.flights(self.user1)

        self.assertSequenceEqual(expected, periods)
 def test_user_in_air_no_logs(self):
     """Not in-air without logs."""
     self.assertFalse(TakeoffOrLandingEvent.user_in_air(self.user1))
Пример #38
0
    def test_kml_filter(self):
        coordinates = [
            (0, -76.0, 38.0, 0.0, 0),
            (1, -76.0, 38.0, 10.0, 0),
            (2, -76.0, 38.0, 20.0, 0),
            (3, -76.0, 38.0, 30.0, 0),
            (4, -76.0, 38.0, 100.0, 0),
            (5, -76.0, 38.0, 30.0, 0),
            (6, -76.0, 38.0, 60.0, 0),
        ]
        filtered_out = [
            (7, 0.1, 0.001, 100, 0),
            (8, 0.0, 0.0, 0, 0),
        ]
        # Create Coordinates
        start = TakeoffOrLandingEvent(user=self.user, uas_in_air=True)
        start.save()
        start.timestamp = self.now
        start.save()
        for coord in coordinates:
            self.create_log_element(*coord)
        for coord in filtered_out:
            self.create_log_element(*coord)
        end = TakeoffOrLandingEvent(user=self.user, uas_in_air=False)
        end.save()
        end.timestamp = self.now + datetime.timedelta(seconds=6.5)
        end.save()

        kml = Kml()
        UasTelemetry.kml(
            user=self.user,
            logs=UasTelemetry.by_user(self.user),
            kml=kml,
            kml_doc=kml)

        for filtered in filtered_out:
            tag = self.coord_format.format(filtered[2], filtered[1],
                                           units.feet_to_meters(filtered[3]))
            self.assertTrue(tag not in kml.kml())

        for coord in coordinates:
            tag = self.coord_format.format(coord[2], coord[1],
                                           units.feet_to_meters(coord[3]))
            self.assertTrue(tag in kml.kml())
Пример #39
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.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()
Пример #40
0
    def test_actionable_submission(self):
        """Tests actionable_submission correctly filters submissions."""
        # t1 created and updated before take off.
        t1 = Target(user=self.user, target_type=TargetType.standard)
        t1.save()
        t1.alphanumeric = 'A'
        t1.save()

        # t2 created before take off and updated in flight.
        t2 = Target(user=self.user, target_type=TargetType.standard)
        t2.save()

        event = TakeoffOrLandingEvent(user=self.user, uas_in_air=True)
        event.save()

        t2.alphanumeric = 'A'
        t2.save()

        # t3 created and updated in flight.
        t3 = Target(user=self.user, target_type=TargetType.standard)
        t3.save()
        t3.alphanumeric = 'A'
        t3.save()

        # t4 created in flight and updated after landing.
        t4 = Target(user=self.user, target_type=TargetType.standard)
        t4.save()

        event = TakeoffOrLandingEvent(user=self.user, uas_in_air=False)
        event.save()

        t4.alphanumeric = 'A'
        t4.save()

        # t5 created and updated after landing.
        t5 = Target(user=self.user, target_type=TargetType.standard)
        t5.save()
        t5.alphanumeric = 'A'
        t5.save()

        # t6 created and updated in second flight.
        event = TakeoffOrLandingEvent(user=self.user, uas_in_air=True)
        event.save()
        t6 = Target(user=self.user, target_type=TargetType.standard)
        t6.save()
        t6.alphanumeric = 'A'
        t6.save()
        event = TakeoffOrLandingEvent(user=self.user, uas_in_air=False)
        event.save()

        self.assertFalse(t1.actionable_submission())
        self.assertFalse(t2.actionable_submission())
        self.assertTrue(t3.actionable_submission())
        self.assertFalse(t4.actionable_submission())
        self.assertFalse(t5.actionable_submission())
        self.assertFalse(t6.actionable_submission())
    def evaluate_periods(self, expected):
        """Check actual periods against expected."""
        periods = TakeoffOrLandingEvent.flights(self.user1)

        self.assertSequenceEqual(expected, periods)
Пример #42
0
def generate_feedback(mission_config, user, team_eval):
    """Generates mission feedback for the given team and mission.

    Args:
        mission_config: The mission to evaluate the team against.
        user: The team user object for which to evaluate and provide feedback.
        team_eval: The team evaluation to fill.
    """
    feedback = team_eval.feedback

    # Calculate the total mission clock time.
    missions = MissionClockEvent.missions(user)
    mission_clock_time = datetime.timedelta(seconds=0)
    for mission in missions:
        duration = mission.duration()
        if duration is None:
            team_eval.warnings.append('Infinite mission clock.')
        else:
            mission_clock_time += duration
    feedback.mission_clock_time_sec = mission_clock_time.total_seconds()

    # Calculate total time in air.
    flight_periods = TakeoffOrLandingEvent.flights(user)
    if flight_periods:
        flight_time = reduce(lambda x, y: x + y,
                             [p.duration() for p in flight_periods])
        feedback.flight_time_sec = flight_time.total_seconds()
    else:
        feedback.flight_time_sec = 0
    # Find the user's flights.
    for period in flight_periods:
        if period.duration() is None:
            team_eval.warnings.append('Infinite flight period.')
    uas_period_logs = [
        UasTelemetry.dedupe(logs)
        for logs in UasTelemetry.by_time_period(user, flight_periods)
    ]
    uas_logs = list(itertools.chain.from_iterable(uas_period_logs))
    if not uas_logs:
        team_eval.warnings.append('No UAS telemetry logs.')

    # Determine interop telemetry rates.
    telem_max, telem_avg = UasTelemetry.rates(user,
                                              flight_periods,
                                              time_period_logs=uas_period_logs)
    if telem_max:
        feedback.uas_telemetry_time_max_sec = telem_max
    if telem_avg:
        feedback.uas_telemetry_time_avg_sec = telem_avg

    # Determine if the uas went out of bounds. This must be done for
    # each period individually so time between periods isn't counted as
    # out of bounds time. Note that this calculates reported time out
    # of bounds, not actual or possible time spent out of bounds.
    out_of_bounds = datetime.timedelta(seconds=0)
    feedback.boundary_violations = 0
    for logs in uas_period_logs:
        bv, bt = FlyZone.out_of_bounds(mission_config.fly_zones.all(), logs)
        feedback.boundary_violations += bv
        out_of_bounds += bt
    feedback.out_of_bounds_time_sec = out_of_bounds.total_seconds()

    # Determine if the uas hit the waypoints.
    feedback.waypoints.extend(
        UasTelemetry.satisfied_waypoints(
            mission_config.home_pos,
            mission_config.mission_waypoints.order_by('order'), uas_logs))

    # Evaluate the object detections.
    user_odlcs = Odlc.objects.filter(user=user).all()
    evaluator = OdlcEvaluator(user_odlcs, mission_config.odlcs.all())
    feedback.odlc.CopyFrom(evaluator.evaluate())

    # Determine collisions with stationary and moving obstacles.
    for obst in mission_config.stationary_obstacles.all():
        obst_eval = feedback.stationary_obstacles.add()
        obst_eval.id = obst.pk
        obst_eval.hit = obst.evaluate_collision_with_uas(uas_logs)
    for obst in mission_config.moving_obstacles.all():
        obst_eval = feedback.moving_obstacles.add()
        obst_eval.id = obst.pk
        obst_eval.hit = obst.evaluate_collision_with_uas(uas_logs)

    # Add judge feedback.
    try:
        judge_feedback = MissionJudgeFeedback.objects.get(
            mission=mission_config.pk, user=user.pk)
        feedback.judge.CopyFrom(judge_feedback.proto())
    except MissionJudgeFeedback.DoesNotExist:
        team_eval.warnings.append('No MissionJudgeFeedback for team.')

    # Sanity check mission time.
    judge_mission_clock = (feedback.judge.flight_time_sec +
                           feedback.judge.post_process_time_sec)
    if abs(feedback.mission_clock_time_sec - judge_mission_clock) > 30:
        team_eval.warnings.append(
            'Mission clock differs between interop and judge.')
 def test_unicode(self):
     """Tests the unicode method executes."""
     log = TakeoffOrLandingEvent(user=self.user1, uas_in_air=True)
     log.save()
     self.assertIsNotNone(log.__unicode__())