예제 #1
0
def rank_origins(origins, destinations):

    p('debug', 'Ranking origins: %s' % origins)

    midpoint_origins = midpoint(origins)
    midpoint_destinations = midpoint(destinations)
    hub_route = Segment(midpoint_origins, midpoint_destinations)
    for origin in origins:

        AC = Segment(midpoint_origins, origin)
        orthogonal_heading = hub_route.get_initial_bearing() + 90
        (a, b) = project_segment(
            abs(orthogonal_heading - AC.get_initial_bearing()),
            AC.get_length())
        projection = midpoint_origins.get_position(orthogonal_heading, a)
        midpoint_to_projection = Segment(midpoint_origins, projection)

        angle = abs(hub_route.get_initial_bearing() -
                    midpoint_to_projection.get_initial_bearing())
        if abs(angle - 90) < 0.1:
            distance = -1 * midpoint_to_projection.get_length()
        else:
            distance = midpoint_to_projection.get_length()
        origin.distance_to_midpoint = distance

    return sorted(origins, key=lambda point: point.distance_to_midpoint)
예제 #2
0
 def arrive(self):
     """Placeholder for the aircraft's arrival."""
     self.position = self.route.waypoints[0]
     self.waypoints_passed.append(self.position)
     p('Deleting waypoint %s from %s due to %s of aircraft %s' %\
       (self.position, self.route, 'arrival', self))
     del self.route.waypoints[0]
예제 #3
0
def handle_lock(event):
    """Upon lock, find a formation (if exists) for current aircraft."""

    aircraft = event.sender
    global allocator

    # Find the formation having self.
    formation = allocator.find_formation(aircraft)

    # If no formation is possible
    if formation is None or not len(formation) > 1:
        p('No formation was possible: %s' % formation)
        return

    # If a formation was found, aircraft is no longer a candidate
    allocator.remove_aircraft(aircraft)

    # Register which hub this formation belongs to
    formation.hub = formation[0].hub

    p('Formation init: %s' % formation)

    # Remove 'enter-lock-area' events for all buddies
    sim.events = filter(
        lambda e: e.label != 'enter-lock-area' or e.sender not in formation,
        sim.events)

    # Prevent buddies from being allocated somewhere else.
    for buddy in formation:
        # Self was already removed
        if buddy is not aircraft:
            allocator.remove_aircraft(buddy)

    global synchronizer
    synchronizer.synchronize(formation)
예제 #4
0
def rank_origins(origins, destinations):
    
    p('debug', 'Ranking origins: %s' % origins)

    midpoint_origins = midpoint(origins)
    midpoint_destinations = midpoint(destinations)
    hub_route = Segment(midpoint_origins, midpoint_destinations)
    for origin in origins:
        
        AC = Segment(midpoint_origins, origin)
        orthogonal_heading = hub_route.get_initial_bearing() + 90    
        (a, b) = project_segment(
            abs(orthogonal_heading - AC.get_initial_bearing()),
            AC.get_length()
        )
        projection = midpoint_origins.get_position(
            orthogonal_heading, a
        )
        midpoint_to_projection = Segment(
            midpoint_origins,
            projection
        )
        
        angle = abs(
            hub_route.get_initial_bearing() - 
            midpoint_to_projection.get_initial_bearing()
        )
        if abs(angle - 90) < 0.1:
            distance = -1 * midpoint_to_projection.get_length()
        else:
            distance = midpoint_to_projection.get_length()
        origin.distance_to_midpoint = distance
        
    return sorted(origins, key = lambda point: point.distance_to_midpoint)
예제 #5
0
def handle_departure(event):
    """Adds the aircraft to the candidate stack and schedules lock event."""

    aircraft = event.sender
    global allocator

    # Register which hub this aircraft will fly to
    aircraft.hub = aircraft.route.waypoints[0]

    #assert aircraft.time_to_waypoint() > config.lock_time

    # If the origin lies within the hub lock area, the aircraft cannot
    # reach cruise before reaching the hub, so instead we ignore it altogether
    # and tell it to fly directly to its destination instead of via the hub.
    if (aircraft.time_to_waypoint() < config.lock_time):

        # Reset the aircraft route
        aircraft.route.waypoints = [aircraft.position, aircraft.destination]
        aircraft.route.init_segments()
        aircraft.controller.calibrate()
        aircraft.is_excluded = True
        p('warning', ('Excluded from flying to the hub: %s' % (aircraft)))
        return

    allocator.add_aircraft(aircraft)
    sim.events.append(
        sim.Event(
            'enter-lock-area',
            aircraft,
            # If aircraft departs from within lock area, set lock time to now
            sim.time + max(aircraft.time_to_waypoint() - config.lock_time, 0)))
예제 #6
0
def handle_lock(event):
    """Upon lock, find a formation (if exists) for current aircraft."""

    aircraft = event.sender
    global allocator

    # Find the formation having self.
    formation = allocator.find_formation(aircraft)

    # If no formation is possible
    if formation is None or not len(formation) > 1:
        p('No formation was possible: %s' % formation)
        return

    # If a formation was found, aircraft is no longer a candidate
    allocator.remove_aircraft(aircraft)

    # Register which hub this formation belongs to
    formation.hub = formation[0].hub

    p('Formation init: %s' % formation)

    # Remove 'enter-lock-area' events for all buddies
    sim.events = filter(lambda e: e.label != 'enter-lock-area' or
                                  e.sender not in formation, sim.events)

    # Prevent buddies from being allocated somewhere else.
    for buddy in formation:
        # Self was already removed
        if buddy is not aircraft:
            allocator.remove_aircraft(buddy)

    global synchronizer
    synchronizer.synchronize(formation)
 def schedule_departure(self):
     p('Scheduling departure of %s at %s. Waypoint: %s' %
       (self.aircraft, self.aircraft.departure_time,
        self.aircraft.route.waypoints[0]))
     self.add_event(
         sim.Event('aircraft-depart', self.aircraft,
                   self.aircraft.departure_time))
예제 #8
0
 def arrive(self):
     """Placeholder for the aircraft's arrival."""
     self.position = self.route.waypoints[0]
     self.waypoints_passed.append(self.position)
     p('Deleting waypoint %s from %s due to %s of aircraft %s' %\
       (self.position, self.route, 'arrival', self))
     del self.route.waypoints[0]
예제 #9
0
 def depart(self):
     """Sets the current position and increments to the first waypoint."""
     self.position = self.route.waypoints[0]
     self.waypoints_passed.append(self.position)
     p('Deleting waypoint %s from %s due to %s  of aircraft %s' %\
       (self.position, self.route, 'departure', self))
     del self.route.waypoints[0]
예제 #10
0
 def depart(self):
     """Sets the current position and increments to the first waypoint."""
     self.position = self.route.waypoints[0]
     self.waypoints_passed.append(self.position)
     p('Deleting waypoint %s from %s due to %s  of aircraft %s' %\
       (self.position, self.route, 'departure', self))
     del self.route.waypoints[0]
