def strip_mouse_press(strip, event):
    if event.button() == Qt.LeftButton:
        acknowledge_strip(strip)
        selection.selectStrip(strip)
    elif event.button() == Qt.MiddleButton:
        if event.modifiers() & Qt.ShiftModifier:  # STRIP UNLINK  REQUEST
            if strip is selection.strip:
                if selection.fpl != None and selection.acft != None:  # both are linked
                    signals.statusBarMsg.emit(
                        'Ambiguous action. Use SHIFT+MMB on FPL or radar contact to unlink.'
                    )
                elif selection.fpl == None and selection.acft != None:  # XPDR link only
                    strip.linkAircraft(None)
                    signals.stripInfoChanged.emit()
                    selection.selectAircraft(selection.acft)
                elif selection.fpl != None and selection.acft == None:  # FPL link only
                    strip.linkFPL(None)
                    signals.stripInfoChanged.emit()
                    selection.selectFPL(selection.fpl)
        else:  # STRIP LINK REQUEST
            if selection.fpl != None and env.linkedStrip(
                    selection.fpl) == None and strip.linkedFPL() == None:
                strip.linkFPL(selection.fpl)
                signals.stripInfoChanged.emit()
                selection.selectStrip(strip)
            elif selection.acft != None and env.linkedStrip(
                    selection.acft) == None and strip.linkedAircraft() == None:
                strip.linkAircraft(selection.acft)
                if settings.strip_autofill_on_ACFT_link:
                    strip.fillFromXPDR()
                signals.stripInfoChanged.emit()
                selection.selectStrip(strip)
            if strip is selection.strip:
                acknowledge_strip(strip)
def vertical_assignment(acft):
    strip = env.linkedStrip(acft)
    if strip == None:
        return None
    else:
        alt = strip.assignedPressureAlt(env.QNH())
        return None if alt == None else alt.ft1013()
 def updateContents(self):
     self.prepareGeometryChange()
     expand = self.mouse_hovering or self.radar_contact is selection.acft or env.linkedStrip(
         self.radar_contact) != None
     self.paint_border = expand
     self.info_text = infoTextLines(self.radar_contact, not expand)
     self.rectangle = TextBoxItem.txt_rect_3lines if expand else TextBoxItem.txt_rect_2lines
Esempio n. 4
0
def auto_print_strip_reason(fpl):
    '''
	Returns reason to print if strip should be auto-printed from FPL; None otherwise
	'''
    if fpl.status() == FPL.CLOSED or fpl.strip_auto_printed \
    or settings.auto_print_strips_IFR_only and fpl[FPL.FLIGHT_RULES] != 'IFR' \
    or env.airport_data == None or env.linkedStrip(fpl) != None:
        return None
    present_time = now()
    ok_reason = None
    if settings.auto_print_strips_include_DEP:  # check DEP time
        dep = fpl[FPL.TIME_OF_DEP]
        if dep != None and fpl[
                FPL.
                ICAO_DEP] == env.airport_data.navpoint.code:  # we know: fpl.status() != FPL.CLOSED
            if dep - settings.auto_print_strips_anticipation <= present_time <= dep:
                ok_reason = 'departure due ' + rel_datetime_str(dep)
    if ok_reason == None and settings.auto_print_strips_include_ARR:  # check ARR time
        eta = fpl.ETA()
        if eta != None and fpl[
                FPL.ICAO_ARR] == env.airport_data.navpoint.code and fpl.status(
                ) == FPL.OPEN:
            if eta - settings.auto_print_strips_anticipation <= present_time <= eta:
                ok_reason = 'arrival due ' + rel_datetime_str(eta)
    return ok_reason
 def linkAircraft(self, acft):
     if self.strip != None and self.strip.linkedAircraft(
     ) == None and env.linkedStrip(acft) == None:
         self.strip.linkAircraft(acft)
         if settings.strip_autofill_on_ACFT_link:
             self.strip.fillFromXPDR()
         signals.stripInfoChanged.emit()
         self.selectAircraft(acft)
 def selectFPL(self, select):
     self.fpl = select
     self.strip = env.linkedStrip(self.fpl)
     if self.strip == None:
         self.acft = None
     else:
         self.acft = self.strip.linkedAircraft()
     signals.selectionChanged.emit()
