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")
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")
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")
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())
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
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)
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
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))
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))
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.')
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 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 }
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
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()
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
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())
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
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
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
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)
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 ]
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_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_no_logs(self): """Not in-air without logs.""" self.assertFalse(TakeoffOrLandingEvent.user_in_air(self.user1))
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__())
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.')
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
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))
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))
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())
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_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)
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__())