예제 #11
0
 def at_waypoint(self):
     """Sets the current position and increments to the next segment."""
     self.position = self.route.waypoints[0]
     self.waypoints_passed.append(self.position)
     p('Deleting waypoint %s from %s due to %s of aircraft %s' %\
       (self.position, self.route, 'waypoint-reach', self))
     del self.route.waypoints[0]
     del self.route.segments[0]
예제 #12
0
 def at_waypoint(self):
     """Sets the current position and increments to the next segment."""
     self.position = self.route.waypoints[0]
     self.waypoints_passed.append(self.position)
     p('Deleting waypoint %s from %s due to %s of aircraft %s' %\
       (self.position, self.route, 'waypoint-reach', self))
     del self.route.waypoints[0]
     del self.route.segments[0]
def w(d, W, model):
    result = W * (1 - math.exp(-d * model['c_T'] / (model['V'] * model['L_D'])))
    p('validate', 'Calculating fuel burned.')
    p('validate', 'Distance: %.2f' % d)
    p('validate', 'Starting weight: %.2f' % W)
    p('validate', 'Result: %d' % result)
    p('validate', '===========================')
    return result
def w(d, W, model):
    result = W * (1 - math.exp(-d * model['c_T'] /
                               (model['V'] * model['L_D'])))
    p('validate', 'Calculating fuel burned.')
    p('validate', 'Distance: %.2f' % d)
    p('validate', 'Starting weight: %.2f' % W)
    p('validate', 'Result: %d' % result)
    p('validate', '===========================')
    return result
예제 #15
0
 def schedule_waypoint(self):
     self.aircraft.waypoint_eta = sim.time + self.aircraft.time_to_waypoint(
     )
     p('Schedule waypoint-arrive. WP: %s. ETA: %d. Aircraft: %s' %
       (self.aircraft.route.waypoints[0], self.aircraft.waypoint_eta,
        self.aircraft))
     self.add_event(
         sim.Event('aircraft-at-waypoint', self.aircraft,
                   self.aircraft.waypoint_eta))
예제 #16
0
 def time_to_waypoint(self):
     """Calculates the time left to fly to the current waypoint."""
     waypoint = self.route.waypoints[0]
     distance = self.position.distance_to(waypoint)
     ttwp = distance / self.speed
     p('Time to waypoint %s (d=%d, curtime=%d) for aircraft %s (v=%s, pos=%s) = %d'
       % ('%s {%d, %d}' % (waypoint, waypoint.lat, waypoint.lon), distance,
          sim.time, self, self.speed, '{%d, %d}' %
          (self.position.lat, self.position.lon), ttwp))
     return ttwp
예제 #17
0
 def schedule_departure(self):
     p('Scheduling departure of %s at %s. Waypoint: %s' % (
         self.aircraft,
         self.aircraft.departure_time,
         self.aircraft.route.waypoints[0]
     ))
     self.add_event(sim.Event(
         'aircraft-depart',
         self.aircraft,
         self.aircraft.departure_time
     ))
예제 #18
0
def handle_waypoint(event):
    aircraft = event.sender
    aircraft.at_waypoint()
    p('Aircraft at waypoint: %s (%s)' % (
        aircraft.position,
        aircraft
    ))
    p('Need to calibrate aircraft %s (%s)' % (
        aircraft, aircraft.route
    ))
    aircraft.controller.calibrate()
예제 #19
0
        def heading_filter(buddy):

            segment = Segment(buddy.hub, buddy.destination)
            buddy_heading = segment.get_initial_bearing()
            phi_obs = abs(leader_heading - buddy_heading)
            p(
                'debug',
                'delta phi observed for %s (phi: %.2f) against %s (phi: %.2f)'
                ': %.2f degrees' %
                (aircraft, leader_heading, buddy, buddy_heading, phi_obs))
            return phi_obs <= (config.phi_max / 2)
예제 #20
0
 def schedule_waypoint(self):
     self.aircraft.waypoint_eta = sim.time + self.aircraft.time_to_waypoint()
     p('Schedule waypoint-arrive. WP: %s. ETA: %d. Aircraft: %s' % (
         self.aircraft.route.waypoints[0],
         self.aircraft.waypoint_eta,
         self.aircraft
     ))
     self.add_event(sim.Event(
         'aircraft-at-waypoint',
         self.aircraft,
         self.aircraft.waypoint_eta
     ))
예제 #21
0
 def update_position(self):
     """Calculates the position of the aircraft according to the simtime"""
     # Assume that the flight has been properly set up with a hub.
     assert len(self.aircraft.route.segments) > 0
     flight_time = sim.time - self.aircraft.departure_time
     distance_flown = flight_time * self.aircraft.speed
     segment = self.aircraft.route.segments[0]
     new_pos = segment.start.get_position(bearing=segment.initial_bearing,
                                          distance=distance_flown)
     p('Updating position of %s from %s to %s (d_flown=%d, flt_time=%d)' %
       ('%s (%s)' % (self.aircraft, self.aircraft.route.segments),
        self.aircraft.position, new_pos, distance_flown, flight_time))
     self.aircraft.position = new_pos
예제 #22
0
        def heading_filter(buddy):

            segment = Segment(buddy.hub, buddy.destination)
            buddy_heading = segment.get_initial_bearing()
            phi_obs = abs(leader_heading - buddy_heading)
            p(
                'debug',
                'delta phi observed for %s (phi: %.2f) against %s (phi: %.2f)'
                ': %.2f degrees' % (
                    aircraft, leader_heading, buddy, buddy_heading, phi_obs
                )
            )
            return phi_obs <= (config.phi_max/2)
예제 #23
0
    def schedule_arrival(self):
        self.aircraft.arrival_time = sim.time + self.aircraft.time_to_waypoint(
        )
        p('Scheduling arrival of aircraft %s at destination %s at time %s' %\
          (
            self.aircraft,
            self.aircraft.destination,
            self.aircraft.arrival_time,
        ))

        self.add_event(
            sim.Event('aircraft-arrive', self.aircraft,
                      self.aircraft.arrival_time))
예제 #24
0
 def time_to_waypoint(self):
     """Calculates the time left to fly to the current waypoint."""
     waypoint = self.route.waypoints[0]
     distance = self.position.distance_to(waypoint)
     ttwp = distance / self.speed
     p('Time to waypoint %s (d=%d, curtime=%d) for aircraft %s (v=%s, pos=%s) = %d' % (
         '%s {%d, %d}' % (waypoint, waypoint.lat, waypoint.lon),
         distance, sim.time, self, self.speed, '{%d, %d}' % (
             self.position.lat,
             self.position.lon
         ), ttwp
     ))
     return ttwp
예제 #25
0
 def schedule_arrival(self):
     self.aircraft.arrival_time = sim.time + self.aircraft.time_to_waypoint()
     p('Scheduling arrival of aircraft %s at destination %s at time %s' %\
       (
         self.aircraft,
         self.aircraft.destination,
         self.aircraft.arrival_time,
     ))
     
     self.add_event(sim.Event(
         'aircraft-arrive',
         self.aircraft,
         self.aircraft.arrival_time
     ))
