Files
OpenSquawk/shared/data/atcDecisionTree.ts

586 lines
20 KiB
TypeScript

const atcDecisionTree = {
"schema_version": "1.0",
"name": "icao_atc_decision_tree",
"description": "Machine-readable ICAO/FAA-style ATC flow for IFR, suited for LLM+Whisper+TTS loop.",
"start_state": "CD_CHECK_ATIS",
"end_states": ["FLOW_COMPLETE"],
"variables": {
"callsign": "DLH39A",
"acf_type": "A320",
"dep": "EDDF",
"dest": "EDDM",
"stand": "A12",
"runway": "25R",
"sid": "ANEKI 7S",
"transition": "ANEKI",
"squawk": "1234",
"initial_altitude_ft": 5000,
"climb_altitude_ft": 7000,
"cruise_flight_level": "FL360",
"star": "RNAV X",
"approach_type": "ILS Z",
"taxi_route": "V A",
"missed_approach": "as published",
"delivery_freq": "121.900",
"ground_freq": "121.700",
"tower_freq": "118.700",
"departure_freq": "125.350",
"approach_freq": "120.800",
"handoff_freq": "121.800",
"atis_freq": "118.025",
"atis_code": "K",
"gate": "B24",
"trans_level": "FL070",
"qnh_hpa": 1015,
"push_delay_min": 5,
"surface_wind": "220/05",
"speed_restriction": "210 knots",
"emergency_heading": "180",
"remarks": "standard",
"time_now": "ISO8601"
},
"flags": {
"in_air": false,
"emergency_active": false,
"current_unit": "DEL",
"stack": []
},
"policies": {
"timeouts": {
"pilot_readback_timeout_s": 8,
"controller_ack_timeout_s": 6,
"no_reply_retry_after_s": 5,
"no_reply_max_retries": 2,
"lost_comms_detect_after_s": 90
},
"no_reply_sequence": [
{"after_s": 5, "controller_say_tpl": "{callsign}, confirm last transmission."},
{"after_s": 10, "controller_say_tpl": "{callsign}, do you read?"},
{"after_s": 20, "controller_say_tpl": "{callsign}, if you read, ident."}
],
"interrupts_allowed_when": {
"MAYDAY": "flags.in_air === true",
"PANPAN": "flags.in_air === true",
"TCAS_RA": "flags.in_air === true",
"GO_AROUND": "true",
"LOST_COMMS": "true",
"UNABLE": "true",
"STANDBY": "true"
}
},
"hooks": {
"on_state_enter": true,
"on_state_exit": true,
"on_timeout": true,
"on_interrupt": true,
"on_handoff": true,
"on_readback_check": true,
"on_pilot_speech": true,
"router_note": "Given pilot utterance → detect intent → if valid in active flow, branch; else jump to flow start with matching intent."
},
"roles": ["pilot", "atc", "system"],
"phases": ["Preflight","Clearance","PushStart","TaxiOut","Departure","Climb","Enroute","Descent","Approach","Landing","TaxiIn","Postflight","Interrupt","LostComms","Missed"],
"states": {
"PREFLIGHT_START": {
"role": "pilot",
"phase": "Preflight",
"prompt_out": "Initial contact with Clearance Delivery.",
"next": [{"to": "CD_CHECK_ATIS"}]
},
"CD_CHECK_ATIS": {
"role": "pilot",
"phase": "Clearance",
"utterance_tpl": "{callsign} information {atis_code}, IFR to {dest}, stand {stand}, request clearance.",
"next": [{"to": "CD_ISSUE_CLR"}],
"on_timeout": [{"after_s": 5, "action": "remind", "say_tpl": "{callsign}, say request."}]
},
"CD_ISSUE_CLR": {
"role": "atc",
"phase": "Clearance",
"say_tpl": "{callsign}, cleared to {dest} via {sid} departure, runway {runway}, climb {initial_altitude_ft} feet, squawk {squawk}.",
"readback_required": ["dest","sid","runway","initial_altitude_ft","squawk"],
"next": [
{"to": "CD_VERIFY_READBACK"},
{"to": "CD_AMEND_CLR", "when": "pilot_requests_amendment === true"}
]
},
"CD_VERIFY_READBACK": {
"role": "pilot",
"phase": "Clearance",
"utterance_tpl": "{callsign} cleared {dest} via {sid}, runway {runway}, climb {initial_altitude_ft}, squawk {squawk}.",
"next": [{"to": "CD_READBACK_CHECK"}]
},
"CD_READBACK_CHECK": {
"role": "atc",
"phase": "Clearance",
"auto": "check_readback",
"readback_required": ["dest","sid","runway","initial_altitude_ft","squawk"],
"ok_next": [{"to": "CD_CLR_COMPLETE"}],
"bad_next": [{"to": "CD_READBACK_CORRECT"}],
"on_timeout": [{"after_s": 6, "action": "say", "say_tpl": "{callsign}, read back clearance."}]
},
"CD_READBACK_CORRECT": {
"role": "atc",
"phase": "Clearance",
"say_tpl": "{callsign}, negative; expect {sid}, runway {runway}, climb {initial_altitude_ft}, squawk {squawk}.",
"next": [{"to": "CD_VERIFY_READBACK"}]
},
"CD_AMEND_CLR": {
"role": "atc",
"phase": "Clearance",
"say_tpl": "{callsign}, amended clearance: {sid} {transition} departure, runway {runway}.",
"next": [{"to": "CD_VERIFY_READBACK"}]
},
"CD_CLR_COMPLETE": {
"role": "atc",
"phase": "Clearance",
"say_tpl": "{callsign}, readback correct. Start-up at own discretion. Contact Ground {ground_freq} when ready for push and start.",
"actions": [{"set": "flags.current_unit", "to": "DEL"}],
"handoff": {"to": "GROUND","freq": "{ground_freq}"},
"next": [{"to": "GRD_READY_FOR_PUSH"}]
},
"GRD_READY_FOR_PUSH": {
"role": "pilot",
"phase": "PushStart",
"utterance_tpl": "{callsign} stand {stand}, ready for push and start.",
"next": [
{"to": "GRD_PUSH_APPROVE", "when": "push_available === true"},
{"to": "GRD_PUSH_WAIT", "when": "push_available === false"}
]
},
"GRD_PUSH_WAIT": {
"role": "atc",
"phase": "PushStart",
"say_tpl": "{callsign}, push and start approved in {push_delay_min} minutes. Expect taxi via {taxi_route}.",
"timer_next": [{"after_s": 60, "to": "GRD_PUSH_APPROVE"}],
"next": [{"to": "GRD_PUSH_APPROVE"}]
},
"GRD_PUSH_APPROVE": {
"role": "atc",
"phase": "PushStart",
"say_tpl": "{callsign}, push and start approved, facing {runway}. QNH {qnh_hpa}.",
"next": [{"to": "GRD_TAXI_REQUEST"}]
},
"GRD_TAXI_REQUEST": {
"role": "pilot",
"phase": "TaxiOut",
"utterance_tpl": "{callsign}, request taxi.",
"next": [{"to": "GRD_TAXI_INSTR"}]
},
"GRD_TAXI_INSTR": {
"role": "atc",
"phase": "TaxiOut",
"say_tpl": "{callsign}, taxi to runway {runway} via {taxi_route}, hold short runway {runway}.",
"readback_required": ["runway","taxi_route","hold_short"],
"next": [{"to": "GRD_TAXI_READBACK"}]
},
"GRD_TAXI_READBACK": {
"role": "pilot",
"phase": "TaxiOut",
"utterance_tpl": "{callsign} taxi to {runway} via {taxi_route}, holding short {runway}.",
"next": [{"to": "GRD_TAXI_READBACK_CHECK"}]
},
"GRD_TAXI_READBACK_CHECK": {
"role": "atc",
"phase": "TaxiOut",
"auto": "check_readback",
"readback_required": ["runway","taxi_route","hold_short"],
"ok_next": [{"to": "TWR_CONTACT"}],
"bad_next": [{"to": "GRD_TAXI_READBACK_CORRECT"}]
},
"GRD_TAXI_READBACK_CORRECT": {
"role": "atc",
"phase": "TaxiOut",
"say_tpl": "{callsign}, negative; taxi to runway {runway} via {taxi_route}, hold short runway {runway}.",
"next": [{"to": "GRD_TAXI_READBACK"}]
},
"TWR_CONTACT": {
"role": "atc",
"phase": "TaxiOut",
"say_tpl": "{callsign}, contact Tower {tower_freq} when number one.",
"handoff": {"to": "TOWER","freq": "{tower_freq}"},
"next": [{"to": "TWR_LINEUP_REQ"}]
},
"TWR_LINEUP_REQ": {
"role": "pilot",
"phase": "Departure",
"utterance_tpl": "{callsign} holding short {runway}, ready for departure.",
"next": [
{"to": "TWR_LINEUP", "when": "runway_occupied === true"},
{"to": "TWR_TAKEOFF_CLR", "when": "runway_occupied === false"}
]
},
"TWR_LINEUP": {
"role": "atc",
"phase": "Departure",
"say_tpl": "{callsign}, line up and wait runway {runway}.",
"next": [{"to": "TWR_TAKEOFF_CLR"}]
},
"TWR_TAKEOFF_CLR": {
"role": "atc",
"phase": "Departure",
"say_tpl": "{callsign}, wind {surface_wind}, runway {runway} cleared for take-off.",
"readback_required": ["runway","cleared_takeoff"],
"actions": [{"set": "flags.in_air", "to": true}],
"next": [{"to": "TWR_TAKEOFF_READBACK"}]
},
"TWR_TAKEOFF_READBACK": {
"role": "pilot",
"phase": "Departure",
"utterance_tpl": "{callsign} cleared for take-off {runway}.",
"next": [{"to": "TWR_TAKEOFF_READBACK_CHECK"}]
},
"TWR_TAKEOFF_READBACK_CHECK": {
"role": "atc",
"phase": "Departure",
"auto": "check_readback",
"readback_required": ["runway","cleared_takeoff"],
"ok_next": [{"to": "DEP_CONTACT"}],
"bad_next": [{"to": "TWR_TAKEOFF_READBACK_CORRECT"}]
},
"TWR_TAKEOFF_READBACK_CORRECT": {
"role": "atc",
"phase": "Departure",
"say_tpl": "{callsign}, negative; runway {runway}, cleared for take-off.",
"next": [{"to": "TWR_TAKEOFF_READBACK"}]
},
"DEP_CONTACT": {
"role": "atc",
"phase": "Departure",
"say_tpl": "{callsign}, contact Departure {departure_freq}.",
"handoff": {"to": "DEPARTURE","freq": "{departure_freq}"},
"actions": [{"set": "flags.current_unit", "to": "DEP"}],
"next": [{"to": "DEP_IDENT"}]
},
"DEP_IDENT": {
"role": "pilot",
"phase": "Climb",
"utterance_tpl": "{callsign} passing {initial_altitude_ft}, on SID {sid}.",
"next": [{"to": "DEP_CLIMB_INSTR"}]
},
"DEP_CLIMB_INSTR": {
"role": "atc",
"phase": "Climb",
"say_tpl": "{callsign}, climb {climb_altitude_ft} feet, proceed direct {transition} if able.",
"next": [
{"to": "DEP_CLIMB_READBACK", "when": "pilot_able === true"},
{"to": "DEP_UNABLE_DIR", "when": "pilot_able === false"}
]
},
"DEP_UNABLE_DIR": {
"role": "pilot",
"phase": "Climb",
"utterance_tpl": "{callsign} unable direct {transition}.",
"next": [{"to": "DEP_ALT_RTE"}]
},
"DEP_ALT_RTE": {
"role": "atc",
"phase": "Climb",
"say_tpl": "{callsign}, continue SID, report passing {climb_altitude_ft}.",
"next": [{"to": "ENR_HANDOFF"}]
},
"DEP_CLIMB_READBACK": {
"role": "pilot",
"phase": "Climb",
"utterance_tpl": "{callsign} climb {climb_altitude_ft}, direct {transition}.",
"next": [{"to": "ENR_HANDOFF"}]
},
"ENR_HANDOFF": {
"role": "atc",
"phase": "Enroute",
"say_tpl": "{callsign}, contact Center {handoff_freq}.",
"handoff": {"to": "CENTER","freq": "{handoff_freq}"},
"actions": [{"set": "flags.current_unit", "to": "CTR"}],
"next": [{"to": "ENR_CRUISE"}]
},
"ENR_CRUISE": {
"role": "pilot",
"phase": "Enroute",
"auto": "monitor",
"next": [{"to": "DES_INITIATE"}]
},
"DES_INITIATE": {
"role": "atc",
"phase": "Descent",
"say_tpl": "{callsign}, descend via {star} {transition}, QNH {qnh_hpa}.",
"next": [{"to": "DES_READBACK"}]
},
"DES_READBACK": {
"role": "pilot",
"phase": "Descent",
"utterance_tpl": "{callsign} descend via {star} {transition}, QNH {qnh_hpa}.",
"next": [{"to": "APP_HANDOFF"}]
},
"APP_HANDOFF": {
"role": "atc",
"phase": "Descent",
"say_tpl": "{callsign}, contact Approach {approach_freq}.",
"handoff": {"to": "APPROACH","freq": "{approach_freq}"},
"actions": [{"set": "flags.current_unit", "to": "APP"}],
"next": [{"to": "APP_VECTORING"}]
},
"APP_VECTORING": {
"role": "atc",
"phase": "Approach",
"say_tpl": "{callsign}, turn left heading 220, descend to {initial_altitude_ft} feet, reduce speed {speed_restriction}.",
"next": [{"to": "APP_CLEARED_APP"}]
},
"APP_CLEARED_APP": {
"role": "atc",
"phase": "Approach",
"say_tpl": "{callsign}, cleared {approach_type} approach runway {runway}, report established.",
"next": [{"to": "APP_ESTABLISHED"}]
},
"APP_ESTABLISHED": {
"role": "pilot",
"phase": "Approach",
"utterance_tpl": "{callsign} established localizer {runway}.",
"next": [{"to": "TWR_LAND_CONTACT"}]
},
"TWR_LAND_CONTACT": {
"role": "atc",
"phase": "Approach",
"say_tpl": "{callsign}, contact Tower {tower_freq}.",
"handoff": {"to": "TOWER","freq": "{tower_freq}"},
"actions": [{"set": "flags.current_unit", "to": "TWR"}],
"next": [{"to": "TWR_LAND_CLEARABLE"}]
},
"TWR_LAND_CLEARABLE": {
"role": "atc",
"phase": "Landing",
"condition": "runway_available === true",
"say_tpl": "{callsign}, wind {surface_wind}, runway {runway} cleared to land.",
"else_say_tpl": "{callsign}, continue approach, expect late landing clearance.",
"next": [
{"to": "TWR_LAND_READBACK", "when": "runway_available === true"},
{"to": "TWR_CONTINUE_APPROACH", "when": "runway_available === false"}
]
},
"TWR_CONTINUE_APPROACH": {
"role": "atc",
"phase": "Landing",
"say_tpl": "{callsign}, continue approach.",
"next": [{"to": "TWR_LAND_CLEARABLE"}]
},
"TWR_LAND_READBACK": {
"role": "pilot",
"phase": "Landing",
"utterance_tpl": "{callsign} cleared to land {runway}.",
"next": [{"to": "TWR_VACATE"}]
},
"TWR_VACATE": {
"role": "atc",
"phase": "Landing",
"say_tpl": "{callsign}, vacate via {taxi_route}, contact Ground {ground_freq}.",
"actions": [{"set": "flags.in_air", "to": false}],
"handoff": {"to": "GROUND","freq": "{ground_freq}"},
"next": [{"to": "GRD_TAXI_IN_REQ"}]
},
"GRD_TAXI_IN_REQ": {
"role": "pilot",
"phase": "TaxiIn",
"utterance_tpl": "{callsign} runway vacated, request taxi to stand.",
"next": [{"to": "GRD_TAXI_INSTR_IN"}]
},
"GRD_TAXI_INSTR_IN": {
"role": "atc",
"phase": "TaxiIn",
"say_tpl": "{callsign}, taxi to stand {gate} via {taxi_route}.",
"readback_required": ["gate","taxi_route"],
"next": [{"to": "GRD_TAXI_IN_READBACK"}]
},
"GRD_TAXI_IN_READBACK": {
"role": "pilot",
"phase": "TaxiIn",
"utterance_tpl": "{callsign} taxi to stand {gate} via {taxi_route}.",
"next": [{"to": "GRD_TAXI_IN_READBACK_CHECK"}]
},
"GRD_TAXI_IN_READBACK_CHECK": {
"role": "atc",
"phase": "TaxiIn",
"auto": "check_readback",
"readback_required": ["gate","taxi_route"],
"ok_next": [{"to": "FLOW_COMPLETE"}],
"bad_next": [{"to": "GRD_TAXI_IN_READBACK_CORRECT"}]
},
"GRD_TAXI_IN_READBACK_CORRECT": {
"role": "atc",
"phase": "TaxiIn",
"say_tpl": "{callsign}, negative; taxi to stand {gate} via {taxi_route}.",
"next": [{"to": "GRD_TAXI_IN_READBACK"}]
},
"FLOW_COMPLETE": {
"role": "system",
"phase": "Postflight",
"auto": "end",
"next": []
},
/* ===== Interrupts (conditioned) ===== */
"INT_MAYDAY": {
"role": "pilot",
"phase": "Interrupt",
"guard": "flags.in_air === true",
"utterance_tpl": "MAYDAY MAYDAY MAYDAY, {callsign}, {problem}, intentions {intent}.",
"priority": "highest",
"actions": [{"set": "flags.emergency_active", "to": true}],
"next": [{"to": "ATC_MAYDAY_VECTOR"}]
},
"ATC_MAYDAY_VECTOR": {
"role": "atc",
"phase": "Interrupt",
"say_tpl": "{callsign}, roger MAYDAY, fly heading {emergency_heading}, climb/descend {initial_altitude_ft}, cleared direct {dest} when able, QNH {qnh_hpa}.",
"next": [{"to": "ATC_MAYDAY_COORD"}]
},
"ATC_MAYDAY_COORD": {
"role": "system",
"phase": "Interrupt",
"actions": ["alert_emergency_services","notify_adjacent_units"],
"next": [{"to": "RESUME_PRIOR_FLOW"}]
},
"INT_PANPAN": {
"role": "pilot",
"phase": "Interrupt",
"guard": "flags.in_air === true",
"utterance_tpl": "PAN PAN PAN, {callsign}, {problem}, request priority.",
"priority": "high",
"actions": [{"set": "flags.emergency_active", "to": true}],
"next": [{"to": "ATC_PAN_ACK"}]
},
"ATC_PAN_ACK": {
"role": "atc",
"phase": "Interrupt",
"say_tpl": "{callsign}, PAN acknowledged, priority granted, expect vectors direct {dest} or nearest suitable.",
"next": [{"to": "RESUME_PRIOR_FLOW"}]
},
"INT_TCAS_RA": {
"role": "pilot",
"phase": "Interrupt",
"guard": "flags.in_air === true",
"utterance_tpl": "{callsign} TCAS RA, deviating.",
"actions": ["suspend_clearances"],
"next": [{"to": "ATC_TCAS_ACK"}]
},
"ATC_TCAS_ACK": {
"role": "atc",
"phase": "Interrupt",
"say_tpl": "{callsign}, roger TCAS RA, report clear of conflict.",
"next": [{"to": "ATC_TCAS_RESUME"}]
},
"ATC_TCAS_RESUME": {
"role": "pilot",
"phase": "Interrupt",
"utterance_tpl": "{callsign} clear of conflict, returning to clearance.",
"actions": [{"set": "flags.emergency_active", "to": false}],
"next": [{"to": "RESUME_PRIOR_FLOW"}]
},
"INT_GOA": {
"role": "pilot",
"phase": "Missed",
"utterance_tpl": "{callsign} going around, {missed_approach}.",
"actions": [{"set": "flags.in_air", "to": true}],
"next": [{"to": "ATC_GOA_INSTR"}]
},
"ATC_GOA_INSTR": {
"role": "atc",
"phase": "Missed",
"say_tpl": "{callsign}, roger go-around, fly published missed approach, climb {initial_altitude_ft}, contact Approach {approach_freq}.",
"handoff": {"to": "APPROACH","freq": "{approach_freq}"},
"next": [{"to": "APP_VECTORING"}]
},
"INT_NORDO": {
"role": "system",
"phase": "LostComms",
"trigger": "no_reply > policies.timeouts.lost_comms_detect_after_s",
"actions": ["lost_comms_procedure"],
"next": [{"to": "ATC_NORDO_ACTION"}]
},
"ATC_NORDO_ACTION": {
"role": "atc",
"phase": "LostComms",
"say_tpl": "(Transmitted blind) {callsign}, if you read, squawk IDENT and continue per last clearance. Expect vectors.",
"next": [{"to": "SYSTEM_NORDO_COORD"}]
},
"SYSTEM_NORDO_COORD": {
"role": "system",
"phase": "LostComms",
"actions": ["notify_adjacent_units","monitor_light_gun","publish_ATIS_note"],
"next": [{"to": "RESUME_PRIOR_FLOW"}]
},
"INT_UNABLE": {
"role": "pilot",
"phase": "Interrupt",
"utterance_tpl": "{callsign} unable {instruction}.",
"next": [{"to": "ATC_ALT_PROPOSAL"}]
},
"ATC_ALT_PROPOSAL": {
"role": "atc",
"phase": "Interrupt",
"say_tpl": "{callsign}, alternative: {alt_instruction}.",
"next": [
{"to": "PILOT_ACCEPT_ALT"},
{"to": "PILOT_REJECT_ALT"}
]
},
"PILOT_ACCEPT_ALT": {
"role": "pilot",
"phase": "Interrupt",
"utterance_tpl": "{callsign} wilco.",
"next": [{"to": "RESUME_PRIOR_FLOW"}]
},
"PILOT_REJECT_ALT": {
"role": "pilot",
"phase": "Interrupt",
"utterance_tpl": "{callsign} negative, request {intent}.",
"next": [{"to": "ATC_ALT_PROPOSAL"}]
},
"INT_STANDBY": {
"role": "pilot",
"phase": "Interrupt",
"utterance_tpl": "{callsign} standby.",
"actions": ["pause_exchange"],
"next": [{"to": "RESUME_PRIOR_FLOW"}]
},
/* ===== Generic glue & router ===== */
"RESUME_PRIOR_FLOW": {
"role": "system",
"phase": "Interrupt",
"auto": "pop_stack_or_route_by_intent",
"actions": [
{"if": "flags.emergency_active === true && flags.in_air === true", "set": "flags.emergency_active", "to": false}
],
"next": []
},
"GEN_NO_REPLY": {
"role": "system",
"phase": "Interrupt",
"trigger": "no_reply",
"policy_ref": "policies.no_reply_sequence",
"escalate_to": "INT_NORDO",
"next": []
}
}
}
export default atcDecisionTree;