def saveChangesAndClose(self): # FPL.CALLSIGN self.set_detail(FPL.CALLSIGN, self.callsign_edit.text().upper()) # FPL.FLIGHT_RULES self.set_detail(FPL.FLIGHT_RULES, self.flightRules_select.currentText()) # FPL.ACFT_TYPE self.set_detail(FPL.ACFT_TYPE, self.aircraftType_edit.getAircraftType()) # FPL.WTC self.set_detail(FPL.WTC, self.wakeTurbCat_select.currentText()) # FPL.ICAO_DEP self.set_detail(FPL.ICAO_DEP, self.depAirportPicker_widget.currentText()) # FPL.ICAO_ARR self.set_detail(FPL.ICAO_ARR, self.arrAirportPicker_widget.currentText()) # FPL.ROUTE self.set_detail(FPL.ROUTE, self.route_edit.getRouteText()) # FPL.CRUISE_ALT self.set_detail(FPL.CRUISE_ALT, self.cruiseAlt_edit.text()) # FPL.TAS self.set_detail(FPL.TAS, (Speed(self.TAS_edit.value()) if self.TAS_enable.isChecked() else None)) # FPL.COMMENTS self.set_detail(FPL.COMMENTS, self.comments_edit.toPlainText())
def new_arrival_GND(self): acft_type = choice(self.parkable_aircraft_types) rwy = rnd_rwy([ rwy for rwy in env.airport_data.allRunways() if rwy.use_for_arrivals ], lambda rwy: rwy.acceptsAcftType(acft_type)) if rwy == None: return None turn_off_lists = l1, l2, l3, l4 = env.airport_data.ground_net.runwayTurnOffs( rwy, minroll=(rwy.length(dthr=True) * 2 / 3)) for lst in turn_off_lists: pop_all( lst, lambda t: not self.groundPositionFullySeparated( env.airport_data.ground_net.nodePosition(t[1]), acft_type)) if all(lst == [] for lst in turn_off_lists): return None else: turn_off_choice = choice(l1) if l1 != [] else (l2 + l3)[0] pos = env.airport_data.ground_net.nodePosition(turn_off_choice[1]) hdg = rwy.orientation() + turn_off_choice[3] params = SoloParams(Status(Status.TAXIING), pos, env.groundStdPressureAlt(pos), hdg, Speed(0)) params.XPDR_code = env.nextSquawkCodeAssignment(XPDR_range_IFR_ARR) pk_request = choice( env.airport_data.ground_net.parkingPositions(acftType=acft_type)) return self.mkAiAcft(acft_type, params, pk_request)
def str2detail(dstr, vstr): try: d = int( dstr ) # CAUTION this assumes there is no str detail key that looks like an int except ValueError: d = dstr if d in [FPL.CALLSIGN, FPL.ACFT_TYPE, FPL.WTC, FPL.ICAO_DEP, FPL.ICAO_ARR, FPL.ICAO_ALT, \ FPL.CRUISE_ALT, FPL.ROUTE, FPL.COMMENTS, FPL.FLIGHT_RULES, assigned_altitude_detail]: # str v = vstr elif d in [FPL.SOULS, assigned_SQ_detail]: # int v = int(vstr) elif d in [FPL.TAS, assigned_speed_detail]: # Speed v = Speed(int(vstr)) elif d == FPL.TIME_OF_DEP: # datetime year, month, day, hour, minute = vstr.split() v = datetime(year=int(year), month=int(month), day=int(day), hour=int(hour), minute=int(minute), tzinfo=timezone.utc) elif d == FPL.EET: # timedelta v = timedelta(seconds=int(vstr)) elif d == assigned_heading_detail: # Heading v = Heading(int(vstr), False) else: raise ValueError('Unknown key for detail conversion: %s' % dstr) return d, v
def speedInstruction(self): zero_cursor = self.radar_contact.IAS() if zero_cursor == None: zero_cursor = some(self.radar_contact.groundSpeed(), speedInstruction_defaultZeroCursor) return Speed( bounded(min_speed_instruction, zero_cursor.kt + self.diff_speed_measured, max_speed_instruction)).rounded(step=10)
def stall_speed(t): cat = acft_cat(t) if cat == 'helos': return None else: fact = stall_speed_factors.get(cat, None) crspd = cruise_speed(t) return Speed(fact * crspd.kt) if fact != None and crspd != None else None
def viewRoute(self): tas = Speed( self.TAS_edit.value()) if self.TAS_enable.isChecked() else None route_to_view = Route(self.route_edit.data_DEP, self.route_edit.data_ARR, self.route_edit.getRouteText()) RouteDialog(route_to_view, speedHint=tas, acftHint=self.aircraftType_edit.getAircraftType(), parent=self).exec()
def updateEET(self): if self.EETfromSpeed_radioButton.isChecked(): self.EET_info.setText( TTF_str(self.route_length, Speed(self.speed_edit.value()))) elif self.EETfromACFT_radioButton.isChecked(): crspd = cruise_speed(self.acftType_select.getAircraftType()) if crspd == None: self.EET_info.setText('(unknown ACFT cruise speed)') else: try: self.EET_info.setText(TTF_str(self.route_length, crspd)) except ValueError: self.EET_info.setText('(speed too low)')
def new_departure_GND(self, goal_point): acft_type = choice(self.parkable_aircraft_types) gn = env.airport_data.ground_net pk = [ p for p in gn.parkingPositions(acftType=acft_type) if self.groundPositionFullySeparated(gn.parkingPosition(p), acft_type) ] if pk == []: return None pkinfo = env.airport_data.ground_net.parkingPosInfo(choice(pk)) params = SoloParams(Status(Status.TAXIING), pkinfo[0], env.groundStdPressureAlt(pkinfo[0]), pkinfo[1], Speed(0)) params.XPDR_code = env.nextSquawkCodeAssignment(XPDR_range_IFR_DEP) return self.mkAiAcft(acft_type, params, (goal_point, None))
def new_departure_TWR(self, goal_point): acft_type = choice( self.parkable_aircraft_types if self.parkable_aircraft_types != [] else self.playable_aircraft_types) rwy = rnd_rwy([ rwy for rwy in env.airport_data.allRunways() if rwy.use_for_departures ], lambda rwy: rwy.acceptsAcftType(acft_type)) if rwy == None: return None hdg = rwy.orientation() + 60 pos = rwy.threshold(dthr=True).moved( hdg.opposite(), .04) # FUTURE use turn-offs backwards when ground net present params = SoloParams(Status(Status.READY, arg=rwy.name), pos, env.groundStdPressureAlt(pos), hdg, Speed(0)) params.XPDR_code = env.nextSquawkCodeAssignment(XPDR_range_IFR_DEP) return self.mkAiAcft(acft_type, params, (goal_point, None))
def acftInitParams(self): if self.ground_status_radioButton.isChecked(): status = Status(Status.TAXIING) elif self.ready_status_radioButton.isChecked(): status = Status(Status.READY, arg=self.depRWY_select.currentText()) else: # airborne status radio button must be ticked status = Status(Status.AIRBORNE) pos = self.spawn_coords hdg = self.spawn_hdg if self.airborne_status_radioButton.isChecked(): ias = cruise_speed(self.createAircraftType_edit.getAircraftType()) alt = StdPressureAlt.fromFL(self.airborneFL_edit.value()) else: # on ground ias = Speed(0) alt = env.groundStdPressureAlt(pos) if self.parked_tickBox.isChecked() and self.closest_PKG != None: pkinf = env.airport_data.ground_net.parkingPosInfo(self.closest_PKG) pos = pkinf[0] hdg = pkinf[1] return SoloParams(status, pos, alt, hdg, ias)
def saveChangesAndClose(self): SharedDetailSheet.saveChangesAndClose(self) ## Assigned stuff # Squawk code if self.assignSquawkCode.isChecked(): self.set_detail(assigned_SQ_detail, self.xpdrCode_select.getSQ()) else: self.set_detail(assigned_SQ_detail, None) # Heading if self.assignHeading.isChecked(): self.set_detail(assigned_heading_detail, Heading(self.assignedHeading_edit.value(), False)) else: self.set_detail(assigned_heading_detail, None) # Altitude/FL if self.assignAltitude.isChecked(): reading = self.assignedAltitude_edit.text() try: # try reformating self.set_detail(assigned_altitude_detail, StdPressureAlt.reformatReading(reading)) except ValueError: self.set_detail(assigned_altitude_detail, reading) else: self.set_detail(assigned_altitude_detail, None) # Speed if self.assignSpeed.isChecked(): self.set_detail(assigned_speed_detail, Speed(self.assignedSpeed_edit.value())) else: self.set_detail(assigned_speed_detail, None) # DONE with details if self.strip.linkedFPL() == None: if self.linkFPL_tickBox.isChecked(): if self.FPL_link_on_save == None: signals.newLinkedFPLrequest.emit(self.strip) else: self.strip.linkFPL(self.FPL_link_on_save) else: # a flight plan is already linked if self.pushToFPL_tickBox.isChecked(): self.strip.pushToFPL() self.accept()
def load_aircraft_db(): ''' loads the dict: ICAO desig -> (category, WTC, cruise speed) where category is either of those used in X-plane, e.g. for parking positions any of tuple elements can use the "unavailable_acft_info_token" to signify unknown info ''' try: with open(aircraft_db_spec_file, encoding='utf8') as f: for line in f: tokens = line.split('#', maxsplit=1)[0].split() if len(tokens) == 4: desig, xplane_cat, wtc, cruise = tokens if xplane_cat == unavailable_acft_info_token: xplane_cat = None if wtc == unavailable_acft_info_token: wtc = None acft_db[desig] = xplane_cat, wtc, (Speed( float(cruise)) if cruise != unavailable_acft_info_token else None) elif tokens != []: print('Error on ACFT spec line: %s' % line.strip()) except FileNotFoundError: print('Aircraft data base file not found: %s' % aircraft_db_spec_file)
from session.config import settings from session.env import env from data.utc import now from data.params import Speed, StdPressureAlt from data.conflict import Conflict # ---------- Constants ---------- snapshot_history_size = 120 # number of radar snapshots snapshot_diff_time = 6 # seconds (how long to look back in history for snapshot diffs) min_taxiing_speed = Speed(5) max_ground_height = 100 # ft # ------------------------------- class Xpdr: all_keys = CODE, IDENT, ALT, CALLSIGN, ACFT, GND, IAS = range(7) class RadarSnapshot: def __init__(self, time_stamp, coords, geom_alt): # Obligatory constructor data self.time_stamp = time_stamp self.coords = coords self.geometric_alt = geom_alt
from session.env import env from data.util import some, bounded from data.strip import rack_detail from data.coords import EarthCoords, RadarCoords, dist_str from data.params import StdPressureAlt, Speed from gui.misc import signals from ext.resources import pixmap_corner_sep # ---------- Constants ---------- altitude_sensitivity = 40 # NM to ft speed_sensitivity = 1 # NM to kt speedInstruction_defaultZeroCursor = Speed(200) min_speed_instruction = 80 max_speed_instruction = 800 taxi_tool_snap_dist = .1 # NM groundnet_pos_taxi_precision = .03 # NM min_taxi_drag = .02 # NM text_label_max_rect = QRect(-100, -40, 200, 80) # ------------------------------- def withMargins(rect, margin): return QRectF(rect.topLeft() - QPointF(margin, margin), rect.bottomRight() + QPointF(margin, margin))
def restrict_speed_under_ceiling(spd, alt, ceiling): if alt.diff(ceiling) <= 0: return Speed(min(spd.kt, 250)) else: return spd
def run(self): ## PREPARING QUERY pos = env.radarPos() qdict = { 'username': settings.MP_social_name, 'lon': pos.lon, 'lat': pos.lat, 'range': some(settings.ORSX_handover_range, settings.radar_range), 'xmlVersion': '1.0', 'contacts': ','.join( acft.identifier for acft in env.radar.contacts()) # should this be all FGMS connections? } if settings.publicised_frequency != None: qdict['frequency'] = str(settings.publicised_frequency) server_response = server_query('getFlightplans', qdict) ## USING RESPONSE if server_response != None: try: ww_root = ElementTree.fromstring(server_response) except ElementTree.ParseError as parse_error: print('Parse error in SX server data: %s' % parse_error) return new_ATCs = [] # ATCs first for ww_atc in ww_root.find('atcsInRange').iter( 'atc'): # NOTE the server sends the full list each time atc = ATC(ww_atc.find('callsign').text) atc.social_name = ww_atc.find('username').text atc.position = EarthCoords(float(ww_atc.find('lat').text), float(ww_atc.find('lon').text)) ww_frq = ww_atc.find('frequency').text try: atc.frequency = CommFrequency(ww_frq) except ValueError: atc.frequency = None new_ATCs.append(atc) self.ATCs_on_last_run = new_ATCs # Then strip data (contact claims and handover) for ww_flightplan in ww_root.iter( 'flightplan' ): # NOTE the server only sends those when something changes ww_header = ww_flightplan.find('header') ww_callsign = ww_header.find('callsign').text ww_owner = ww_header.find('owner').text if ww_owner == None: if ww_callsign in self.current_contact_claims: del self.current_contact_claims[ww_callsign] else: self.current_contact_claims[ww_callsign] = ww_owner if ww_header.find( 'handover' ).text == settings.session_manager.myCallsign( ): # RECEIVE A STRIP! strip = Strip() strip.writeDetail(received_from_detail, ww_owner) strip.writeDetail( assigned_SQ_detail, ck_int(ww_header.find('squawk').text, base=8)) strip.writeDetail(assigned_altitude_detail, ww_header.find('assignedAlt').text) # Ignored from WW header above: <flags>, <assignedRunway>, <assignedRoute>, <status>, <flight> # Ignored from WW data below: <fuelTime>; used with ulterior motive: <pilot> ww_data = ww_flightplan.find('data') # ATC-pie hides a token in <pilot>, wake turb. on its left and callsign to its right # e.g. <pilot>M__ATC-pie__X-FOO</pilot> for M turb. and X-FOO strip callsign # If the token is absent, we know the strip is from OpenRadar hidden_tokens = some(ww_data.find('pilot').text, '').split(ATCpie_hidden_string, maxsplit=1) if len( hidden_tokens ) == 1: # hidden marker NOT present; previous strip editor was OpenRadar strip.writeDetail(FPL.CALLSIGN, ww_callsign) else: # recognise strip edited with ATC-pie strip.writeDetail(FPL.WTC, hidden_tokens[0]) strip.writeDetail(FPL.CALLSIGN, hidden_tokens[1]) strip.writeDetail(FPL.FLIGHT_RULES, ww_data.find('type').text) strip.writeDetail(FPL.ACFT_TYPE, ww_data.find('aircraft').text) strip.writeDetail(FPL.ICAO_DEP, ww_data.find('departure').text) strip.writeDetail(FPL.ICAO_ARR, ww_data.find('destination').text) strip.writeDetail(FPL.ROUTE, ww_data.find('route').text) strip.writeDetail(FPL.CRUISE_ALT, ww_data.find('cruisingAlt').text) spd = ck_int(ww_data.find('trueAirspeed').text) if spd != None: strip.writeDetail(FPL.TAS, Speed(spd)) strip.writeDetail(FPL.COMMENTS, ww_data.find('remarks').text) # Possibly ignored details (OpenRadar confuses FPLs and strips): DEP time, EET, alt. AD, souls [*] signals.receiveStrip.emit(strip) send_update(ww_callsign, strip) # Acknowledge strip
def touch_down_speed(t): stall = stall_speed(t) return Speed(touch_down_speed_factor * stall.kt) if stall != None else None
def take_off_speed(t): stall = stall_speed(t) return Speed(take_off_speed_factor * stall.kt) if stall != None else None
def FPLdetails_from_XMLelement(fpl_elt): fpl_id = None # tbd below details = {} online_comments = [] online_status = None dep_date = dep_time = None arr_date = arr_time = None for elt in fpl_elt: if elt.tag == 'flightplanId': fpl_id = int(elt.text) elif elt.tag == 'status': try: # dict keys truncated below because of possible messy spelling at Lenny64 (e.g. "close" vs. "closed") online_status = { 'f': FPL.FILED, 'o': FPL.OPEN, 'c': FPL.CLOSED }[elt.text[0]] except (KeyError, IndexError): print( 'Please report unrecognised status string from Lenny64: %s' % elt.text) elif elt.tag == 'callsign': details[FPL.CALLSIGN] = elt.text elif elt.tag == 'aircraft': details[FPL.ACFT_TYPE] = elt.text elif elt.tag == 'airportFrom': details[FPL.ICAO_DEP] = elt.text elif elt.tag == 'airportTo': details[FPL.ICAO_ARR] = elt.text elif elt.tag == 'alternateDestination': details[FPL.ICAO_ALT] = elt.text elif elt.tag == 'cruiseAltitude': details[FPL.CRUISE_ALT] = elt.text elif elt.tag == 'trueAirspeed' and elt.text != None: try: details[FPL.TAS] = Speed(int(elt.text)) except ValueError: pass # Ignore unreadable integer, check with Lenny for authorised string formats elif elt.tag == 'soulsOnBoard' and elt.text != None: try: details[FPL.SOULS] = int(elt.text) except ValueError: pass # Ignore unreadable integer, check with Lenny for authorised string formats elif elt.tag == 'dateDeparture': match = lenny64_date_regexp.fullmatch(elt.text) if match: yyyy, mm, dd = int(match.group(1)), int(match.group(2)), int( match.group(3)) dep_date = date(yyyy, mm, dd) elif elt.tag == 'departureTime': match = lenny64_time_regexp.fullmatch(elt.text) if match: hh, mm, ss = int(match.group(1)), int(match.group(2)), int( match.group(3)) dep_time = time(hh, mm, ss) elif elt.tag == 'dateArrival': match = lenny64_date_regexp.fullmatch(elt.text) if match: yyyy, mm, dd = int(match.group(1)), int(match.group(2)), int( match.group(3)) arr_date = date(yyyy, mm, dd) elif elt.tag == 'arrivalTime': match = lenny64_time_regexp.fullmatch(elt.text) if match: hh, mm, ss = int(match.group(1)), int(match.group(2)), int( match.group(3)) arr_time = time(hh, mm, ss) elif elt.tag == 'category': details[FPL.FLIGHT_RULES] = elt.text elif elt.tag == 'waypoints': details[FPL.ROUTE] = elt.text elif elt.tag == 'additionalInformation': found = elt.find(ATCpie_comment_element_tag) # ATC comments if found != None: details[FPL.COMMENTS] = found.text found = elt.find( ATCpie_wakeTurb_element_tag) # wake turbulence category if found != None: details[FPL.WTC] = found.text elif elt.tag == 'comments': for comment_elt in elt.iter('comment'): comment = comment_elt.find('message').text if comment != None and comment != '': user = comment_elt.find('user').text online_comments.append('%s: %s' % (some(user, 'N/A'), comment)) if dep_date != None and dep_time != None: details[FPL.TIME_OF_DEP] = datetime(dep_date.year, dep_date.month, dep_date.day, \ hour=dep_time.hour, minute=dep_time.minute, second=dep_time.second, tzinfo=timezone.utc) if arr_date != None and arr_time != None: # makes no sense without a departure datetime eta = datetime(arr_date.year, arr_date.month, arr_date.day, \ hour=arr_time.hour, minute=arr_time.minute, second=arr_time.second, tzinfo=timezone.utc) details[FPL.EET] = eta - details[FPL.TIME_OF_DEP] return fpl_id, details, online_comments, online_status
def saveRadarSnapshot(self): prev = self.lastSnapshot() # always exists if prev.time_stamp == self.live_update_time: return # No point saving values again: they were not updated since last snapshot # otherwise create a new snapshot snapshot = RadarSnapshot(self.live_update_time, self.liveCoords(), self.liveGeometricAlt()) snapshot.xpdrData = self.live_XPDR_data.copy() if settings.radar_cheat or self.individual_cheat: # We try to compensate, but cannot always win so None values are possible. # Plus: CODE, IDENT and GND have no useful compensation. if Xpdr.ALT not in snapshot.xpdrData: stdpa = StdPressureAlt.fromAMSL(snapshot.geometric_alt, env.QNH()) snapshot.xpdrData[Xpdr.ALT] = StdPressureAlt(stdpa.ft1013()) if Xpdr.CALLSIGN not in snapshot.xpdrData: snapshot.xpdrData[Xpdr.CALLSIGN] = self.identifier if Xpdr.ACFT not in snapshot.xpdrData: snapshot.xpdrData[Xpdr.ACFT] = self.aircraft_type else: # contact is not cheated if settings.SSR_mode_capability == '0': # no SSR so no XPDR data can be snapshot snapshot.xpdrData.clear() else: # SSR on; check against A/C/S capability if settings.SSR_mode_capability == 'A': # radar does not have the capability to pick up altitude if Xpdr.ALT in snapshot.xpdrData: del snapshot.xpdrData[Xpdr.ALT] if settings.SSR_mode_capability != 'S': # radar does not have mode S interrogation capability for k in (Xpdr.CALLSIGN, Xpdr.ACFT, Xpdr.IAS, Xpdr.GND): if k in snapshot.xpdrData: del snapshot.xpdrData[k] # Inferred values if self.frozen: # copy from previous snapshot snapshot.heading = prev.heading snapshot.groundSpeed = prev.groundSpeed snapshot.verticalSpeed = prev.verticalSpeed else: # compute values from change between snapshots # Search history for best snapshot to use for diff diff_seconds = (snapshot.time_stamp - prev.time_stamp).total_seconds() i = 1 # index of currently selected prev while i < len(self.radar_snapshots) and diff_seconds < snapshot_diff_time: i += 1 prev = self.radar_snapshots[-i] diff_seconds = (snapshot.time_stamp - prev.time_stamp).total_seconds() # Fill snapshot diffs if prev.coords != None and snapshot.coords != None: # ground speed snapshot.groundSpeed = Speed(prev.coords.distanceTo(snapshot.coords) * 3600 / diff_seconds) # heading if snapshot.groundSpeed != None and snapshot.groundSpeed.diff(min_taxiing_speed) > 0: # acft moving across the ground try: snapshot.heading = snapshot.coords.headingFrom(prev.coords) except ValueError: snapshot.heading = prev.heading # stopped: keep prev. hdg else: snapshot.heading = prev.heading # vertical speed prev_alt = prev.xpdrData.get(Xpdr.ALT, None) this_alt = snapshot.xpdrData.get(Xpdr.ALT, None) if prev_alt != None and this_alt != None: snapshot.verticalSpeed = (this_alt.diff(prev_alt)) * 60 / diff_seconds # Append snapshot to history self.radar_snapshots.append(snapshot) if len(self.radar_snapshots) > snapshot_history_size: del self.radar_snapshots[0]
def interpret_string(string): tokens = string.split() named_runways = pop_named_runways(tokens) rwy_to_expect = ' '.join(named_runways) ## Callsign recognition ialnum, alnumtk = find_tokens(is_alphanum_token, tokens, 0, False) addressee_tokens = [] for i in range(ialnum + len(alnumtk)): addressee_tokens.append(tokens.pop(0)) ## Instruction list recognised_instructions = [] # Instruction.CANCEL_APP try: try: i = tokens.index('go-around') j = i + 1 except ValueError: i = tokens.index('cancel') # cf. "cancel approach" j = i + 2 except ValueError: pass else: SR_log('RECOGNISED: cancel app', tokens) recognised_instructions.append( Instruction(Instruction.CANCEL_APP, voiceData={})) del tokens[i:j] # Instruction.EXPECT_RWY try: i = tokens.index('expect') except ValueError: pass else: # 'j' below is last index to remove once instr is recognised; runway tokens are already removed j_max = min(len(tokens), i + 3) j = next((j for j in range(i + 1, j_max) if tokens[j] not in ['ils', 'visual', 'approach']), j_max) app = next((b for t, b in [('ils', True), ('visual', False)] if t in tokens[i + 1:j + 1]), None) SR_log('RECOGNISED: rwy/app', rwy_to_expect, app, tokens) recognised_instructions.append( Instruction(Instruction.EXPECT_RWY, arg=rwy_to_expect, voiceData={'app': app})) del tokens[i:j + 1] # Instruction.VECTOR_HDG try: try: i = tokens.index('turn') except ValueError: i = tokens.index('heading') except ValueError: pass else: try: ni, ntk = find_num_tokens(tokens, i + 1) hdg = convert_num_tokens(ntk) except ValueError as err: SR_log('Please report bug with heading instruction: %s' % err, tokens) else: SR_log('RECOGNISED: hdg', hdg, tokens) recognised_instructions.append( Instruction(Instruction.VECTOR_HDG, arg=Heading(hdg, False), voiceData={})) del tokens[i:ni + len(ntk)] # Instruction.VECTOR_ALT try: ifl = tokens.index('flight-level') # "flight level" except ValueError: # try altitude th = hu = None try: ith = tokens.index('thousand') nith, thtk = find_num_tokens(tokens, ith - 1, bwd=True) del tokens[nith:ith + 1] SR_log('Tokens left after "thousand":', tokens) th = convert_num_tokens(thtk) # STYLE catch a fail here? except ValueError: pass try: ihu = tokens.index('hundred') nihu, hutk = find_num_tokens(tokens, ihu - 1, bwd=True) del tokens[nihu:ihu + 1] SR_log('Tokens left after "hundred":', tokens) hu = convert_num_tokens(hutk) # STYLE catch a fail here? except ValueError: pass if th != None or hu != None: # got altitude alt = 1000 * some(th, 0) + 100 * some(hu, 0) SR_log('RECOGNISED: alt', alt) recognised_instructions.append( Instruction(Instruction.VECTOR_ALT, arg=('%d ft' % alt), voiceData={})) else: # got FL try: nifl, fltk = find_num_tokens(tokens, ifl + 1) fl = convert_num_tokens(fltk) except ValueError as err: SR_log('Please report bug with FL instruction: %s' % err, tokens) else: SR_log('RECOGNISED: FL', fl, tokens) recognised_instructions.append( Instruction(Instruction.VECTOR_ALT, arg=('FL%03d' % fl), voiceData={})) del tokens[ifl:nifl + len(fltk)] # Instruction.VECTOR_SPD try: i = tokens.index('speed') except ValueError: pass else: if tokens[i + 1:i + 3] == ['your', 'discretion']: SR_log('RECOGNISED: cancel spd') recognised_instructions.append( Instruction(Instruction.CANCEL_VECTOR_SPD, voiceData={})) del tokens[i:i + 3] else: try: ni, ntk = find_num_tokens(tokens, i + 1) spd = convert_num_tokens(ntk) except ValueError as err: SR_log('Please report bug with speed instruction: %s' % err, tokens) else: SR_log('RECOGNISED: spd', spd, tokens) recognised_instructions.append( Instruction(Instruction.VECTOR_SPD, arg=Speed(spd), voiceData={})) del tokens[i:ni + len(ntk)] # Instruction.SQUAWK try: i = tokens.index('squawk') except ValueError: pass else: sq = [digit_tokens[tokens[k]] for k in range(i + 1, i + 5)] sq_code = 8 * 8 * 8 * sq[0] + 8 * 8 * sq[1] + 8 * sq[2] + sq[3] SR_log('RECOGNISED: sq', sq_code, tokens) recognised_instructions.append( Instruction(Instruction.SQUAWK, arg=sq_code, voiceData={})) del tokens[i:i + 5] # Instruction.HAND_OVER try: i = tokens.index('contact') except ValueError: pass else: try: atc = atc_tokens[tokens[i + 1]] except (KeyError, IndexError) as err: SR_log('Please report bug with h/o instruction: %s' % err, tokens) else: SR_log('RECOGNISED: handover', tokens) recognised_instructions.append( Instruction(Instruction.HAND_OVER, arg=(atc, None), voiceData={})) del tokens[i:i + 2] # Instruction.INTERCEPT_LOC try: iloc = tokens.index('localiser') i, tk = find_tokens('intercept'.__eq__, tokens, iloc - 1, True) except ValueError: pass else: SR_log('RECOGNISED: loc', tokens) recognised_instructions.append( Instruction(Instruction.INTERCEPT_LOC, voiceData={'rwy': rwy_to_expect})) del tokens[i:iloc + 1] # Instruction.CLEARED_APP try: try: iapp = tokens.index( 'approach' ) # WARNING "approach" also appears in CANCEL_APP, EXPECT_RWY and HAND_OVER, but should be removed by now except ValueError: iapp = tokens.index( 'ils' ) # WARNING "ils" also appears in EXPECT_RWY, but should be removed by now i, tk1_ignore = find_tokens('cleared'.__eq__, tokens, iapp - 1, True) except ValueError: pass else: app = next((b for t, b in [('ils', True), ('visual', False)] if t in tokens[i + 1:iapp + 1]), None) SR_log('RECOGNISED: app', app, tokens) recognised_instructions.append( Instruction(Instruction.CLEARED_APP, voiceData={ 'rwy': rwy_to_expect, 'app': app })) del tokens[i:iapp + 1] # Instruction.LINE_UP try: i = tokens.index('wait') except ValueError: pass else: SR_log('RECOGNISED: luw', tokens) recognised_instructions.append( Instruction(Instruction.LINE_UP, voiceData={'rwy': rwy_to_expect})) del tokens[i - 2:i + 1] # Instruction.CLEARED_TKOF try: i = tokens.index('take-off') except ValueError: pass else: SR_log('RECOGNISED: cto', tokens) recognised_instructions.append( Instruction(Instruction.CLEARED_TKOF, voiceData={'rwy': rwy_to_expect})) del tokens[i - 2:i + 1] # Instruction.CLEARED_TO_LAND try: i = tokens.index('land') except ValueError: pass else: SR_log('RECOGNISED: ctl', tokens) recognised_instructions.append( Instruction(Instruction.CLEARED_TO_LAND, voiceData={'rwy': rwy_to_expect})) del tokens[i - 2:i + 1] # Instruction.SAY_INTENTIONS try: i = tokens.index('intentions') except ValueError: pass else: SR_log('RECOGNISED: intentions?', tokens) recognised_instructions.append( Instruction(Instruction.SAY_INTENTIONS, voiceData={})) del tokens[i - 1:i + 1] # Instruction.VECTOR_DCT try: i = tokens.index('proceed') except ValueError: pass else: try: pi, ptk = find_tokens(is_navpoint_token, tokens, i + 1, False) point = convert_navpoint_tokens(ptk) except ValueError as err: SR_log('Please report bug with DCT instruction: %s' % err, tokens) else: SR_log('RECOGNISED: dct', point, tokens) recognised_instructions.append( Instruction(Instruction.VECTOR_DCT, arg=point, voiceData={})) del tokens[i:pi + len(ptk)] # Instruction.HOLD, Instruction.HOLD_POSITION try: i = tokens.index('hold') except ValueError: pass else: if i + 1 < len(tokens) and tokens[i + 1] == 'position': SR_log('RECOGNISED: hold-position', tokens) recognised_instructions.append( Instruction(Instruction.HOLD_POSITION, voiceData={})) del tokens[i:i + 2] else: try: pi, ptk = find_tokens(is_navpoint_token, tokens, i + 1, False) point = convert_navpoint_tokens(ptk) except ValueError as err: SR_log('Please report bug with hold instruction: %s' % err, tokens) else: j = pi + len(ptk) if j < len(tokens) and tokens[j] in ['left', 'right']: turns = tokens[j] == 'right' j += 2 else: turns = True SR_log('RECOGNISED: hold', point, tokens) recognised_instructions.append( Instruction(Instruction.HOLD, arg=(point, turns), voiceData={})) del tokens[i:j] return addressee_tokens, recognised_instructions