예제 #26
0
def construct_hub(origins, destinations, Z):

    midpoint_origins = midpoint(origins)
    midpoint_destinations = midpoint(destinations)

    hub_route = Segment(midpoint_origins, midpoint_destinations)
    hub = hub_route.start.get_position(hub_route.get_initial_bearing(),
                                       hub_route.get_length() * Z)

    hub.origins = origins
    hub.destinations = destinations

    p('debug', 'Constructed hub at %s' % (hub))

    return hub
예제 #27
0
def construct_hub(origins, destinations, Z):

    midpoint_origins      = midpoint(origins)
    midpoint_destinations = midpoint(destinations)
    
    hub_route = Segment(midpoint_origins, midpoint_destinations)
    hub = hub_route.start.get_position(
        hub_route.get_initial_bearing(),
        hub_route.get_length() * Z
    )
    
    hub.origins      = origins
    hub.destinations = destinations
    
    p('debug', 'Constructed hub at %s' % (hub))
    
    return hub
예제 #28
0
 def update_position(self):
     """Calculates the position of the aircraft according to the simtime"""
     # Assume that the flight has been properly set up with a hub.
     assert len(self.aircraft.route.segments) > 0
     flight_time    = sim.time - self.aircraft.departure_time
     distance_flown = flight_time * self.aircraft.speed
     segment        = self.aircraft.route.segments[0]
     new_pos        = segment.start.get_position(
         bearing  = segment.initial_bearing,
         distance = distance_flown
     )
     p('Updating position of %s from %s to %s (d_flown=%d, flt_time=%d)' % (
         '%s (%s)' % (self.aircraft, self.aircraft.route.segments),
         self.aircraft.position,
         new_pos,
         distance_flown,
         flight_time
     ))
     self.aircraft.position = new_pos
예제 #29
0
    def distance_to(self, point):
        R = Earth.R
        lat1 = math.radians(self.lat)
        lat2 = math.radians(point.lat)
        lon1 = math.radians(self.lon)
        lon2 = math.radians(point.lon)
        param = \
            math.sin(lat1)*math.sin(lat2)+\
            math.cos(lat1)*math.cos(lat2)*\
            math.cos(lon2-lon1)

        # Reduce precision to avoid 1.00000000000000034734 being out of domain
        param = float('%.6f' % param)

        d = math.acos(param) * R

        p('validate',
          'Distance between %s and %s is %.2f NM' % (self, point, d))

        return d
예제 #30
0
def handle_alive(event):

    global vars

    formation = event.sender

    # We should have a hookoff point for each participant, and it should be
    # the current segment
    for aircraft in formation:
        assert aircraft.hookoff_point
        # The remaining segment should be hookoff-destination
        #debug.print_object(aircraft)
        assert len(aircraft.route.segments) > 0

        # Let the aircraft know in which formation it belongs
        aircraft.formation = formation

        #assert aircraft.route.segments[0].end.coincides(
        #    aircraft.hookoff_point
        #)

    vars["formation_count"] += 1
    vars['formation_aircraft_count'] += len(formation)

    formation_phi = 0
    for aircraft in formation:

        hub_to_dest = Segment(aircraft.hub, aircraft.destination)
        hub_to_hookoff = Segment(aircraft.hub, aircraft.hookoff_point)

        bearing = hub_to_dest.get_initial_bearing()
        formation_bearing = hub_to_hookoff.get_initial_bearing()
        phi_obs = abs(bearing - formation_bearing)
        p('debug', 'Aircraft %s, phi_obs: %.2f' % (aircraft, phi_obs))
        #assert phi_obs <= config.phi_max
        formation_phi += phi_obs
        #print aircraft.route.segments[0].initial_bearing()
    vars['phi_obs_sum'] += formation_phi

    for aircraft in formation:
        vars['Q_sum'] = vars['Q_sum'] + aircraft.Q
예제 #31
0
def handle_alive(event):

    global vars

    formation = event.sender
    
    # We should have a hookoff point for each participant, and it should be
    # the current segment
    for aircraft in formation:
        assert aircraft.hookoff_point
        # The remaining segment should be hookoff-destination
        #debug.print_object(aircraft)
        assert len(aircraft.route.segments) > 0

        # Let the aircraft know in which formation it belongs
        aircraft.formation = formation

        #assert aircraft.route.segments[0].end.coincides(
        #    aircraft.hookoff_point
        #)
    
    vars["formation_count"] += 1
    vars['formation_aircraft_count'] += len(formation)
    
    formation_phi = 0
    for aircraft in formation:
        
        hub_to_dest = Segment(aircraft.hub, aircraft.destination)
        hub_to_hookoff = Segment(aircraft.hub, aircraft.hookoff_point)
        
        bearing = hub_to_dest.get_initial_bearing()
        formation_bearing = hub_to_hookoff.get_initial_bearing()
        phi_obs = abs(bearing - formation_bearing)
        p('debug', 'Aircraft %s, phi_obs: %.2f' % (aircraft, phi_obs))
        #assert phi_obs <= config.phi_max
        formation_phi += phi_obs
        #print aircraft.route.segments[0].initial_bearing()
    vars['phi_obs_sum'] += formation_phi
        
    for aircraft in formation:
        vars['Q_sum']   = vars['Q_sum'] + aircraft.Q
예제 #32
0
    def distance_to(self, point):
        R = Earth.R
        lat1 = math.radians(self.lat)
        lat2 = math.radians(point.lat)
        lon1 = math.radians(self.lon)
        lon2 = math.radians(point.lon)
        param = \
            math.sin(lat1)*math.sin(lat2)+\
            math.cos(lat1)*math.cos(lat2)*\
            math.cos(lon2-lon1)

        # Reduce precision to avoid 1.00000000000000034734 being out of domain
        param = float('%.6f' % param)

        d = math.acos(param)*R
        
        p('validate', 'Distance between %s and %s is %.2f NM' % (
            self, point, d
        ))
        
        return d
예제 #33
0
def handle_departure(event):
    """Adds the aircraft to the candidate stack and schedules lock event."""

    aircraft  = event.sender
    global allocator
    
    # Register which hub this aircraft will fly to
    aircraft.hub = aircraft.route.waypoints[0]
    
    #assert aircraft.time_to_waypoint() > config.lock_time

    # If the origin lies within the hub lock area, the aircraft cannot
    # reach cruise before reaching the hub, so instead we ignore it altogether
    # and tell it to fly directly to its destination instead of via the hub.
    if(aircraft.time_to_waypoint() < config.lock_time):
        
        # Reset the aircraft route
        aircraft.route.waypoints = [
            aircraft.position,
            aircraft.destination
        ]
        aircraft.route.init_segments()
        aircraft.controller.calibrate()
        aircraft.is_excluded = True
        p('warning', (
            'Excluded from flying to the hub: %s' % (
                aircraft
            )
        ))
        return

    allocator.add_aircraft(aircraft)        
    sim.events.append(sim.Event(
        'enter-lock-area',
        aircraft,
        # If aircraft departs from within lock area, set lock time to now
        sim.time + max(aircraft.time_to_waypoint() - config.lock_time, 0)
    ))