Esempio n. 7
0
	def aircraftHasDisappeared(self, acft):
		strip = env.linkedStrip(acft)
		if strip != None:
			strip.linkAircraft(None)
		if selection.acft is acft:
			if strip == None: # was not linked
				selection.deselect()
			else:
				selection.selectStrip(strip)
Esempio n. 8
0
def ACFT_pen_colour(radar_contact):
    if radar_contact.ignored:
        return settings.colour('ACFT_ignored')
    strip = env.linkedStrip(radar_contact)
    if strip != None:  # look for strip position colour (rack colour, overrides range colour)
        rack = strip.lookup(rack_detail)
        if rack != None and rack in settings.rack_colours:
            return settings.rack_colours[rack]
    sq = radar_contact.xpdrCode()
    if sq != None:  # look for a range colour
        try:
            return next(rng for rng in settings.XPDR_assignment_ranges
                        if rng.lo <= sq <= rng.hi and rng.col != None).col
        except StopIteration:
            pass  # no range colour available
    return settings.colour(
        'ACFT_unlinked') if strip == None else settings.colour('ACFT_linked')
def horizontal_path(acft, hdg=True, rte=True, ttf=None, div=False):
    '''
	Returns the anticipated ACFT path, considering headings (if "hdg") and/or routes (if "rte"), until
	route destination is reached, "ttf" is given and flown, or path goes out of both map and radar ranges.
	If "div" is True, the returned list is divided into sections flown in one "route division time".
	NB: Heading assignments override routes if both are considered.
	'''
    speed = acft.groundSpeed()
    if speed != None:
        strip = env.linkedStrip(acft)
        if strip != None:
            pos = acft.coords()
            waypoints = None
            if hdg:
                assHdg = strip.lookup(assigned_heading_detail)
                if assHdg != None:
                    wp_dist = 2 * max(settings.map_range, settings.radar_range
                                      ) if ttf == None else distance_flown(
                                          ttf, speed)
                    waypoints = [pos.moved(assHdg, wp_dist)]
            if waypoints == None and rte:
                route = strip.lookup(parsed_route_detail)
                if route != None:
                    waypoints = [
                        route.waypoint(i).coordinates for i in range(
                            route.currentLegIndex(pos), route.legCount())
                    ]
            if waypoints != None:
                dist_limit = None if ttf == None else distance_flown(
                    ttf, speed)
                div_dist = distance_flown(route_division_ttf, speed)
                if div_dist < min_path_hop_when_not_dividing and dist_limit == None:
                    if div:
                        raise ValueError(
                            'ACFT speed too low to calculate path and count on divisions with no TTF limit'
                        )
                    else:  # set to fall-back value, shortcutting ACFT speed but resulting div's are flattened anyway (not wanted)
                        div_dist = min_path_hop_when_not_dividing
                divisions = horizontal_route_divisions(pos,
                                                       waypoints,
                                                       div_dist,
                                                       limit=dist_limit)
                return divisions if div else flatten(divisions)
    raise NoPath()
