In his “Philosophical Investigations”, Ludwig Wittgenstein said
“The meaning of a word is its use in the language”
- Ludwig Wittgenstein
Here by language Wittgenstein means not the whole language but context-dependent language game. In our case, this is Air Traffic Control (ATC) communications.
We try to represent the meaning of an ATC command using the CCG parser and the specially developed ATC-related lexicon.
This is a simple example:
COMMAND: Speedbird 123 climb flight level three four zero.
PARSE (simplified JSON):
{
"CALLSIGN": {
"AIRCRAFT": "Speedbird",
"INTNUMBER": "123"
},
"ALTITUDECHANGE": {
"ALTITUDECHANGE": "climb",
"FLEVEL": {
"FLEVEL": "flight level",
"FLEVELVALUE": "three four zero"
}
}
}
This JSON contains:
1) All words from original command in the natural order
2) Categories:
- CALLSIGN
- AIRCRAFT
- INTNUMBER
- ALTITUDECHANGE
- FLEVEL
- FLEVELVALUE
3) Functions:
- CALLSIGN ( AIRCRAFT, INTNUMBER )
- ALTITUDECHANGE ( ALTITUDECHANGE, FLEVEL )
- FLEVEL ( FLEVEL, FLEVELVALUE)
Both categories and functions are defined in the lexicon that controls the parsing process. We believe that this combination of the command words, categories and structures represented with nested functions may help understand the command meaning.
Other sections of the document will discuss some more specific interesting cases of the ATC commands.
In this ATC command, the controller warns the pilot about another aircraft (traffic) the pilot should be worried about, and asks the pilot to report when the traffic will be in sight.
COMMAND: SkyWest 3124 traffic twelve o’clock three miles opposite direction altitude indicates nine thousand Advise when you have the traffic in sight
The command contains information about the traffic that may help the pilot in looking for the traffic:
- Direction to the traffic relative to the pilot position: twelve o’clock
- Distance to the traffic: three miles
- Direction of the traffic flow: opposite direction
- Traffic altitude: nine thousand
The problem here is that this information is not standardized in terms of which data and in which order are included in the command.
So we can’t define a single function (lambda function for the lexicon) that can accumulate all data about the traffic in all cases. The arity of the function and order, and types of its arguments are unknown in advance.
To solve the problem in the context of the CCG parser, we define multiple versions of the TRAFFICINFO function, each with a small number of arguments and use it as nested functions.
PARSE (simplified JSON):
{
"CALLSIGN": {
"AIRCRAFT": "SkyWest",
"INTNUMBER": "3124"
},
"TRAFFICINFO": {
"TRAFFICINFO": {
"TRAFFICINFO": {
"TRAFFIC": "traffic",
"CLOCKPOSITION": {
"WORDNUMBER": "twelve",
"CLOCKPOSITION": "oclock"
}
}
},
"DISTANCE": {
"DISTANCE": "three",
"DISTANCE": "miles"
},
"DIRECTION": {
"DIRECTION": "opposite",
"DIRECTION": "direction"
},
"ALTITUDE": {
"ALTITUDE": {
"ALTITUDE": "altitude",
"IS_1": "indicates",
"WORDNUMBER": {
"WORDNUMBER": "nine",
"WORDNUMBER": "thousand"
}
}
}
},
"REPORT": {
"REPORT": "Advise",
"WHEN": "when",
"WHO": "you have",
"the TRAFFICINFO": {
"TRAFFIC": "traffic",
"STATUS": "in sight"
}
}
}
We see here these variants of the TRAFFICINFO function:
- TRAFFICINFO ( TRAFFIC, CLOCKPOSITION )
- TRAFFICINFO ( TRAFFICINFO, DISTANCE )
- TRAFFICINFO ( TRAFFICINFO, DIRECTION )
- TRAFFICINFO ( TRAFFICINFO, ALTITUDE )
- TRAFFICINFO ( TRAFFIC, STATUS )
Here is one another example:
COMMAND: United 341 traffic 10 o’clock 3 miles crossing left to right same altitude Citabria.
PARSE (simplified JSON):
{
"CALLSIGN": {
"AIRCRAFT": "United",
"INTNUMBER": "341"
},
"TRAFFICINFO": {
"TRAFFICINFO": {
"TRAFFICINFO": {
"TRAFFICINFO": {
"TRAFFICINFO": {
"TRAFFICINFO": {
"TRAFFICINFO": {
"TRAFFIC": "traffic",
"CLOCKPOSITION": {
"INTNUMBER": "10",
"CLOCKPOSITION": "oclock"
}
}
},
"DISTANCE": {
"DISTANCE": "3",
"DISTANCE": "miles"
}
}
},
"ONGOINGACTION": {
"ONGOINGACTION": "crossing",
"DIRECTION": "left to right"
},
"ALTITUDE": {
"ALTITUDE": "same",
"ALTITUDE": "altitude"
}
}
},
"AIRCRAFT": "Citabria"
}
}
We see here new variants of the TRAFFICINFO function:
- TRAFFICINFO ( TRAFFICINFO, ONGOINGACTION )
- TRAFFICINFO ( TRAFFICINFO, AIRCRAFT )
Let’s consider this ATC command:
COMMAND: Air France 9 hold at DOVER VOR standard right turns maintain flight level 270 Expect further clearance at 1530Z Contact London Control 132.
This means that the pilot should wait for new instructions (further clearance) over DOVER VOR fix executing standard right turns at flight level 270. If he didn’t get new instructions on or before 15:30 Zulu then he should execute holding exit logic procedure.
The question is when the pilot should contact London Control - just after he gets new instructions or before? Really, this means who is responsible for new instructions issue - current controller or the London Control?
I discussed this question with chatGPT and got this explanation:
You should contact London Control immediately.
1530Z is when you expect further clearance, not when you switch frequency.
But contact instruction doesn’t belong to the HOLD scope:
HOLD is a flight procedure, CONTACT is a control relationship
They live in different semantic layers:
HOLD → aircraft trajectory behavior
CONTACT → air traffic control authority
They should not share scope.
Interesting.
PARSE (simplified JSON):
{
"CALLSIGN": {
"AIRCRAFT": "Air France",
"INTNUMBER": "9"
},
"HOLD": {
"HOLD": {
"HOLD": {
"HOLD": {
"HOLD": {
"HOLD": {
"HOLD": {
"HOLD": "hold",
"AT": "at",
"FIX": {
"WAYPOINT": "DOVER",
"NAVAID": "VOR"
}
}
},
"TURNS": "standard right turns"
}
},
"MAINTAIN": {
"MAINTAIN": {
"MAINTAIN": "maintain",
"FLEVEL": {
"FLEVEL": "flight level",
"FLEVELVALUE": "270"
}
}
}
}
},
"EXPECT": {
"EXPECT": {
"EXPECT": "Expect",
"CLEARANCE": {
"CLEARANCE": "further clearance",
"AT": "at",
"TIMEINFO": "1530Z"
}
}
}
},
"CONTACT": {
"CONTACT": "Contact",
"CONTROLLER": {
"PLACE": "London",
"CONTROLLER": {
"CONTROLLER": "Control",
"FREQUENCY": {
"REALNUMBER": "132.7"
}
}
}
}
}
Functions used for HOLD parsing for this command:
- HOLD ( HOLD, AT, FIX)
- HOLD ( HOLD, TURNS)
- HOLD ( HOLD, MAINTAIN)
- HOLD ( HOLD, EXPECT)
And this is one another example related to the HOLD instruction.
COMMAND: American 105 heavy due traffic proceed direct DIXIE hold north on the Victor zero eight zero radial right turns expect further clearance one seven one zero Zulu Maintain flight level two four zero After holding proceed Victor 16 to Robbinsville then Tango 212 to Yardley then as filed
PARSE (simplified and condensed JSON):
{
"CALLSIGN": {...},
"REASON": {...},
"NAVIGATION": {...},
"HOLD": {
"HOLD": {
"HOLD": {
"HOLD": {
"HOLD": {
"HOLD": {
"HOLD": {
"HOLD": {
"HOLD": "hold",
"POSITION": {
"DIRECTIONMAGNETIC": "north",
"ON": "on",
"the RADIAL": {
"ROUTE": "Victor zero eight zero",
"RADIAL": "radial"
}
}
}
},
"TURNS": "right turns"
}
},
"EXPECT": {
"EXPECT": {
"EXPECT": "expect",
"CLEARANCE": "further clearance",
"TIME": {
"WORDNUMPHONEALPHABET": {
"WORDNUMBER": "one seven one zero",
"PHONETICALPHABET": "Zulu"
}
}
}
}
}
},
"MAINTAIN": {
"MAINTAIN": {
"MAINTAIN": "Maintain",
"FLEVEL": {
"FLEVEL": "flight level",
"FLEVELVALUE": "two four zero"
}
}
}
}
},
"NAVIGATION": {
"AFTER": {
"AFTER": "After",
"HOLD": "holding"
},
"NAVIGATION": {...}
},
"THEN": {...},
"THEN_3": {...}
}
Here for the HOLD parsing, these variants of the HOLD function are used to form a representation of its scope:
- HOLD ( HOLD, POSITION )
- HOLD ( HOLD, TURNS )
- HOLD ( HOLD, EXPECT )
- HOLD ( HOLD, MAINTAIN )
Let’s analyze this command:
UAL789 due to traffic fly heading 300 when able proceed direct MLF rejoin filed route
The question here is the scope of the WHEN condition - is this condition for fly heading 300 or for proceed direct MLF?
Let’s look into parsing results:
PARSE (simplified JSON):
{
"CALLSIGN": "UAL789",
"HEADING": {
"HEADING": {
"REASON": {
"DUE": "due to",
"TRAFFIC": "traffic"
},
"HEADING": {
"HEADING": {
"NAVIGATION": "fly",
"HEADING": {
"HEADING": "heading",
"INTNUMBER": "300"
}
}
}
}
},
"NAVIGATION": {
"WHEN": {
"WHEN": "when",
"MODALITY": "able"
},
"NAVIGATION": {
"NAVIGATION": {
"NAVIGATION": "proceed",
"DIRECTION": {
"DIRECTION": "direct",
"FIX": "MLF"
}
}
}
},
"NAVIGATION": {
"NAVIGATION": "rejoin",
"ROUTE": "filed route"
}
}
Here we see these functions:
- REASON ( DUE, TRAFFIC ) - this function explains why we should change heading (flight course)
- HEADING ( HEADING, INTNUMBER) - new course of flight
- HEADING ( NAVIGATION, HEADING )
- HEADING ( REASON, HEADING) - change course of flight with explanation
- WHEN ( WHEN, MODALITY ) - WHEN condition
- DIRECTION ( DIRECTION, FIX) - direction to a fix
- NAVIGATION ( NAVIGATION, DIRECTION ) - fly to the fix
- NAVIGATION ( WHEN, NAVIGATION ) - fly to the fix when able
- NAVIGATION ( NAVIGATION, ROUTE ) - use route from flight plan to continue flight from the fix MLF
The parser made a decision - the scope of the WHEN condition includes flight to the fix MLF, but not course change.
Why is this correct:
- HEADING is the instruction from a controller that the pilot should execute immediately, just now. This may relate to safety.
In our example, this is traffic - another aircraft that the pilot should separate from. So here the pilot doesn’t have any time to delay until he/she is able.
- If HEADING is irrelevant to the WHEN condition, then NAVIGATION is the only choice. Here the pilot gets instructions on how to return to the previous route.
Now we consider an example with AFTER condition.
The logic in this case is the same as in the case of WHEN condition - look into functions before and after the AFTER condition and make the best choice.
If you are not sure about this choice, then the best decision is just to leave the AFTER/WHEN condition outside of the surrounding functions.
COMMAND: Lufthansa 123 runway 25C cleared for takeoff After departure turn right heading 270
PARSE ( simplified JSON ):
{
"CALLSIGN": {
"AIRCRAFT": "Lufthansa",
"INTNUMBER": "123"
},
"RUNWAY": {
"RUNWAY": "runway",
"RUNWAY": "25C"
},
"CLEARED": {
"CLEARED": "cleared",
"FOR": "for",
"DEPARTURE": "takeoff"
},
"HEADING": {
"AFTER": {
"AFTER": "After",
"DEPARTURE": "departure"
},
"HEADING": {
"HEADING": {
"NAVIGATION": "turn right",
"HEADING": {
"HEADING": "heading",
"INTNUMBER": "270"
}
}
}
}
}
Relevant functions:
- CLEARED ( CLEARED, FOR, DEPARTURE) - a runway is cleared for departure
- AFTER ( AFTER, DEPARTURE ) - AFTER condition - after departure
- HEADING ( HEADING, INTNUMBER) - new course of flight
- HEADING ( NAVIGATION, HEADING )
- HEADING ( AFTER, NAVIGATION ) - turn right to the new course after departure.
Here we see that condition AFTER is included in HEADING instruction.
Previously it was said that WHEN condition can’t be included in HEADING instruction because this instruction should be executed immediately.
But here we have a precise description of the event after which the course should be changed - just after departure.
Sometimes a pilot or a controller should abort landing because, for example bad weather or other traffic. In such cases, a special procedure is initiated, such as go around. The controller may update its parameter setting heading, climb parameters and some others.
The question is what is the scope of the go around instruction? Talking with chatGPT I got these recommendations:
Inside GO_AROUND scope:
>-heading
>-track
>-climb/descent
>-altitude
>-navigation fixes
>-sequencing like “then”, “after”
Outside GO_AROUND scope:
>-contact
>-monitor
>-report
>-squawk
>-ident
It seems that the reason of the landing abort (weather, traffic) may also be included in the scope.
Please see a few examples below:
1) Bad weather, standard procedure
COMMAND: WZZ124 go around weather below minima
PARSE (simplified JSON):
{
"CALLSIGN": "WZZ124",
"APPROACHNOTCOMPLETED": {
"APPROACHNOTCOMPLETED": "go around",
"WEATHER": {
"WEATHER": "weather",
"COMPARISON": "below",
"FEATURE": "minima"
}
}
}
2) Heading and altitude are specified
COMMAND: RYR84X go around fly runway heading climb and maintain two thousand feet
PARSE (simplified JSON):
{
"CALLSIGN": "RYR84X",
"APPROACHNOTCOMPLETED": {
"APPROACHNOTCOMPLETED": "go around",
"HEADING": {
"NAVIGATION": "fly",
"HEADING": "runway heading"
},
"ALTITUDECHANGE": {
"ALTITUDECHANGE": {
"ALTITUDECHANGE": "climb and maintain",
"ALTITUDE": {
"WORDNUMBER": {
"WORDNUMBER": "two",
"WORDNUMBER": "thousand feet"
}
}
}
}
}
}
3) Heading instruction inside, contact instruction outside of the go around scope
COMMAND: THY5KT go around turn left heading two one zero contact approach one one eight decimal six.
PARSE (simplified JSON):
{
"CALLSIGN": "THY5KT",
"APPROACHNOTCOMPLETED": {
"APPROACHNOTCOMPLETED": "go around",
"HEADING": {
"HEADING": {
"NAVIGATION": "turn left",
"HEADING": {
"HEADING": "heading",
"WORDNUMBER": "two one zero"
}
}
}
},
"CONTACT": {
"CONTACT": "contact",
"CONTROLLER": {
"CONTROLLER": "approach",
"FREQUENCY": {
"REALWORDNUM": {
"WORDNUMBER": "one one eight",
"REALWORDNUM": "decimal six"
}
}
}
}
}
Orbit procedure is somehow similar to go around or hold procedures but relates to orbiting around traffic circuits, for example, near downwind traffic circuit.
The goal of this orbiting may be traffic separation, sequencing.
Let’s consider three examples.
1)
COMMAND: Cessna Four Five Two Three orbit left on base traffic not yet clear
PARSE (simplified JSON):
{
"CALLSIGN": {
"AIRCRAFT": "Cessna",
"WORDNUMBER": "Four Five Two Three"
},
"ORBIT": {
"ORBIT": "orbit",
"ORBITDIRECTION": "left",
"POSITION": {
"ON": "on",
"TRAFFICCIRCUIT": "base"
}
},
"TRAFFICINFO": {
"TRAFFIC": "traffic",
"STATUS": "not yet clear"
}
}
Here we see that Cessna 4523 got an instruction to orbit near the base traffic circuit, making 360-degree turn to the left.
TRAFFICINFO section explains the context of the instruction - another traffic may be in a conflict with Cessna 4523, but this is not yet clear.
The pilot should get a new command (before the end of the orbit?), what to do next.
2)
COMMAND: Echo Zulu Mike continue left orbits until further advised
PARSE (simplified JSON):
{
"CALLSIGN": {
"PHONETICALPHABET": {
"PHONETICALPHABET": {
"PHONETICALPHABET": "Echo",
"PHONETICALPHABET": {
"PHONETICALPHABET": "Zulu",
"PHONETICALPHABET": "Mike"
}
}
}
},
"REQUESTINSTRUCTION": {
"REQUESTINSTRUCTION": "continue",
"ORBIT": {
"ORBIT": {
"ORBITDIRECTION": "left",
"ORBIT": "orbits",
"UNTIL": {
"UNTIL": "until",
"ADVISORY": "further advised"
}
}
}
}
}
Here number of orbits is not specified (implicitly, as in the previous example, or explicitly, as in “make two orbits”). Instead, the scope of the orbit contains the condition UNTIL.
3) And finally, the scope of the orbit instruction may contain other parameters, for example, altitude restriction.
COMMAND: Echo India Romeo make left orbits not above one thousand feet.
PARSE (simplified JSON):
{
"CALLSIGN": {
"PHONETICALPHABET": {
"PHONETICALPHABET": {
"PHONETICALPHABET": "Echo",
"PHONETICALPHABET": {
"PHONETICALPHABET": "India",
"PHONETICALPHABET": "Romeo"
}
}
}
},
"REQUESTINSTRUCTION": {
"REQUESTINSTRUCTION": "make",
"ORBIT": {
"ORBIT": {
"ORBITDIRECTION": "left",
"ORBIT": "orbits",
"ALTITUDE": {
"COMPARISON": {
"COMPARISON": {
"NEGATION": "not",
"COMPARISON": {
"COMPARISON": "above",
"WORDNUMBER": {
"WORDNUMBER": "one",
"WORDNUMBER": "thousand feet"
}
}
}
}
}
}
}
}
}
An ATC instruction may be conditional. The condition IF may stay both before and after the instruction body. This is important that the condition and instruction body be paired by the instruction scope.
Let’s consider two examples of ATC commands with IF condition.
1) This command just instructs the pilot what to do if GPS doesn’t work. He/she should proceed to JKL beacon and then hold awaiting further instructions.
The problem here is EXPECT (await) instruction.
From one hand side, this instruction should be in the scope of the IF condition.
But it also should be in the scope of hold instruction because the pilot should wait for new instructions being in the hold loop.
So it seems this is OK to place the EXPECT instruction in the scope of HOLD instruction.
COMMAND: If unable GPS proceed to JKL NDB and hold await further instructions
PARSE (simplified JSON):
{
"CONDITION": {
"CONDITION": {
"CONDITION": {
"CONDITION": "If",
"STATUS": {
"MODALITY": "unable",
"NAVAID": "GPS"
}
},
"NAVIGATION": {
"NAVIGATION": {
"NAVIGATION": "proceed",
"TO": "to",
"FIX": {
"FIX": "JKL",
"NAVAID": "NDB"
}
}
},
"CONJ": "and",
"HOLD": {
"HOLD": {
"HOLD": "hold",
"EXPECT": {
"EXPECT": "await",
"REQUESTINSTRUCTION": "further instructions"
}
}
}
}
}
}
2) Here N456CD is released - the pilot is granted a permission to take off at any time until a specified time moment.
This permission is denied if the pilot didn’t manage to take off before 15:25 Zulu time.
COMMAND: N456CD is released Void if not off by 1525 Zulu
PARSE (simplified JSON):
{
"CLEARANCE": {
"CALLSIGN": "N456CD",
"IS": "is",
"CLEARANCE": "released"
},
"OPERATION": {
"OPERATION": "Void",
"CONDITION": {
"CONDITION": "if",
"NEGATION": {
"NEGATION": "not",
"DEPARTURE": {
"OFF": "off",
"BY": "by",
"TIMEINFO": {
"TIMEINFO": "1525",
"PHONETICALPHABET": "Zulu"
}
}
}
}
}
}
Let’s consider this example:
COMMAND: United 112 fly heading 270 vector to rejoin Q42 airway Resume own navigation once established
What can we say about this command? It seems that in an early time moment United 112 had a problem - for example with turbulence, or with another traffic separation or with something else.
Because of this problem, the pilot was required to change his route by a controller.
Now the problem is fixed and the pilot may resume own navigation (return to a route as it was filled in his flight plan?).
But initially the pilot should fly heading 270 to rejoin Q42 airway (route from flight plan?).
Once this rejoining is established, the pilot is responsible for continuing to fly by the route as it was filed in his flight plan.
PARSE (simplified JSON):
{
"CALLSIGN": {
"AIRCRAFT": "United",
"INTNUMBER": "112"
},
"HEADING": {
"NAVIGATION": "fly",
"HEADING": {
"HEADING": "heading",
"INTNUMBER": "270"
}
},
"RADAR": {
"RADAR": "vector",
"TO": "to",
"NAVIGATION": {
"NAVIGATION": "rejoin",
"ROUTING": {
"ROUTE": "Q42",
"ROUTE": "airway"
}
}
},
"RESUME": {
"RESUME": "Resume",
"DISCRETION": "own navigation",
"CONDITION": {
"CONDITION": "once",
"STATUS": "established"
}
}
}
Controllers prefer to place AFTER condition before conditional instruction because in the other case (instruction before condition), if transmission problem occurs the pilot may get instruction but miss condition. This is a very dangerous situation.
However, while not recommended, the AFTER condition may be placed after instruction or even interrupt the instruction.
COMMAND: Taxi after the Airbus A320 via Alpha to holding point runway 27
PARSE (simplified JSON):
{
"GROUNDMOVEMENT": {
"GROUNDMOVEMENT": "Taxi",
"AFTER": {
"AFTER": {
"AFTER": "after",
"the AIRCRAFT": "Airbus A320"
}
},
"ROUTE": {
"ROUTE": {
"VIA": "via",
"ROUTE": {
"PHONETICALPHABET": "Alpha",
"ROUTE": {
"TO": "to",
"HOLDINGPOINT": {
"HOLDINGPOINT": "holding point",
"RUNWAY": {
"RUNWAY": "runway",
"INTNUMBER": "27"
}
}
}
}
}
}
}
}
While usually only one instruction follows the AFTER condition it is possible that more than one instruction follows the condition:
COMMAND: After the landing Airbus cross runway 18 and taxi via Bravo.
PARSE (simplified JSON):
{
"NAVIGATION": {
"AFTER": {
"AFTER": {
"AFTER": "After",
"the ONGOINGACTION": {
"ONGOINGACTION": "landing",
"AIRCRAFT": "Airbus"
}
}
},
"NAVIGATION": {
"NAVIGATION": {
"NAVIGATION": "cross",
"RUNWAY": {
"RUNWAY": "runway",
"INTNUMBER": "18"
}
}
},
"CONJ": "and",
"GROUNDMOVEMENT": {
"GROUNDMOVEMENT": "taxi",
"VIA": "via",
"PHONETICALPHABET": "Bravo"
}
}
}