예제 #34
0
    def delay_events(self, delay):

        p('Delaying all events for aircraft %s by delay = %s.' %
          (self.aircraft, delay))
        p('Event list before delayal: %s' % self.aircraft.events)

        # Quick an dirty: expose delay for statistics.
        # @todo decouple. Idea: create a separate delay event that sends the
        # amount this aircraft was delayed to anybody who listens?
        # In order to make this quick-and-dirty fix bug free, we can only
        # delay an aircraft once in its lifetime to prevent the variable being
        # overwritten by multiple calls to this methods.
        assert not hasattr(self.aircraft, 'hub_delay')
        self.aircraft.hub_delay = delay

        if hasattr(self.aircraft, 'waypoint_eta'):
            self.aircraft.waypoint_eta = self.aircraft.waypoint_eta + delay
        if hasattr(self.aircraft, 'arrival_time'):
            self.aircraft.arrival_time = self.aircraft.arrival_time + delay
        for event in self.aircraft.events:
            event.time = event.time + delay
        p('Event list after delayal: %s' % self.aircraft.events)
예제 #35
0
    def delay_events(self, delay):
        
        p('Delaying all events for aircraft %s by delay = %s.' % (
            self.aircraft, delay
        ))
        p('Event list before delayal: %s' % self.aircraft.events)

        # Quick an dirty: expose delay for statistics.
        # @todo decouple. Idea: create a separate delay event that sends the
        # amount this aircraft was delayed to anybody who listens?
        # In order to make this quick-and-dirty fix bug free, we can only
        # delay an aircraft once in its lifetime to prevent the variable being
        # overwritten by multiple calls to this methods.
        assert not hasattr(self.aircraft, 'hub_delay')
        self.aircraft.hub_delay = delay

        if hasattr(self.aircraft, 'waypoint_eta'):
            self.aircraft.waypoint_eta = self.aircraft.waypoint_eta + delay
        if hasattr(self.aircraft, 'arrival_time'):
            self.aircraft.arrival_time = self.aircraft.arrival_time + delay
        for event in self.aircraft.events:
            event.time = event.time + delay
        p('Event list after delayal: %s' % self.aircraft.events)
예제 #36
0
def get_via_stdin():
    """Set up the planes list, assume tab-separated columns via stdin.
    Can be piped, example: $ cat data/flights.tsv | ./thesis.py"""

    planes = []

    if len(input_history) == 0:
        for row in csv.reader(sys.stdin, delimiter='\t'):
            input_history.append(row)

    for row in input_history:

        departure_time = int(row[0])
        label = row[1]
        waypoints = row[2].split('-')
        aircraft_type = row[3]

        # If a flight has been calibrated, the formation probability will be
        # given. Otherwise it will be set to None.
        try:
            probability = float(row[4])
            # Disregard row if config tells us to
            if probability < config.min_P:
                p(
                    'warning',
                    'Probability of row does not match criterium %s, row %s' %
                    (config.min_P, row))
                continue
            p('warning', 'Yes! Row %s can be included!' % row)
        except IndexError:
            probability = None

        # Keep track of scheduled departure time for later retrieval
        departure_time_scheduled = departure_time

        # Departure times are randomly distributed
        # In some rare cases (only for early aircraft) the departure time might
        # become negative so we restrict it to being only positive
        departure_time = departure_time +\
            random.uniform(-1 * config.dt, config.dt)
        departure_time = max(0, departure_time)

        p('debug',
          'sched: %d, real: %d' % (departure_time_scheduled, departure_time))

        aircraft = Aircraft(
            label=label,
            route=Route([Waypoint(waypoints[0]),
                         Waypoint(waypoints[1])]),
            departure_time=departure_time,
            aircraft_type=aircraft_type)

        aircraft.departure_time_scheduled = departure_time_scheduled

        # If a flight has been calibrated, the formation probability will be
        # given. Otherwise it will be set to None.
        aircraft.probability = probability

        ## Find a random hub
        #hub = random.choice(config.hubs),

        ## Find the closest hub
        #hub = min(config.hubs, key = lambda x: x.distance_to(aircraft.origin))

        planes.append(aircraft)

    return planes
예제 #37
0
def handle_waypoint(event):
    aircraft = event.sender
    aircraft.at_waypoint()
    p('Aircraft at waypoint: %s (%s)' % (aircraft.position, aircraft))
    p('Need to calibrate aircraft %s (%s)' % (aircraft, aircraft.route))
    aircraft.controller.calibrate()
예제 #38
0
    def allocate(self, aircraft):

        p('debug', 'Starting formation allocation for %s' % aircraft)

        # Do not perform allocation if no hub exists in the flight route.
        if len(aircraft.route.segments) == 0:
            return

        self.formations = []
        intervals = []
        candidates = self.aircraft_queue
        hub = aircraft.route.waypoints[0]

        # This is bad. We don't want to filter anything.
        # @todo: pre-process at a higher level.
        # Only consider other aircraft flying to the same hub
        candidates = filter(lambda a: a.route.waypoints[0] is hub, candidates)

        p('debug', 'Full candidate set: %s' % candidates)

        # Only consider aircraft having a maximum heading difference between
        # the hub and their destination
        segment = Segment(aircraft.hub, aircraft.destination)
        leader_heading = segment.get_initial_bearing()

        def heading_filter(buddy):

            segment = Segment(buddy.hub, buddy.destination)
            buddy_heading = segment.get_initial_bearing()
            phi_obs = abs(leader_heading - buddy_heading)
            p(
                'debug',
                'delta phi observed for %s (phi: %.2f) against %s (phi: %.2f)'
                ': %.2f degrees' %
                (aircraft, leader_heading, buddy, buddy_heading, phi_obs))
            return phi_obs <= (config.phi_max / 2)

        candidates = filter(heading_filter, candidates)

        # Other interesting filters
        if 'same-airline' in config.restrictions:
            airline = aircraft.label[0:2]
            candidates = filter(lambda a: a.label[0:2] == airline, candidates)
        if 'same-aircraft-type' in config.restrictions:
            aircraft_type = aircraft.aircraft_type
            candidates = filter(lambda a: a.aircraft_type == aircraft_type,
                                candidates)

        p('debug', 'Reduced candidate set: %s' % candidates)

        for candidate in candidates:

            # Quick and dirty: recalc position. Instead, pull eta from var.
            candidate.controller.update_position()
            tth = candidate.time_to_waypoint()  # time to hub
            hub_eta = sim.time + tth

            # From the moment the aircraft enters the lock area, the slack
            # decreases linearly to zero upon hub arrival.
            if tth < config.lock_time:
                slack = tth * config.etah_slack / config.lock_time
            else:
                slack = config.etah_slack

            p('Time = %s, Hub (= %s) eta %s for candidate %s' %\
              (sim.time, hub, hub_eta, candidate))
            intervals.append(
                Interval(candidate,
                         int(hub_eta) - slack,
                         int(hub_eta) + slack))

        for interval_group in group(intervals):
            formation = Formation()
            for interval in interval_group:
                formation.append(interval.obj)
            self.formations.append(formation)