Esempio n. 10
0
 def __init__(self, gui, fpl):
     QDialog.__init__(self, gui)
     self.setupUi(self)
     self.installEventFilter(RadioKeyEventFilter(self))
     self.setWindowIcon(QIcon(IconFile.panel_FPLs))
     self.fpl = fpl
     self.updateOnlineStatusBox()
     self.stripLinked_info.setText(
         'yes' if env.linkedStrip(self.fpl) != None else 'no')
     SharedDetailSheet.__init__(self)
     self.viewOnlineComments_button.clicked.connect(self.viewOnlineComments)
     self.openFPL_button.clicked.connect(
         lambda: self.changeFplOnlineStatus(FPL.OPEN))
     self.closeFPL_button.clicked.connect(
         lambda: self.changeFplOnlineStatus(FPL.CLOSED))
     # FUTURE[tabOrder]: remove tab order corrections below? current Qt focus behaviour is currently bad wthout them
     self.setTabOrder(self.cruiseAlt_edit, self.altAirportPicker_widget)
     self.setTabOrder(self.altAirportPicker_widget.focusProxy(),
                      self.flightRules_select)
	def updateItem(self):
		self.occupation_indication = 0
		if self.physical_location == settings.location_code:
			try:
				boxed_strip = env.strips.findStrip(lambda strip: strip.lookup(runway_box_detail) == self.physical_runway_index)
				self.occupation_indication = 1
			except StopIteration: # no strip boxed on this runway
				boxed_strip = None
			if settings.monitor_runway_occupation:
				occ = env.radar.runwayOccupation(self.physical_runway_index)
				if len(occ) >= 2 or len(occ) == 1 and boxed_strip == None:
					self.occupation_indication = 2
				elif len(occ) == 1: # one traffic on RWY and runway is reserved
					acft = occ[0]
					boxed_link = boxed_strip.linkedAircraft() # NB: can be None
					if not (boxed_link == None and env.linkedStrip(acft) == None or boxed_link is acft):
						self.occupation_indication = 2
		self.setZValue(self.occupation_indication)
		self.update(self.rect)
Esempio n. 12
0
	def mousePressEvent(self, event):
		QTableView.mousePressEvent(self, event)
		try:
			proxy_index = self.selectedIndexes()[0]
		except IndexError:
			return
		fpl = env.FPLs.FPL(self.model().mapToSource(proxy_index).row())
		if event.button() == Qt.MiddleButton:
			if event.modifiers() & Qt.ShiftModifier: # Trying to unlink
				if selection.strip != None and selection.strip.linkedFPL() is fpl:
					selection.strip.linkFPL(None)
					selection.selectStrip(selection.strip)
				else:
					signals.selectionChanged.emit() # revert selection
			else: # Trying to link
				if selection.strip != None and selection.strip.linkedFPL() == None and env.linkedStrip(fpl) == None:
					selection.strip.linkFPL(fpl)
					selection.selectFPL(fpl)
				else:
					signals.selectionChanged.emit() # revert selection
		else:
			selection.selectFPL(fpl)
 def updateContacts(self):
     for item in self.layerItems(Layer.AIRCRAFT):
         item.updateItem()
         item.setVisible(self.show_GND_modes
                         or not item.radar_contact.xpdrGND()
                         or env.linkedStrip(item.radar_contact) != None)
Esempio n. 14
0
def strip_sheet_FPL_match(callsign_filter, fpl):
    return callsign_filter.upper() in some(fpl[FPL.CALLSIGN], '').upper() \
      and env.linkedStrip(fpl) == None and fpl.flightIsInTimeWindow(timedelta(hours=12))
 def updateButtons(self):
     fpl = selection.fpl
     self.revert_button.setEnabled(fpl != None and fpl.existsOnline()
                                   and fpl.needsUpload())
     self.remove_button.setEnabled(fpl != None and not fpl.existsOnline()
                                   and env.linkedStrip(fpl) == None)
