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 __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.missions = MissionClockEvent.missions(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 Target meets Actionable Intelligence submission criteria. A target is "actionable" if it was submitted and last updated during the aircraft's first flight. Args: flights: Optional memoized flights for this target's user. If omitted, the flights will be looked up. Returns: True if target may be considered an "actionable" submission. """ if flights is None: flights = TakeoffOrLandingEvent.flights(self.user) if len(flights) > 0: flight = flights[0] if flight.within(self.creation_time) and flight.within(self.last_modified_time): return True return False
def actionable_submission(self, flights=None): """Checks if Target meets Actionable Intelligence submission criteria. A target is "actionable" if the aircraft was in flight from initial target submission until the last update. Note that this does not check the localization or characterization Actionable Intelligence criteria. Args: flights: Optional memoized flights for this target's user. If omitted, the flights will be looked up. Returns: True if target may be considered an "actionable" submission. """ if flights is None: flights = TakeoffOrLandingEvent.flights(self.user) for flight in flights: if flight.within(self.creation_time) and \ flight.within(self.last_modified_time): return True return False
def actionable_submission(self, flights=None): """Checks if Target meets Actionable Intelligence submission criteria. A target is "actionable" if it was submitted and last updated during the aircraft's first flight. Args: flights: Optional memoized flights for this target's user. If omitted, the flights will be looked up. Returns: True if target may be considered an "actionable" submission. """ if flights is None: flights = TakeoffOrLandingEvent.flights(self.user) if len(flights) > 0: flight = flights[0] if flight.within(self.creation_time) and \ flight.within(self.last_modified_time): return True return False
def __init__(self, submitted_targets, real_targets): """Creates an evaluation of submitted targets against real targets. Args: submitted_targets: List of submitted Target objects, all from the same user. real_targets: List of real Target objects made by judges. Raises: AssertionError: not all submitted targets are from the same user. """ self.submitted_targets = submitted_targets self.real_targets = real_targets if self.submitted_targets: self.user = self.submitted_targets[0].user for t in self.submitted_targets: if t.user != self.user: raise AssertionError( "All submitted targets must be from the same user") self.flights = TakeoffOrLandingEvent.flights(self.user) self.matches = self.match_targets(submitted_targets, real_targets)
def evaluate_teams(self): """Evaluates the teams (non admin users) of the competition. Returns: A map from user to evaluate data. The evaluation data has the following map structure: { 'waypoints_satisfied': { id: Boolean, } 'out_of_bounds_time': Seconds spent out of bounds, 'interop_times': { 'server_info': {'max': Value, 'avg': Value}, 'obst_info': {'max': Value, 'avg': Value}, 'uas_telem': {'max': Value, 'avg': Value}, }, 'stationary_obst_collision': { id: Boolean }, 'moving_obst_collision': { id: Boolean } } """ # Start a results map from user to evaluation data results = {} # Fill in evaluation data for each user except admins users = User.objects.all() logger.info("Starting team evaluations.") for user in users: # Ignore admins. if user.is_superuser: continue logger.info("Evaluation starting for user: %s." % user.username) # Start the evaluation data structure. eval_data = results.setdefault(user, {}) # Find the user's flights. flight_periods = TakeoffOrLandingEvent.flights(user) uas_period_logs = UasTelemetry.dedupe(UasTelemetry.by_time_period(user, flight_periods)) uas_logs = list(itertools.chain.from_iterable(uas_period_logs)) # Determine if the uas hit the waypoints. waypoints_hit = self.satisfied_waypoints(uas_logs) waypoints_keyed = {} for i, hit in enumerate(waypoints_hit): waypoints_keyed[i + 1] = hit eval_data["waypoints_satisfied"] = waypoints_keyed # 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_time = 0 for logs in uas_period_logs: out_of_bounds_time += FlyZone.out_of_bounds(self.fly_zones.all(), logs) eval_data["out_of_bounds_time"] = out_of_bounds_time # Determine interop rates. interop_times = eval_data.setdefault("interop_times", {}) server_info_times = ServerInfoAccessLog.rates(user, flight_periods) obstacle_times = ObstacleAccessLog.rates(user, flight_periods) uas_telemetry_times = UasTelemetry.rates(user, flight_periods, time_period_logs=uas_period_logs) interop_times["server_info"] = {"max": server_info_times[0], "avg": server_info_times[1]} interop_times["obst_info"] = {"max": obstacle_times[0], "avg": obstacle_times[1]} interop_times["uas_telem"] = {"max": uas_telemetry_times[0], "avg": uas_telemetry_times[1]} # Determine collisions with stationary and moving obstacles. stationary_collisions = eval_data.setdefault("stationary_obst_collision", {}) for obst in self.stationary_obstacles.all(): collision = obst.evaluate_collision_with_uas(uas_logs) stationary_collisions[obst.pk] = collision moving_collisions = eval_data.setdefault("moving_obst_collision", {}) for obst in self.moving_obstacles.all(): collision = obst.evaluate_collision_with_uas(uas_logs) moving_collisions[obst.pk] = collision return results
def kml(self, kml, kml_doc): """ Appends kml nodes describing this mission configurations. Args: kml: A simpleKML Container to which the mission data will be added kml_doc: The simpleKML Document to which schemas will be added """ mission_name = 'Mission {}'.format(self.pk) kml_folder = kml.newfolder(name=mission_name) # Static Points locations = { 'Home Position': self.home_pos, 'Emergent LKP': self.emergent_last_known_pos, 'Off Axis': self.off_axis_odlc_pos, 'Air Drop': self.air_drop_pos, } for key, point in locations.iteritems(): gps = (point.longitude, point.latitude) wp = kml_folder.newpoint(name=key, coords=[gps]) wp.description = str(point) # Waypoints waypoints_folder = kml_folder.newfolder(name='Waypoints') linestring = waypoints_folder.newlinestring(name='Waypoints') waypoints = [] waypoint_num = 1 for waypoint in self.mission_waypoints.order_by('order'): gps = waypoint.position.gps_position coord = (gps.longitude, gps.latitude, units.feet_to_meters(waypoint.position.altitude_msl)) waypoints.append(coord) # Add waypoint marker wp = waypoints_folder.newpoint(name=str(waypoint_num), coords=[coord]) wp.description = str(waypoint) wp.altitudemode = AltitudeMode.absolute wp.extrude = 1 wp.visibility = False waypoint_num += 1 linestring.coords = waypoints # Waypoints Style linestring.altitudemode = AltitudeMode.absolute linestring.extrude = 1 linestring.style.linestyle.color = Color.black linestring.style.polystyle.color = Color.changealphaint( 100, Color.green) # Search Area search_area_folder = kml_folder.newfolder(name='Search Area') search_area = [] search_area_num = 1 for point in self.search_grid_points.order_by('order'): gps = point.position.gps_position coord = (gps.longitude, gps.latitude, units.feet_to_meters(point.position.altitude_msl)) search_area.append(coord) # Add boundary marker wp = search_area_folder.newpoint(name=str(search_area_num), coords=[coord]) wp.description = str(point) wp.visibility = False search_area_num += 1 if search_area: # Create search area polygon. pol = search_area_folder.newpolygon(name='Search Area') search_area.append(search_area[0]) pol.outerboundaryis = search_area # Search Area Style. pol.style.linestyle.color = Color.black pol.style.linestyle.width = 2 pol.style.polystyle.color = Color.changealphaint(50, Color.blue) # Stationary Obstacles. stationary_obstacles_folder = kml_folder.newfolder( name='Stationary Obstacles') # TODO: Implement # Moving Obstacles. users = User.objects.all() moving_obstacle_periods = [] for user in users: moving_obstacle_periods.extend(TakeoffOrLandingEvent.flights(user)) moving_obstacles_folder = kml_folder.newfolder(name='Moving Obstacles') for obstacle in self.moving_obstacles.all(): obstacle.kml(moving_obstacle_periods, moving_obstacles_folder, kml_doc)
def evaluate_teams(self): """Evaluates the teams (non admin users) of the competition. Returns: A map from user to evaluate data. The evaluation data has the following map structure: { 'mission_clock_time': Seconds spent on mission clock, 'waypoints_satisfied': { id: Boolean, } 'out_of_bounds_time': Seconds spent out of bounds, 'targets': Data from TargetEvaluation, 'interop_times': { 'server_info': {'max': Value, 'avg': Value}, 'obst_info': {'max': Value, 'avg': Value}, 'uas_telem': {'max': Value, 'avg': Value}, }, 'stationary_obst_collision': { id: Boolean }, 'moving_obst_collision': { id: Boolean } 'warnings': [ "String message." ], } """ # Start a results map from user to evaluation data results = {} # Fill in evaluation data for each user except admins users = User.objects.all() logger.info('Starting team evaluations.') for user in users: # Ignore admins. if user.is_superuser: continue logger.info('Evaluation starting for user: %s.' % user.username) # Start the evaluation data structure. eval_data = results.setdefault(user, {}) warnings = [] eval_data['warnings'] = warnings # Calculate the total mission clock time. mission_clock_time = 0 missions = MissionClockEvent.missions(user) for mission in missions: duration = mission.duration() if duration is None: warnings.append('Infinite duration mission clock.') else: mission_clock_time += duration.total_seconds() eval_data['mission_clock_time'] = mission_clock_time # Find the user's flights. flight_periods = TakeoffOrLandingEvent.flights(user) for period in flight_periods: if period.duration() is None: warnings.append('Infinite duration 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: warnings.append('No UAS telemetry logs.') # Determine if the uas hit the waypoints. waypoints_hit, waypoints_hit_track, waypoints_closest = \ self.satisfied_waypoints(uas_logs) eval_data['waypoints_satisfied'] = waypoints_hit eval_data['waypoints_satisfied_track'] = waypoints_hit_track eval_data['waypoints_closest'] = waypoints_closest # 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_time = 0 for logs in uas_period_logs: out_of_bounds_time += FlyZone.out_of_bounds( self.fly_zones.all(), logs) eval_data['out_of_bounds_time'] = out_of_bounds_time # Evaluate the targets. eval_data['targets'] = {} user_targets = Target.objects.filter(user=user).all() target_sets = { 'manual': [t for t in user_targets if not t.autonomous], 'auto': [t for t in user_targets if t.autonomous][:settings.TARGET_MAX_NUM_AUTONOMOUS], } for target_set, targets in target_sets.iteritems(): evaluator = TargetEvaluator(targets, self.targets.all()) eval_data['targets'][target_set] = evaluator.evaluation_dict() # Determine interop rates. interop_times = eval_data.setdefault('interop_times', {}) server_info_times = ServerInfoAccessLog.rates(user, flight_periods) obstacle_times = ObstacleAccessLog.rates(user, flight_periods) uas_telemetry_times = UasTelemetry.rates( user, flight_periods, time_period_logs=uas_period_logs) interop_times['server_info'] = { 'max': server_info_times[0], 'avg': server_info_times[1] } interop_times['obst_info'] = { 'max': obstacle_times[0], 'avg': obstacle_times[1] } interop_times['uas_telem'] = { 'max': uas_telemetry_times[0], 'avg': uas_telemetry_times[1] } # Determine collisions with stationary and moving obstacles. stationary_collisions = eval_data.setdefault( 'stationary_obst_collision', {}) for obst in self.stationary_obstacles.all(): collision = obst.evaluate_collision_with_uas(uas_logs) stationary_collisions[obst.pk] = collision moving_collisions = eval_data.setdefault('moving_obst_collision', {}) for obst in self.moving_obstacles.all(): collision = obst.evaluate_collision_with_uas(uas_logs) moving_collisions[obst.pk] = collision return results
def evaluate_teams(self, users=None): """Evaluates the teams (non admin users) of the competition. Args: users: Optional list of users to eval. If None will evaluate all. Returns: A map from user to evaluate data. The evaluation data has the following map structure: { 'mission_clock_time': Seconds spent on mission clock, 'waypoints_satisfied': { id: Boolean, } 'out_of_bounds_time': Seconds spent out of bounds, 'targets': Data from TargetEvaluation, 'uas_telem_time': { 'max': Value, 'avg': Value, }, 'stationary_obst_collision': { id: Boolean }, 'moving_obst_collision': { id: Boolean } 'warnings': [ "String message." ], } """ # Start a results map from user to evaluation data results = {} # If not provided, eval all users. if users is None: users = User.objects.all() logger.info('Starting team evaluations.') for user in users: # Ignore admins. if user.is_superuser: continue logger.info('Evaluation starting for user: %s.' % user.username) # Start the evaluation data structure. eval_data = results.setdefault(user, {}) warnings = [] eval_data['warnings'] = warnings # Calculate the total mission clock time. mission_clock_time = 0 missions = MissionClockEvent.missions(user) for mission in missions: duration = mission.duration() if duration is None: warnings.append('Infinite duration mission clock.') else: mission_clock_time += duration.total_seconds() eval_data['mission_clock_time'] = mission_clock_time # Find the user's flights. flight_periods = TakeoffOrLandingEvent.flights(user) for period in flight_periods: if period.duration() is None: warnings.append('Infinite duration 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: warnings.append('No UAS telemetry logs.') # Determine if the uas hit the waypoints. waypoints_hit, waypoints_hit_track, waypoints_closest = \ self.satisfied_waypoints(uas_logs) eval_data['waypoints_satisfied'] = waypoints_hit eval_data['waypoints_satisfied_track'] = waypoints_hit_track eval_data['waypoints_closest'] = waypoints_closest # 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_time = 0 for logs in uas_period_logs: out_of_bounds_time += FlyZone.out_of_bounds( self.fly_zones.all(), logs) eval_data['out_of_bounds_time'] = out_of_bounds_time # Evaluate the targets. eval_data['targets'] = {} user_targets = Target.objects.filter(user=user).all() target_sets = { 'manual': [t for t in user_targets if not t.autonomous], 'auto': [t for t in user_targets if t.autonomous], } for target_set, targets in target_sets.iteritems(): evaluator = TargetEvaluator(targets, self.targets.all()) eval_data['targets'][target_set] = evaluator.evaluation_dict() # Determine interop telemetry rates. uas_telemetry_times = UasTelemetry.rates( user, flight_periods, time_period_logs=uas_period_logs) eval_data['uas_telem_times'] = { 'max': uas_telemetry_times[0], 'avg': uas_telemetry_times[1] } # Determine collisions with stationary and moving obstacles. stationary_collisions = eval_data.setdefault( 'stationary_obst_collision', {}) for obst in self.stationary_obstacles.all(): collision = obst.evaluate_collision_with_uas(uas_logs) stationary_collisions[obst.pk] = collision moving_collisions = eval_data.setdefault('moving_obst_collision', {}) for obst in self.moving_obstacles.all(): collision = obst.evaluate_collision_with_uas(uas_logs) moving_collisions[obst.pk] = collision return results
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 evaluate_teams(self): """Evaluates the teams (non admin users) of the competition. Returns: A map from user to evaluate data. The evaluation data has the following map structure: { 'waypoints_satisfied': { id: Boolean, } 'out_of_bounds_time': Seconds spent out of bounds, 'interop_times': { 'server_info': {'max': Value, 'avg': Value}, 'obst_info': {'max': Value, 'avg': Value}, 'uas_telem': {'max': Value, 'avg': Value}, }, 'stationary_obst_collision': { id: Boolean }, 'moving_obst_collision': { id: Boolean } } """ # Start a results map from user to evaluation data results = {} # Fill in evaluation data for each user except admins users = User.objects.all() logger.info('Starting team evaluations.') for user in users: # Ignore admins. if user.is_superuser: continue logger.info('Evaluation starting for user: %s.' % user.username) # Start the evaluation data structure. eval_data = results.setdefault(user, {}) # Find the user's flights. flight_periods = TakeoffOrLandingEvent.flights(user) 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)) # Determine if the uas hit the waypoints. waypoints_hit = self.satisfied_waypoints(uas_logs) waypoints_keyed = {} for i, hit in enumerate(waypoints_hit): waypoints_keyed[i + 1] = hit eval_data['waypoints_satisfied'] = waypoints_keyed # 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_time = 0 for logs in uas_period_logs: out_of_bounds_time += FlyZone.out_of_bounds( self.fly_zones.all(), logs) eval_data['out_of_bounds_time'] = out_of_bounds_time # Determine interop rates. interop_times = eval_data.setdefault('interop_times', {}) server_info_times = ServerInfoAccessLog.rates(user, flight_periods) obstacle_times = ObstacleAccessLog.rates(user, flight_periods) uas_telemetry_times = UasTelemetry.rates( user, flight_periods, time_period_logs=uas_period_logs) interop_times['server_info'] = { 'max': server_info_times[0], 'avg': server_info_times[1] } interop_times['obst_info'] = { 'max': obstacle_times[0], 'avg': obstacle_times[1] } interop_times['uas_telem'] = { 'max': uas_telemetry_times[0], 'avg': uas_telemetry_times[1] } # Determine collisions with stationary and moving obstacles. stationary_collisions = eval_data.setdefault( 'stationary_obst_collision', {}) for obst in self.stationary_obstacles.all(): collision = obst.evaluate_collision_with_uas(uas_logs) stationary_collisions[obst.pk] = collision moving_collisions = eval_data.setdefault('moving_obst_collision', {}) for obst in self.moving_obstacles.all(): collision = obst.evaluate_collision_with_uas(uas_logs) moving_collisions[obst.pk] = collision return results