예제 #39
0
def render(segments):
    
    # create new figure, axes instances.
    fig = plt.figure()
    ax  = fig.add_axes([0.1,0.1,0.8,0.8], axisbg = '#a5bfdd')
    
    llcrnrlon = config.map_dimensions['lon'][0]
    urcrnrlon = config.map_dimensions['lon'][1]
    
    llcrnrlat = config.map_dimensions['lat'][0]
    urcrnrlat = config.map_dimensions['lat'][1]

    # setup mercator map projection.
    m = Basemap(
        llcrnrlon = llcrnrlon, llcrnrlat = llcrnrlat,
        urcrnrlon = urcrnrlon, urcrnrlat = urcrnrlat,
        rsphere = (6378137.00,6356752.3142),
        resolution = 'c', projection = 'merc',
        #lat_0 = 40.,lon_0 = -20.,lat_ts = 20.
    )
    
    for segment in segments['benchmark']:
        m.drawgreatcircle(segment.start.lon, segment.start.lat,
                          segment.end.lon, segment.end.lat,
                          linewidth = 1, color='#00458A')
        x, y = m(segment.start.lon, segment.start.lat)
        m.plot(x, y, 'bo', ms = 4)
        x, y = m(segment.end.lon, segment.end.lat)
        m.plot(x, y, 'bo', ms = 4)

    for segment in segments['formation']:
        p('geo-debug', 'Start to plot a formation trajectory')
        p('geo-debug', 'Trajectory: (%s, %s) -> (%s, %s)' % (
            segment.start.lon, segment.start.lat,
            segment.end.lon, segment.end.lat
        ))
        m.drawgreatcircle(segment.start.lon, segment.start.lat,
                          segment.end.lon, segment.end.lat,
                          linewidth = 1, color='g')
        x, y = m(segment.start.lon, segment.start.lat)
        m.plot(x, y, 'go', ms = 4)
        x, y = m(segment.end.lon, segment.end.lat)
        m.plot(x, y, 'go', ms = 4)
        p('geo-debug', 'Done with plotting a formation trajectory')
    
    for segment in segments['solo']:
        m.drawgreatcircle(segment.start.lon, segment.start.lat,
                          segment.end.lon, segment.end.lat,
                          linewidth = 1, color='r')
        x, y = m(segment.start.lon, segment.start.lat)
        m.plot(x, y, 'ro', ms = 4)
        x, y = m(segment.end.lon, segment.end.lat)
        m.plot(x, y, 'ro', ms = 4)

    m.drawcoastlines(color='#8f8457')
    m.fillcontinents(color='#f5f0db')
    m.drawcountries(color='#a9a06d')
    m.drawparallels(config.map['parallels'], labels = [1,1,0,1])
    m.drawmeridians(config.map['meridians'], labels = [1,1,0,1])
    
    return plt, ax
예제 #40
0
def handle_arrive(event):
    
    global vars, hubs

    aircraft = event.sender
    
    # Temporarily use one model for everything
    model = copy.deepcopy(config.model)
    
    # Also calculate the direct distance
    segment = Segment(aircraft.origin, aircraft.destination)
    direct = segment.get_length()
    #direct = 3193 #temp overwrite for validation
    p('validate', 'Distance direct for %s is %dNM' % (
        aircraft, direct
    ))
    p('validate', 'Getting the benchmark fuel')
    vars['distance_direct'] += direct
    vars['fuel_direct'] += get_fuel_burned_during_cruise(direct, model)
    p('validate', 'OK, we have the benchmark fuel now')
    
    hub = aircraft.hub
    assert hub in config.hubs
    
    # Sometimes (especially with very low Z and high L) aircraft are excluded
    # from formation flight due to the origin being within the lock area.
    if hasattr(aircraft, 'is_excluded') and aircraft.is_excluded:
        vars['distance_solo'] += direct
        vars['fuel_actual'] += get_fuel_burned_during_cruise(direct, model)
        return

    #key = 'flight_count_%s' % hub
    #if key not in vars:
    #    vars[key] = 0
    #vars[key] = vars[key] + 1

    # Aircraft always fly solo to the hub
    segment = Segment(aircraft.origin, hub)
    origin_to_hub = segment.get_length()
    p('Distance origin_to_hub for %s is %dNM' % (
        aircraft,
        origin_to_hub
    ))
    vars['distance_solo'] += origin_to_hub
    
    # If in formation
    if hasattr(aircraft, 'formation'):
        
        segment = Segment(hub, aircraft.hookoff_point)
        hub_to_hookoff = segment.get_length()
        p('Distance hub_to_hookoff for %s is %dNM' % (
            aircraft,
            hub_to_hookoff
        ))
        
        vars['distance_formation'] += hub_to_hookoff

        segment = Segment(aircraft.hookoff_point, aircraft.destination)
        hookoff_to_destination = segment.get_length()
        p('Distance hookoff_to_destination for %s is %dNM' % (
            aircraft,
            hookoff_to_destination
        ))
        vars['distance_solo'] += hookoff_to_destination

        # Collect all hub delays
        # The calibration aircraft was never delayed
        if hasattr(aircraft, 'hub_delay'):
            vars['hub_delay_sum'] = vars['hub_delay_sum'] +\
                aircraft.hub_delay
            
        if aircraft.incurs_benefits:
            discount = config.alpha
        else:
            discount = 0

        p('validate', 'Discount = %s for %s' % (discount, aircraft))
        
        fuel_formation = formationburn(
            int(origin_to_hub),
            int(hub_to_hookoff),
            int(hookoff_to_destination),
            model = model,
            discount = discount
        )
        
        p('validate', 'Fuel burn formation = %d for %s' % (
            fuel_formation, aircraft
        ))

        vars['fuel_actual'] += fuel_formation
    # If fully solo
    else:

        p('validate', 'Discount = false for %s' % (aircraft))

        segment = Segment(hub, aircraft.destination)
        hub_to_destination = segment.get_length()
        p('Distance hub_to_destination for %s is %dNM' % (
            aircraft,
            hub_to_destination
        ))
        vars['distance_solo'] += hub_to_destination
        
        fuel_solo = get_fuel_burned_during_cruise(
            origin_to_hub + hub_to_destination,
            model = model,
        )

        p('validate', 'Fuel burn solo = %d for %s' % (
            fuel_solo, aircraft
        ))

        vars['fuel_actual'] += fuel_solo