def infoTextLines(radar_contact, compact_display):
    strip = env.linkedStrip(radar_contact)

    # FL/ALT. & SPEED LINE
    ass_alt = None if strip == None else strip.lookup(assigned_altitude_detail)
    if ass_alt != None:
        try:
            ass_alt = StdPressureAlt.reformatReading(ass_alt, unit=False)
        except ValueError:
            ass_alt = None
    if settings.SSR_mode_capability in '0A':
        fl_speed_line = ''
    else:  # we may hope for altitude values from XPDR
        if radar_contact.xpdrGND():
            fl_speed_line = 'GND '
        else:
            xpdr_alt = radar_contact.xpdrAlt()
            if xpdr_alt == None:
                fl_speed_line = 'alt? '
            else:
                if settings.radar_tag_interpret_XPDR_FL:
                    alt_str = env.readStdAlt(xpdr_alt, unit=False)
                else:
                    alt_str = '%03d' % xpdr_alt.FL()
                vs = radar_contact.verticalSpeed()
                if vs == None:
                    comp_char = '-'
                elif abs(vs) < vertical_speed_sensitivity:
                    comp_char = '='
                elif vs > 0:
                    comp_char = '↗'
                else:
                    comp_char = '↘'
                fl_speed_line = '%s %c ' % (alt_str, comp_char)
    if ass_alt != None:
        fl_speed_line += ass_alt
    fl_speed_line += '  '
    if strip != None:
        fl_speed_line += some(strip.lookup(FPL.WTC, fpl=True), '')
    speed = radar_contact.groundSpeed()
    if speed != None:
        fl_speed_line += '%03d' % speed.kt

    # XPDR CODE || WAYPOINT/DEST LINE
    xpdr_code = radar_contact.xpdrCode()
    emg = False if xpdr_code == None else xpdr_code in XPDR_emergency_codes
    if emg or strip == None:  # Show XPDR code
        sq_wp_line = '' if xpdr_code == None else '%04o' % xpdr_code
        if emg:
            sq_wp_line += '  !!EMG'
    else:
        parsed_route = strip.lookup(parsed_route_detail)
        if parsed_route == None:
            dest = some(strip.lookup(FPL.ICAO_ARR, fpl=True), '')
            try:
                ad = world_navpoint_db.findAirfield(dest)
                sq_wp_line = '%s  %s°' % (ad.code,
                                          radar_contact.coords().headingTo(
                                              ad.coordinates).read())
            except NavpointError:  # not an airport
                sq_wp_line = dest
        else:  # got parsed route
            leg = parsed_route.currentLegIndex(radar_contact.coords())
            if leg == 0 and parsed_route.SID() != None:
                sq_wp_line = 'SID %s' % parsed_route.SID()
            elif leg == parsed_route.legCount() - 1 and parsed_route.STAR(
            ) != None:
                sq_wp_line = 'STAR %s' % parsed_route.STAR()
            else:
                wp = parsed_route.waypoint(leg)
                sq_wp_line = '%s  %s°' % (wp.code,
                                          radar_contact.coords().headingTo(
                                              wp.coordinates).read())

    result_lines = [
        sq_wp_line, fl_speed_line
    ] if settings.radar_tag_FL_at_bottom else [fl_speed_line, sq_wp_line]

    # CALLSIGN & ACFT TYPE LINE (top line, only if NOT compact display)
    if not compact_display:
        line1 = ''
        cs = radar_contact.xpdrCallsign()
        if cs == None and strip != None:
            cs = strip.callsign(fpl=True)
        if settings.session_manager.session_type == SessionType.TEACHER:
            dl = env.cpdlc.currentDataLink(radar_contact.identifier)
        else:
            dl = None if cs == None else env.cpdlc.currentDataLink(cs)
        if dl != None:
            line1 += {
                ConnectionStatus.OK: '⚡ ',
                ConnectionStatus.EXPECTING: '[⚡] ',
                ConnectionStatus.PROBLEM: '!![⚡] '
            }[dl.status()]
        line1 += some(cs, '?')
        if strip != None and strip.lookup(FPL.COMMENTS) != None:
            line1 += '*'
        if radar_contact.xpdrIdent():
            line1 += '  !!ident'
        t = radar_contact.xpdrAcftType()
        if t == None and strip != None:
            t = strip.lookup(FPL.ACFT_TYPE, fpl=True)
        if t != None:
            line1 += '  %s' % t
        result_lines.insert(0, line1)

    return '\n'.join(result_lines)
Esempio n. 17
0
    def scan(self):
        visible_aircraft = {
            a.identifier: a
            for a in settings.session_manager.getAircraft()
            if a.isRadarVisible()
        }

        ## UPDATE AIRCRAFT LIST
        lost_contacts = []
        for got_acft in self.aircraft_list:
            try:
                ignore = visible_aircraft.pop(got_acft.identifier)
                got_acft.saveRadarSnapshot()
                self.blips_invisible[got_acft.identifier] = 0
            except KeyError:
                count = self.blips_invisible[got_acft.identifier]
                if count < settings.invisible_blips_before_contact_lost:
                    self.blips_invisible[got_acft.identifier] = count + 1
                else:
                    lost_contacts.append(got_acft.identifier)
        # Remove lost aircraft
        for acft in pop_all(self.aircraft_list,
                            lambda acft: acft.identifier in lost_contacts):
            strip = env.linkedStrip(acft)
            del self.blips_invisible[acft.identifier]
            self.known_EMG_squawkers.discard(acft.identifier)
            self.lostContact.emit(acft)
            if strip != None:
                signals.controlledContactLost.emit(strip, acft.coords())
        # Add newly visible aircraft
        for new_acft in visible_aircraft.values():
            new_acft.saveRadarSnapshot()
            self.aircraft_list.append(new_acft)
            self.blips_invisible[new_acft.identifier] = 0
            self.newContact.emit(new_acft)

        ## CHECK FOR NEW EMERGENCIY SQUAWKS
        for acft in self.aircraft_list:
            if acft.xpdrCode() in XPDR_emergency_codes:
                if acft.identifier not in self.known_EMG_squawkers:
                    self.known_EMG_squawkers.add(acft.identifier)
                    self.emergencySquawk.emit(acft)
            else:
                self.known_EMG_squawkers.discard(acft.identifier)

        ## CHECK FOR NEW/LOST RADAR IDENTIFICATIONS
        if settings.traffic_identification_assistant:
            found_S_links = []
            found_A_links = []
            for strip in env.strips.listStrips(
                    lambda s: s.linkedAircraft() == None):
                mode_S_found = False
                # Try mode S identification
                if strip.lookup(FPL.CALLSIGN) != None:
                    scs = strip.lookup(FPL.CALLSIGN).upper()
                    if env.strips.count(
                            lambda s: s.lookup(FPL.CALLSIGN) != None and s.
                            lookup(FPL.CALLSIGN).upper() == scs) == 1:
                        candidates = [
                            acft for acft in self.aircraft_list
                            if acft.xpdrCallsign() != None
                            and acft.xpdrCallsign().upper() == scs
                        ]
                        if len(candidates) == 1:
                            found_S_links.append((strip, candidates[0]))
                            mode_S_found = True
                # Try mode A identification
                if not mode_S_found:
                    ssq = strip.lookup(assigned_SQ_detail)
                    if ssq != None and env.strips.count(lambda s: \
                      s.lookup(assigned_SQ_detail) == ssq and s.linkedAircraft() == None) == 1: # only one non-linked strip with this SQ
                        candidates = [acft for acft in self.aircraft_list if not any(a is acft for s, a in found_S_links) \
                         and acft.xpdrCode() == ssq and env.linkedStrip(acft) == None]
                        if len(candidates) == 1:  # only one aircraft matching
                            found_A_links.append((strip, candidates[0]))
            for s, a in pop_all(
                    self.soft_links, lambda sl: not any(sl[0] is s and sl[
                        1] is a for s, a in found_S_links + found_A_links)):
                s.writeDetail(soft_link_detail, None)
            for s, a, m in [(s, a, True) for s, a in found_S_links
                            ] + [(s, a, False) for s, a in found_A_links]:
                if not any(sl[0] is s and sl[1] is a
                           for sl in self.soft_links):  # new found soft link
                    if strip.lookup(
                            received_from_detail
                    ) != None and settings.strip_autolink_on_ident and (
                            m or settings.strip_autolink_include_modeC):
                        s.linkAircraft(a)
                    else:  # strip not automatically linked; notify of a new identification
                        self.soft_links.append((s, a))
                        s.writeDetail(soft_link_detail, a)
                        signals.aircraftIdentification.emit(s, a, m)

        ## UPDATE POSITION/ROUTE WARNINGS
        conflicts = {
            acft.identifier: Conflict.NO_CONFLICT
            for acft in self.aircraft_list
        }
        traffic_for_route_checks = []
        for strip in env.strips.listStrips(
        ):  # check for position conflicts and build list of traffic to check for routes later
            acft = strip.linkedAircraft()
            if acft != None and acft.identifier in conflicts:  # controlled traffic with radar contact
                for other in self.aircraft_list:
                    if other is not acft and position_conflict_test(
                            acft, other
                    ) == Conflict.NEAR_MISS:  # positive separation loss detected
                        conflicts[acft.identifier] = conflicts[
                            other.identifier] = Conflict.NEAR_MISS
                if not bypass_route_conflict_check(strip):
                    traffic_for_route_checks.append(acft)
        if settings.route_conflict_warnings:  # check for route conflicts
            while traffic_for_route_checks != []:  # progressively emptying the list
                acft = traffic_for_route_checks.pop()
                for other in traffic_for_route_checks:
                    c = path_conflict_test(acft, other)
                    conflicts[acft.identifier] = max(
                        conflicts[acft.identifier], c)
                    conflicts[other.identifier] = max(
                        conflicts[other.identifier], c)
        # now update aircraft conflicts and emit signals if any are new
        new_near_miss = new_path_conflict = False
        for contact in self.aircraft_list:
            new_conflict = conflicts[contact.identifier]
            if new_conflict > contact.conflict:
                new_near_miss |= new_conflict == Conflict.NEAR_MISS
                new_path_conflict |= new_conflict in [
                    Conflict.DEPENDS_ON_ALT, Conflict.PATH_CONFLICT
                ]
            contact.conflict = new_conflict
        if new_path_conflict:
            self.pathConflict.emit()
        if new_near_miss:
            self.nearMiss.emit()

        ## UPDATE RUNWAY OCCUPATION
        for phrwy in self.runway_occupation:
            new_occ = []
            if settings.monitor_runway_occupation:
                rwy1, rwy2 = env.airport_data.physicalRunway(phrwy)
                width_metres = env.airport_data.physicalRunwayData(phrwy)[0]
                thr1 = rwy1.threshold().toRadarCoords()
                thr2 = rwy2.threshold().toRadarCoords()
                w = m2NM * width_metres
                for acft in self.aircraft_list:
                    if acft.considerOnGround():
                        if acft.coords().toRadarCoords().isBetween(
                                thr1, thr2, w / 2):  # ACFT is on RWY
                            new_occ.append(acft)
                            if not any(
                                    a is acft
                                    for a in self.runway_occupation[phrwy]
                            ):  # just entered the RWY: check if alarm must sound
                                try:
                                    boxed_link = env.strips.findStrip(
                                        lambda strip: strip.lookup(
                                            runway_box_detail) == phrwy
                                    ).linkedAircraft()
                                except StopIteration:  # no strip boxed on this runway
                                    if rwy1.inUse() or rwy2.inUse(
                                    ):  # entering a non-reserved but active RWY
                                        self.runwayIncursion.emit(phrwy, acft)
                                else:  # RWY is reserved
                                    if boxed_link == None and env.linkedStrip(
                                            acft
                                    ) == None or boxed_link is acft:
                                        # entering ACFT is the one cleared to enter, or can be
                                        if self.runway_occupation[
                                                phrwy] != []:  # some ACFT was/were already on RWY
                                            call_guilty = acft if boxed_link == None else self.runway_occupation[
                                                phrwy][0]
                                            self.runwayIncursion.emit(
                                                phrwy, call_guilty)
                                    else:  # entering ACFT is known to be different from the one cleared to enter
                                        self.runwayIncursion.emit(phrwy, acft)
            self.runway_occupation[phrwy] = new_occ

        # Finished aircraft stuff
        self.last_sweep = now()
        self.blip.emit()