예제 #41
0
 def remove_aircraft(self, aircraft):
     try:
         self.aircraft_queue.remove(aircraft)
     except ValueError:
         p('Could not remove %s from queue because not present' % aircraft)
예제 #42
0
def handle_waypoint(event):
    # Aircraft is no longer a candidate when it arrives at any waypoint
    # @todo Only remove from queue if at hub. But it's unlikely that aircraft
    # reach another waypoint before the hub so this is fine for now.
    p('Aircraft %s at waypoint, no longer formation candidate' % event.sender)
    allocator.remove_aircraft(event.sender)
예제 #43
0
    def allocate(self, aircraft):
        
        p('debug', 'Starting formation allocation for %s' % aircraft)
        
        # Do not perform allocation if no hub exists in the flight route.
        if len(aircraft.route.segments) == 0:
            return

        self.formations = []
        intervals       = []
        candidates      = self.aircraft_queue
        hub             = aircraft.route.waypoints[0]

        # This is bad. We don't want to filter anything. 
        # @todo: pre-process at a higher level.
        # Only consider other aircraft flying to the same hub
        candidates = filter(lambda a: a.route.waypoints[0] is hub, 
                            candidates)
        
        p('debug', 'Full candidate set: %s' % candidates)

        # Only consider aircraft having a maximum heading difference between
        # the hub and their destination
        segment = Segment(aircraft.hub, aircraft.destination)
        leader_heading = segment.get_initial_bearing()

        def heading_filter(buddy):

            segment = Segment(buddy.hub, buddy.destination)
            buddy_heading = segment.get_initial_bearing()
            phi_obs = abs(leader_heading - buddy_heading)
            p(
                'debug',
                'delta phi observed for %s (phi: %.2f) against %s (phi: %.2f)'
                ': %.2f degrees' % (
                    aircraft, leader_heading, buddy, buddy_heading, phi_obs
                )
            )
            return phi_obs <= (config.phi_max/2)

        candidates = filter(heading_filter, candidates)

        # Other interesting filters
        if 'same-airline' in config.restrictions:
            airline = aircraft.label[0:2]
            candidates = filter(lambda a: a.label[0:2] == airline,
                                candidates)
        if 'same-aircraft-type' in config.restrictions:
            aircraft_type = aircraft.aircraft_type
            candidates = filter(lambda a: a.aircraft_type == aircraft_type,
                                candidates)
        
        p('debug', 'Reduced candidate set: %s' % candidates)

        for candidate in candidates:

            # Quick and dirty: recalc position. Instead, pull eta from var.
            candidate.controller.update_position()
            tth = candidate.time_to_waypoint() # time to hub
            hub_eta = sim.time + tth
            
            # From the moment the aircraft enters the lock area, the slack
            # decreases linearly to zero upon hub arrival.
            if tth < config.lock_time:
                slack = tth * config.etah_slack / config.lock_time
            else:
                slack = config.etah_slack

            p('Time = %s, Hub (= %s) eta %s for candidate %s' %\
              (sim.time, hub, hub_eta, candidate))
            intervals.append(Interval(
                candidate,
                int(hub_eta) - slack,
                int(hub_eta) + slack
            ))
            
        for interval_group in group(intervals):
            formation = Formation()
            for interval in interval_group:
                formation.append(interval.obj)
            self.formations.append(formation)
예제 #44
0
vars['formation_size']         = 6
vars['origin_to_hub']          = 242
vars['origin_to_destination']  = 3193
vars['hookoff_to_destination'] = 0

vars['hub_to_hookoff']         = (
    vars['origin_to_destination'] -
    vars['origin_to_hub'] -
    vars['hookoff_to_destination']
)

vars['fuel_benchmark']         = 0
vars['fuel_formation']         = 0

p('validate', 'Getting the benchmark fuel')
vars['fuel_benchmark'] = vars['formation_size'] *\
    get_fuel_burned_during_cruise(vars['origin_to_destination'], model)
p('validate', 'OK, we have the benchmark fuel now')

incurs_benefit = False

for i in range(0, vars['formation_size']):
    
    model = copy.deepcopy(model)

    if incurs_benefit is True:
        discount = .13
    else:
        discount = 0
    
예제 #45
0
vars = {}

vars['formation_size'] = 6
vars['origin_to_hub'] = 242
vars['origin_to_destination'] = 3193
vars['hookoff_to_destination'] = 0

vars['hub_to_hookoff'] = (vars['origin_to_destination'] -
                          vars['origin_to_hub'] -
                          vars['hookoff_to_destination'])

vars['fuel_benchmark'] = 0
vars['fuel_formation'] = 0

p('validate', 'Getting the benchmark fuel')
vars['fuel_benchmark'] = vars['formation_size'] *\
    get_fuel_burned_during_cruise(vars['origin_to_destination'], model)
p('validate', 'OK, we have the benchmark fuel now')

incurs_benefit = False

for i in range(0, vars['formation_size']):

    model = copy.deepcopy(model)

    if incurs_benefit is True:
        discount = .13
    else:
        discount = 0
예제 #46
0
def handle_arrive(event):

    global vars, hubs

    aircraft = event.sender

    # Temporarily use one model for everything
    model = copy.deepcopy(config.model)

    # Also calculate the direct distance
    segment = Segment(aircraft.origin, aircraft.destination)
    direct = segment.get_length()
    #direct = 3193 #temp overwrite for validation
    p('validate', 'Distance direct for %s is %dNM' % (aircraft, direct))
    p('validate', 'Getting the benchmark fuel')
    vars['distance_direct'] += direct
    vars['fuel_direct'] += get_fuel_burned_during_cruise(direct, model)
    p('validate', 'OK, we have the benchmark fuel now')

    hub = aircraft.hub
    assert hub in config.hubs

    # Sometimes (especially with very low Z and high L) aircraft are excluded
    # from formation flight due to the origin being within the lock area.
    if hasattr(aircraft, 'is_excluded') and aircraft.is_excluded:
        vars['distance_solo'] += direct
        vars['fuel_actual'] += get_fuel_burned_during_cruise(direct, model)
        return

    #key = 'flight_count_%s' % hub
    #if key not in vars:
    #    vars[key] = 0
    #vars[key] = vars[key] + 1

    # Aircraft always fly solo to the hub
    segment = Segment(aircraft.origin, hub)
    origin_to_hub = segment.get_length()
    p('Distance origin_to_hub for %s is %dNM' % (aircraft, origin_to_hub))
    vars['distance_solo'] += origin_to_hub

    # If in formation
    if hasattr(aircraft, 'formation'):

        segment = Segment(hub, aircraft.hookoff_point)
        hub_to_hookoff = segment.get_length()
        p('Distance hub_to_hookoff for %s is %dNM' %
          (aircraft, hub_to_hookoff))

        vars['distance_formation'] += hub_to_hookoff

        segment = Segment(aircraft.hookoff_point, aircraft.destination)
        hookoff_to_destination = segment.get_length()
        p('Distance hookoff_to_destination for %s is %dNM' %
          (aircraft, hookoff_to_destination))
        vars['distance_solo'] += hookoff_to_destination

        # Collect all hub delays
        # The calibration aircraft was never delayed
        if hasattr(aircraft, 'hub_delay'):
            vars['hub_delay_sum'] = vars['hub_delay_sum'] +\
                aircraft.hub_delay

        if aircraft.incurs_benefits:
            discount = config.alpha
        else:
            discount = 0

        p('validate', 'Discount = %s for %s' % (discount, aircraft))

        fuel_formation = formationburn(int(origin_to_hub),
                                       int(hub_to_hookoff),
                                       int(hookoff_to_destination),
                                       model=model,
                                       discount=discount)

        p('validate',
          'Fuel burn formation = %d for %s' % (fuel_formation, aircraft))

        vars['fuel_actual'] += fuel_formation
    # If fully solo
    else:

        p('validate', 'Discount = false for %s' % (aircraft))

        segment = Segment(hub, aircraft.destination)
        hub_to_destination = segment.get_length()
        p('Distance hub_to_destination for %s is %dNM' %
          (aircraft, hub_to_destination))
        vars['distance_solo'] += hub_to_destination

        fuel_solo = get_fuel_burned_during_cruise(
            origin_to_hub + hub_to_destination,
            model=model,
        )

        p('validate', 'Fuel burn solo = %d for %s' % (fuel_solo, aircraft))

        vars['fuel_actual'] += fuel_solo
예제 #47
0
 def remove_aircraft(self, aircraft):
     try:
         self.aircraft_queue.remove(aircraft)
     except ValueError:
         p('Could not remove %s from queue because not present' % aircraft)
예제 #48
0
def handle_waypoint(event):
    # Aircraft is no longer a candidate when it arrives at any waypoint
    # @todo Only remove from queue if at hub. But it's unlikely that aircraft
    # reach another waypoint before the hub so this is fine for now.
    p('Aircraft %s at waypoint, no longer formation candidate' % event.sender)
    allocator.remove_aircraft(event.sender)
예제 #49
0
    def calibrate(self):
        """Determines the trunk route and hookoff points"""
        
        # Determine formation trunk route
        destinations = []
        for aircraft in self:
            destinations.append(aircraft.destination)
        arrival_midpoint = midpoint(destinations)
        p('destinations: %s' % destinations)
        p('midpoint = %s' % arrival_midpoint)
        hub_to_midpoint = Segment(aircraft.hub, arrival_midpoint)
    
        # Determine hookoff point for each aircraft, except the last
        for aircraft in self:
            
            hub_to_destination = Segment(aircraft.hub, aircraft.destination)
            
            p('flight %s hub %s to destination: %s' % (
                aircraft,
                '%s{%d, %d}' % (
                    aircraft.hub,
                    aircraft.hub.lat,
                    aircraft.hub.lon
                ),
                aircraft.destination
            ))
            p('flight %s hub %s to midpoint: %s' % (
                aircraft,
                '%s{%d, %d}' % (
                    aircraft.hub,
                    aircraft.hub.lat,
                    aircraft.hub.lon
                ),
                arrival_midpoint
            ))
            
            aircraft.hookoff_point = get_hookoff(
                hub_to_midpoint,
                aircraft.destination,
                config.alpha
            )
            
            hub_to_hookoff = Segment(aircraft.hub, aircraft.hookoff_point)
            
            aircraft.Q = hub_to_hookoff.get_length() /\
                         hub_to_midpoint.get_length()

            p('flight %s, hub %s to hook-off point: %s' % (
                aircraft,
                '%s{%d, %d}' % (
                    aircraft.hub,
                    aircraft.hub.lat,
                    aircraft.hub.lon
                ),
                aircraft.hookoff_point
            ))
    
            aircraft.hookoff_point.name = 'hookoff-%s' % aircraft.hookoff_point
            
        # Place aircraft in order, ascending with Q, to fulfill LIFO condition.
        formation = sorted(self, key = lambda item: item.Q)
    
        # All aircraft at the front of the formation having the same destination
        # should hook off where the previous buddy (having a different
        # destination) hooked off.
    
        # Example: formation AMS-SFO, BRU-SFO, LHR-ATL.
        # AMS-SFO and BRU-SFO should hook off where LHR-ATL hooked off.
        # @todo Let AMS-SFO and BRU-SFO continue together along a new average
        #       formation trajectory (in this case directly to the destination)
    
        # First find the leading set of aircraft having the same destination
        formation.reverse()
        leading_destination = formation[0].destination
        leaders = []
        for aircraft in formation:
            
            # Start with always incurring benefits
            aircraft.incurs_benefits = True
    
            if not aircraft.destination.coincides(leading_destination):
                aircraft.is_leader = False
                continue

            aircraft.is_leader = True
            
            # Only the first leader incurs no benefits at all
            if len(leaders) == 0:
                aircraft.incurs_benefits = False
            
            leaders.append(aircraft)
    
        p('Leaders of formation %s are %s' % (
            formation,
            leaders
        ))
    
        # Then find the buddy just before the set of leading aircraft, if
        # it exists.
        try:
            # The leaders: same hookoff point as last buddy.
            last_buddy = formation[len(leaders)]
            for aircraft in leaders:
                aircraft.Q = last_buddy.Q
                #aircraft.P = last_buddy.P
                aircraft.hookoff_point = last_buddy.hookoff_point
        except IndexError:
            pass
    
        # Change reversed formation back to normal
        formation.reverse()

        for aircraft in formation:
    
            p('Adjusting waypoints of %s. Initial waypoints: %s' % (
                aircraft,
                aircraft.route.waypoints
            ))
            aircraft.route.waypoints = [
                #aircraft.hub,
                aircraft.hookoff_point,
                aircraft.destination]
            aircraft.route.init_segments()
            p('Adjusted waypoints of %s. New waypoints: %s' % (
                aircraft,
                aircraft.route.waypoints
            ))
            p('Need to calibrate aircraft %s (%s) in formation %s' % (
                aircraft, aircraft.route, formation
            ))
            aircraft.controller.calibrate()