Esempio n. 18
0
    def updateDisplay(self):
        if self.radar_contact == None:
            self.info_area.setEnabled(False)
            return
        else:
            self.info_area.setEnabled(True)

        # AIRCRAFT BOX
        # Heading
        hdg = self.radar_contact.heading()
        self.aircraftHeading_info.setText('?' if hdg == None else hdg.read() +
                                          '°')
        # Alt./FL
        alt = self.radar_contact.xpdrAlt()
        if alt == None:
            self.aircraftAltitude_info.setText(
                'N/A' if settings.SSR_mode_capability in '0A' else '?')
        else:
            alt_str = env.readStdAlt(alt, step=None, unit=True)
            if not alt_str.startswith('FL') and env.QNH(
                    noneSafe=False) == None:
                alt_str += '  !!QNH'
            self.aircraftAltitude_info.setText(alt_str)
        # Ground speed
        groundSpeed = self.radar_contact.groundSpeed()
        if groundSpeed == None:
            self.aircraftGroundSpeed_info.setText('?')
        else:
            self.aircraftGroundSpeed_info.setText(str(groundSpeed))
        # Indicated airspeed speed
        ias = self.radar_contact.IAS()
        if ias == None:
            self.indicatedAirSpeed_info.setText(
                '?' if settings.SSR_mode_capability == 'S' else 'N/A')
        else:
            s = str(ias)
            if self.radar_contact.xpdrIAS() == None:
                s += '  !!estimate'
            self.indicatedAirSpeed_info.setText(s)
        # Vertical speed
        vs = self.radar_contact.verticalSpeed()
        if vs == None:
            self.aircraftVerticalSpeed_info.setText(
                'N/A' if settings.SSR_mode_capability in '0A' else '?')
        else:
            self.aircraftVerticalSpeed_info.setText('%+d ft/min' % vs)

        # ROUTE BOX
        coords = self.radar_contact.coords()
        strip = env.linkedStrip(self.radar_contact)
        route = None if strip == None else strip.lookup(parsed_route_detail)
        self._last_known_route = route
        if route == None:
            self.route_box.setEnabled(False)
        else:
            self.route_box.setEnabled(True)
            i_leg = route.currentLegIndex(coords)
            wpdist = coords.distanceTo(route.waypoint(i_leg).coordinates)
            self.legCount_info.setText('%d of %d' %
                                       (i_leg + 1, route.legCount()))
            self.legSpec_info.setText(route.legStr(i_leg))
            self.waypointAt_info.setText(dist_str(wpdist))
            try:  # TTF
                if groundSpeed == None:
                    raise ValueError('No ground speed info')
                self.waypointTTF_info.setText(TTF_str(wpdist, groundSpeed))
            except ValueError:
                self.waypointTTF_info.setText('?')

        # AIRPORT BOX
        if env.airport_data != None:
            airport_dist = coords.distanceTo(env.radarPos())
            self.airportBearing_info.setText(
                coords.headingTo(env.radarPos()).read())
            self.airportDistance_info.setText(dist_str(airport_dist))
            try:  # TTF
                if groundSpeed == None:
                    raise ValueError('No ground speed info')
                self.airportTTF_info.setText(TTF_str(airport_dist,
                                                     groundSpeed))
            except ValueError:
                self.airportTTF_info.setText('?')