예제 #50
0
def get_via_stdin():
    """Set up the planes list, assume tab-separated columns via stdin.
    Can be piped, example: $ cat data/flights.tsv | ./thesis.py"""
    
    planes = []

    if len(input_history) == 0:
        for row in csv.reader(sys.stdin, delimiter = '\t'):
            input_history.append(row)

    for row in input_history:

        departure_time = int(row[0])
        label          = row[1]
        waypoints      = row[2].split('-')
        aircraft_type  = row[3]

        # If a flight has been calibrated, the formation probability will be
        # given. Otherwise it will be set to None.
        try:
            probability = float(row[4])
            # Disregard row if config tells us to
            if probability < config.min_P:
                p('warning', 'Probability of row does not match criterium %s, row %s' % (
                    config.min_P,
                    row
                ))
                continue
            p('warning', 'Yes! Row %s can be included!' % row)
        except IndexError:
            probability = None
            
        # Keep track of scheduled departure time for later retrieval
        departure_time_scheduled = departure_time

        # Departure times are randomly distributed
        # In some rare cases (only for early aircraft) the departure time might
        # become negative so we restrict it to being only positive
        departure_time = departure_time +\
            random.uniform(-1 * config.dt, config.dt)
        departure_time = max(0, departure_time)
        
        p('debug', 'sched: %d, real: %d' % (
            departure_time_scheduled,
            departure_time
        ))

        aircraft = Aircraft(
            label = label,
            route = Route([
                Waypoint(waypoints[0]),
                Waypoint(waypoints[1])
            ]),
            departure_time = departure_time,
            aircraft_type = aircraft_type)

        aircraft.departure_time_scheduled = departure_time_scheduled
        
        # If a flight has been calibrated, the formation probability will be
        # given. Otherwise it will be set to None.
        aircraft.probability = probability

        ## Find a random hub
        #hub = random.choice(config.hubs),

        ## Find the closest hub
        #hub = min(config.hubs, key = lambda x: x.distance_to(aircraft.origin))

        planes.append(aircraft)

    return planes
예제 #51
0
    def synchronize(self, formation):
        
        for aircraft in formation:
            
            # Ensure position is up-to-date for eta calculation later
            aircraft.controller.update_position()
            
            # Ensure all participants have the hub as their active waypoint
            assert aircraft.route.waypoints[0].coincides(formation.hub)

        # Select the aircraft that arrives last at the hub
        last_aircraft = max(formation, key = lambda a: a.time_to_waypoint())
        time_to_hub   = last_aircraft.time_to_waypoint()
        
        p('The last aircraft in formation %s is: %s.' % (
            formation, last_aircraft
        ))
        p('The time to hub for formation %s is %d.' % (
            formation, time_to_hub
        ))

        for aircraft in formation:
            
            if aircraft is last_aircraft:
                continue
            
            # Make sure the position of the aircraft is current
            aircraft.controller.update_position()
            
            # Compute time difference between arrivals
            delay = time_to_hub - aircraft.time_to_waypoint()
            
            p('Original hub arrival time for aircraft %s: %d.' % (
                aircraft, sim.time + aircraft.time_to_waypoint()
            ))
            
            p('Required hub arrival time for aircraft %s: %d.' % (
                aircraft, sim.time + time_to_hub
            ))
            
            p('Required hub arrival delay for aircraft %s: %d.' % (
                aircraft, delay
            ))

            # We should only delay, never expedite
            assert delay >= 0

            # Delay arrival of participant to match required arrival.
            aircraft.controller.delay_events(delay)

        # Add a tiny delay to make sure that all aircraft-at-waypoint
        # events are fired before the formation-alive event.
        formation_alive_time = sim.time + time_to_hub + 0.0001
        p('Set formation-alive time at %d + %d = %d for formation %s' % (
            sim.time,
            time_to_hub,
            formation_alive_time,
            formation
        ))
        sim.events.append(sim.Event(
            'formation-alive',
            formation,
            formation_alive_time
        ))
예제 #52
0
    def calibrate(self):
        """Determines the trunk route and hookoff points"""

        # Determine formation trunk route
        destinations = []
        for aircraft in self:
            destinations.append(aircraft.destination)
        arrival_midpoint = midpoint(destinations)
        p('destinations: %s' % destinations)
        p('midpoint = %s' % arrival_midpoint)
        hub_to_midpoint = Segment(aircraft.hub, arrival_midpoint)

        # Determine hookoff point for each aircraft, except the last
        for aircraft in self:

            hub_to_destination = Segment(aircraft.hub, aircraft.destination)

            p('flight %s hub %s to destination: %s' %
              (aircraft, '%s{%d, %d}' %
               (aircraft.hub, aircraft.hub.lat, aircraft.hub.lon),
               aircraft.destination))
            p('flight %s hub %s to midpoint: %s' %
              (aircraft, '%s{%d, %d}' %
               (aircraft.hub, aircraft.hub.lat, aircraft.hub.lon),
               arrival_midpoint))

            aircraft.hookoff_point = get_hookoff(hub_to_midpoint,
                                                 aircraft.destination,
                                                 config.alpha)

            hub_to_hookoff = Segment(aircraft.hub, aircraft.hookoff_point)

            aircraft.Q = hub_to_hookoff.get_length() /\
                         hub_to_midpoint.get_length()

            p('flight %s, hub %s to hook-off point: %s' %
              (aircraft, '%s{%d, %d}' %
               (aircraft.hub, aircraft.hub.lat, aircraft.hub.lon),
               aircraft.hookoff_point))

            aircraft.hookoff_point.name = 'hookoff-%s' % aircraft.hookoff_point

        # Place aircraft in order, ascending with Q, to fulfill LIFO condition.
        formation = sorted(self, key=lambda item: item.Q)

        # All aircraft at the front of the formation having the same destination
        # should hook off where the previous buddy (having a different
        # destination) hooked off.

        # Example: formation AMS-SFO, BRU-SFO, LHR-ATL.
        # AMS-SFO and BRU-SFO should hook off where LHR-ATL hooked off.
        # @todo Let AMS-SFO and BRU-SFO continue together along a new average
        #       formation trajectory (in this case directly to the destination)

        # First find the leading set of aircraft having the same destination
        formation.reverse()
        leading_destination = formation[0].destination
        leaders = []
        for aircraft in formation:

            # Start with always incurring benefits
            aircraft.incurs_benefits = True

            if not aircraft.destination.coincides(leading_destination):
                aircraft.is_leader = False
                continue

            aircraft.is_leader = True

            # Only the first leader incurs no benefits at all
            if len(leaders) == 0:
                aircraft.incurs_benefits = False

            leaders.append(aircraft)

        p('Leaders of formation %s are %s' % (formation, leaders))

        # Then find the buddy just before the set of leading aircraft, if
        # it exists.
        try:
            # The leaders: same hookoff point as last buddy.
            last_buddy = formation[len(leaders)]
            for aircraft in leaders:
                aircraft.Q = last_buddy.Q
                #aircraft.P = last_buddy.P
                aircraft.hookoff_point = last_buddy.hookoff_point
        except IndexError:
            pass

        # Change reversed formation back to normal
        formation.reverse()

        for aircraft in formation:

            p('Adjusting waypoints of %s. Initial waypoints: %s' %
              (aircraft, aircraft.route.waypoints))
            aircraft.route.waypoints = [
                #aircraft.hub,
                aircraft.hookoff_point,
                aircraft.destination
            ]
            aircraft.route.init_segments()
            p('Adjusted waypoints of %s. New waypoints: %s' %
              (aircraft, aircraft.route.waypoints))
            p('Need to calibrate aircraft %s (%s) in formation %s' %
              (aircraft, aircraft.route, formation))
            aircraft.controller.calibrate()