RJTT ACA 3.3.0 and tools 0.11.0
This commit is contained in:
+1773
-1024
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
# `RJTT` ACA 3.2.0
|
||||
# `RJTT` ACA 3.3.0
|
||||
|
||||
This is an implementation of the Tokyo ACA (Approach Control Area) for [Endless ATC](https://steamcommunity.com/app/666610) featuring `RJTT` Tokyo International Airport (commonly referred to as Haneda) and `RJAA` Narita International Airport. JSDF-M base `RJTL` Shimofusa is also represented at very high scores (difficulties). The airspace ceiling is FL240.
|
||||
|
||||
@@ -9,7 +9,7 @@ The Tokyo ACA is a very large terminal area containing two of Japan's largest ai
|
||||
STARs are implemented as approach transitions. To activate an approach, an aircraft must be flying direct to an applicable fix, then the APP button can be activated. Multiple approaches may be available from a fix. Pressing the APP button again before issuing the approach clearance (do not long press) will select the next approach available from that fix. If the aircraft is already on an approach from that fix, you will need to cancel the approach clearance first before issuing another approach clearance.
|
||||
|
||||
JSDF-M base `RJTE` Tateyama and JSDF-G bases `RJTK` Kisarazu, `RJAK` Kasumigaura are not represented as it appears traffic should mostly be military helicopters, which are difficult to represent in this game.
|
||||
`RJTO` Oshima/`RJAN` Niijima are not represented as traffic is either helicopters or traffic to `RJTF` Chofu in `RJTY` Yokota ACA. Unfortunately traffic to `RJTY` is difficult to represent as `RJTT` ACA has airspace on top of most of `RJTY` ACA, meaning that within the game, it is not possible to get planes to "spawn" from the appropriate region. However, the runways for `RJTO`, `RJAN`, and `RJTF` are shown on the radar map.
|
||||
`RJTO` Oshima/`RJAN` Niijima are not represented as traffic is either helicopters or traffic to `RJTF` Chofu in `RJTY` Yokota ACA. Unfortunately traffic to `RJTY` is difficult to represent as `RJTT` ACA has airspace on top of most of `RJTY` ACA, meaning that within the game, it is not possible to get planes to "spawn" from the appropriate region. However, the runways for `RJTO`, `RJAN`, `RJTF`, `RJTE`, and `RJTK` are shown on the radar map.
|
||||
|
||||
## Airports
|
||||
|
||||
@@ -299,4 +299,21 @@ Note that traffic data (`airlines = `) is expanded by a python script `expand_ai
|
||||
- Implemented tower frequencies
|
||||
* 3.2.0 - 2021/07/06
|
||||
- Add handoff callsign / frequency support for departures
|
||||
- Additions/corrections to rare traffic.
|
||||
- Additions/corrections to rare traffic.
|
||||
* 3.3.0 - 2021/07/18
|
||||
- Implement different entry altitudes for each entrypoint
|
||||
- Implement different initial climb altitudes for each departure
|
||||
- Arrival entrypoints changed for most arrival routes to waypoints along the ACA boundary
|
||||
- Arrivals are automatically cleared for a relevant STAR
|
||||
- This is a temporary measure until 4.5.1 releases
|
||||
- this will be changed so that aircraft will only be cleared to the first fix of the STAR and hold there
|
||||
- Clearing aircraft via the STAR will then be the responsibility of approach control (you)
|
||||
- Correct pronunciation for `TETRA` departures from `RJAA`
|
||||
- Add 200kts restriction at `ARLON` for ILS Z 34L at `RJTT` to prevent certain aircraft such as `B772` from overshooting the GS
|
||||
- Add inactive `RJTK` Kisarazu and `RJTE` Tateyama
|
||||
- Add `KZT` Kisarazu and `TET` Tateyama TACANs
|
||||
- ANA `B737`s retired
|
||||
- Made some rare traffic unique (no more than one will appear at any one time)
|
||||
* 3.4.0 - pending release of 4.5.1
|
||||
- Upon entering the ACA, planes will fly along any airways to the beginning of the STAR and hold
|
||||
- Approach (you) will be responsible for clearing for the STAR
|
||||
@@ -1,4 +1,4 @@
|
||||
# `RJTT` 進入管制区 3.2.0
|
||||
# `RJTT` 進入管制区 3.3.0
|
||||
|
||||
*作者は日本人ではないため、圧倒的語彙力のなさと知識不足によるおかしいまたは間違っている表現があるかもしれません。
|
||||
|
||||
@@ -251,4 +251,21 @@ Approaches are available using APP mode from `TOHNE` and `ASEKI`. Arrival routes
|
||||
- 各空港のタワー周波数を実装
|
||||
* 3.2.0 - 2021/07/06
|
||||
- 出発機のハンドオフ先のコールサイン及び周波数を実装
|
||||
- レア発着機の追加や修正
|
||||
- レア発着機の追加や修正
|
||||
* 3.3.0 - 2021/07/06
|
||||
- 進入管制区の各入口の維持高度を個別に設定
|
||||
- 各SIDの初期維持高度を個別に設定
|
||||
- 進入管制区の入口ポイントを進入管制区境界にあるポイントに変更
|
||||
- 到着機はSTARの飛行が許可された状態でスポーンする
|
||||
- この仕様はゲームの4.5.1のリリースまでの一時的処置
|
||||
- 後で仕様をSTARの開始点までの許可に変更する(そこでホールドに入る)
|
||||
- STARへの許可はアプローチ管制(あなた)の責任になる
|
||||
- `RJAA`の`TETRA`SIDの発音を修正
|
||||
- 一部の飛行機(`B772`など)がGSを超えてしまうのを防止するために`RJTT`ILS Z 34Lに`ARLON`での200kt制限を設ける
|
||||
- `RJTK`木更津と`RJTE`館山の滑走路を実装(オープンはしない)
|
||||
- `KZT`木更津と`TET`館山TACANを実装
|
||||
- ANA`B737`引退
|
||||
- 一部のレア発着機をユニークに(同じのは同時に2以上出現しない)
|
||||
* 3.4.0 - 4.5.1リリース待ち
|
||||
- 到着機は進入管制区に入った後、RNAV経路に沿ってSTAR開始点まで飛行してホールドに入る
|
||||
- STARの許可を出すのはアプローチ管制(あなた)になる
|
||||
+1384
-975
File diff suppressed because it is too large
Load Diff
+136
-2
@@ -1,4 +1,4 @@
|
||||
# Endless ATC Custom Airport Tools 0.10.1
|
||||
# Endless ATC Custom Airport Tools 0.11.0
|
||||
|
||||
In this directory are a few tools useful for writing Endless ATC airport files. You can see examples of its usage in `RJTT` and `RJBB`.
|
||||
|
||||
@@ -35,6 +35,27 @@ Applicable sections:
|
||||
* `[transition]` (deprecated)
|
||||
* `route=`
|
||||
|
||||
### Header
|
||||
|
||||
The build process results in a fairly minimal file with no comments. To add a header, define it in `[meta]` of the source file:
|
||||
|
||||
```INI
|
||||
[meta]
|
||||
header = RJTT ACA 3.3.0
|
||||
See RJTT_readme.md
|
||||
```
|
||||
|
||||
A message indicating this file was built using these tools and the location of the source file will be appended after the `header=` message:
|
||||
|
||||
```INI
|
||||
# RJTT ACA 3.3.0
|
||||
# See RJTT_readme.md
|
||||
|
||||
# This file is generated from the source file source\RJTT.txt using expand.py.
|
||||
# All comments have been stripped, and edits are not made directly to this file.
|
||||
# If you would like to contribute, or see the author's comments, please refer to the source file.
|
||||
```
|
||||
|
||||
### Fix definitions and references
|
||||
|
||||
Fixes defined in `[airspace]` `beacons=` are stored in a database for reference elsewhere in the file.
|
||||
@@ -65,6 +86,111 @@ Any fixes can be referenced by name later in the following areas using `!<fix_na
|
||||
|
||||
The `center=` of the TMA will be made available under the name `_CTR` as a hidden fix.
|
||||
|
||||
Example for `handoff=`:
|
||||
```INI
|
||||
handoff =
|
||||
#T13 (Musashi Sector)
|
||||
!KAGNA, Tokyo Control, Tokyo Control, 132.1
|
||||
!SEDRI, Tokyo Control, Tokyo Control, 132.1
|
||||
!MITOP, Tokyo Control, Tokyo Control, 132.1
|
||||
!DALMA, Tokyo Control, Tokyo Control, 292.4
|
||||
#T03 (Kanto North Sector)
|
||||
!KIMIN, Tokyo Control, Tokyo Control, 124.1
|
||||
!AGRIS, Tokyo Control, Tokyo Control, 124.1
|
||||
!CLARK, Tokyo Control, Tokyo Control, 124.1
|
||||
!JD, Tokyo Control, Tokyo Control, 276.1
|
||||
```
|
||||
|
||||
becomes:
|
||||
```INI
|
||||
handoff = 259, Tokyo Control, Tokyo Control, 132.1
|
||||
267, Tokyo Control, Tokyo Control, 132.1
|
||||
275, Tokyo Control, Tokyo Control, 132.1
|
||||
261, Tokyo Control, Tokyo Control, 292.4
|
||||
359, Tokyo Control, Tokyo Control, 124.1
|
||||
350, Tokyo Control, Tokyo Control, 124.1
|
||||
336, Tokyo Control, Tokyo Control, 124.1
|
||||
348, Tokyo Control, Tokyo Control, 276.1
|
||||
```
|
||||
|
||||
### Multiplying entry points
|
||||
|
||||
```INI
|
||||
entrypoints =
|
||||
#Following line will be repeated 10 times (without the ", *10" at the end)
|
||||
242, SPENS, 22000, *10
|
||||
#Following line will be repeated 6 times (without the ", *6" at the end)
|
||||
223, SELNO, 23000, *6
|
||||
123, DOLBA, 22000
|
||||
193, TOPIT, 22000
|
||||
#Following line will be repeated 7 times (without the ", *7" at the end)
|
||||
359, TEDIX, 16000, *7
|
||||
64, LALID, 16000
|
||||
```
|
||||
|
||||
### Airlines
|
||||
|
||||
Airlines definitions with a frequency greater than 10 will be split into multiple definitions with frequency 10 or less.
|
||||
|
||||
```INI
|
||||
#The below will result in 30 lines of "ana, 10, b763, ..." and 10 lines of "ana, 10, b77l, w"
|
||||
#The a124 lines will remain the same as they are less than 10 frequency
|
||||
airlines =
|
||||
ana, 300, b763, all nippon, w
|
||||
ana, 100, b77l, all nippon, w
|
||||
vda, 5, a124, volga, nswe
|
||||
adb, 1, a124, antonov, nswe
|
||||
```
|
||||
|
||||
### Auto airlines callsigns
|
||||
|
||||
If callsigns is enabled in meta, callsigns can be omitted in `airlines=`. They will be resolved using the list in `common.ini` in the directory where these tools are located.
|
||||
You can override these callsign definitions (or add local callsigns) in a `common.ini` in the directory of the source file.
|
||||
```INI
|
||||
[meta]
|
||||
header = RJTT ACA 3.3.0
|
||||
See RJTT_readme.md
|
||||
callsigns = True
|
||||
```
|
||||
common.ini:
|
||||
```INI
|
||||
[expand.callsigns]
|
||||
cygns = cygnus
|
||||
#if the callsign code portion is less than 2 characters, add an underscore prefix to make it 3 characters
|
||||
_jf = japan force
|
||||
```
|
||||
|
||||
Then in your source file:
|
||||
```INI
|
||||
airlines =
|
||||
cygns-1, 10, b77w, nswe
|
||||
#if the callsign code portion is less than 2 characters, add an underscore prefix to make it 3 characters
|
||||
_jf-1, 10, b77w, nswe
|
||||
```
|
||||
Which becomes:
|
||||
```INI
|
||||
airlines =
|
||||
cygns-1, 10, b77w, cygnus, nswe
|
||||
jf-1, 10, b77w, japan force, nswe
|
||||
```
|
||||
|
||||
### Unique aircraft callsign resolution (requires `callsigns = True`)
|
||||
|
||||
You can define callsigns with two dashes, one at the end. This will result in a "unique" aircraft of which only one can appear at a time:
|
||||
```INI
|
||||
airlines =
|
||||
gaf-1-, 1, a343, nw
|
||||
_jf-1-, 10, b77w, nswe
|
||||
```
|
||||
becomes:
|
||||
```INI
|
||||
airlines =
|
||||
gaf1-, 1, a343, german air force one, nw
|
||||
jf1-, 10, b77w, japan air force one, nswe
|
||||
```
|
||||
|
||||
Note that the only possible aircraft that can spawn with the above definitions are `GAF1` and `JF1`. The game will be unable to spawn any more than two aircraft as only two unique callsigns can be generated.
|
||||
|
||||
### Advanced fix definitions (requires PyGeodesy)
|
||||
|
||||
If PyGeodesy is available, the below functions are also available. The tool will error if a file being built tries to use advanced fix definitions when PyGeodesy is not available.
|
||||
@@ -409,4 +535,12 @@ route = *4
|
||||
- `_CTR` fix will be generated as a hidden fix from `[airspace] center=`.
|
||||
- Added feature to calculate headings from fixes for `[airspace] handoff=`.
|
||||
* 0.10.1 - 2021/07/01
|
||||
- Bugfix: consider magnetic variation when computing `[airspace] handoff=`.
|
||||
- Bugfix: consider magnetic variation when computing `[airspace] handoff=`.
|
||||
* 0.11.0 - 2021/07/18
|
||||
- Entrypoints now support multipliers
|
||||
- Approaches that end with a hold can link to other approaches
|
||||
- In preparation for 4.5.1
|
||||
- To be used to connect to multiple runways
|
||||
- The approach route is not extended with the linked approach
|
||||
- Airlines can be defined with two dashes to define unique traffic
|
||||
- Pronunciation generation added to support this
|
||||
+41
-3
@@ -207,7 +207,7 @@ ssg = slovak government
|
||||
jcg = japan coast guard
|
||||
flc = flight check
|
||||
vjt = vista jet
|
||||
ivy0 = ivory coast
|
||||
ivy = ivory coast
|
||||
trk = turkish republic
|
||||
bah = bahrain
|
||||
orf = oman
|
||||
@@ -220,7 +220,45 @@ idaf = indonesia air force
|
||||
sui = swiss air force
|
||||
esw = w-business
|
||||
rmf = angkasa
|
||||
bec00 = bravo echo charlie zero zero
|
||||
rsd = state aero
|
||||
|
||||
[expand.gateways]
|
||||
* = nswe
|
||||
* = nswe
|
||||
|
||||
[expand.phonetic]
|
||||
0 = zero
|
||||
1 = one
|
||||
2 = two
|
||||
3 = tree
|
||||
4 = four
|
||||
5 = fife
|
||||
6 = six
|
||||
7 = seven
|
||||
8 = eight
|
||||
9 = niner
|
||||
a = alpha
|
||||
b = bravo
|
||||
c = charlie
|
||||
d = delta
|
||||
e = echo
|
||||
f = foxtrot
|
||||
g = golf
|
||||
h = hotel
|
||||
i = india
|
||||
j = juliet
|
||||
k = kilo
|
||||
l = lima
|
||||
m = mike
|
||||
n = november
|
||||
o = oscar
|
||||
p = papa
|
||||
q = quebec
|
||||
r = romeo
|
||||
s = sierra
|
||||
t = tango
|
||||
u = uniform
|
||||
v = victor
|
||||
w = whiskey
|
||||
x = x-ray
|
||||
y = yankee
|
||||
z = zulu
|
||||
+167
-25
@@ -3,6 +3,7 @@ from collections import deque
|
||||
import configparser
|
||||
from copy import deepcopy
|
||||
from dataclasses import dataclass
|
||||
from itertools import chain
|
||||
import os
|
||||
import re
|
||||
from typing import ClassVar, Optional
|
||||
@@ -378,6 +379,12 @@ class Airline:
|
||||
callsigns = None
|
||||
use_callsigns = True
|
||||
test_callsigns = False
|
||||
phonetic = None
|
||||
|
||||
@classmethod
|
||||
def initialize(cls, callsigns, phonetic):
|
||||
cls.callsigns = callsigns
|
||||
cls.phonetic = phonetic
|
||||
|
||||
def __init__(self, callsign, frequency, types, *data, gateways=None):
|
||||
"""Create an airline from an entry in the airlines= list. `data` should be
|
||||
@@ -395,6 +402,11 @@ class Airline:
|
||||
if '-' not in callsign:
|
||||
self.pronunciation = Airline.callsigns[callsign]
|
||||
else:
|
||||
if callsign.endswith('-') and callsign.index('-') != callsign.rindex('-'):
|
||||
unique = True
|
||||
callsign = callsign[:-1]
|
||||
else:
|
||||
unique = False
|
||||
# if length of key is more than 3, assume it is not registration
|
||||
key = callsign[:callsign.index('-')]
|
||||
self.callsign = callsign.strip('_')
|
||||
@@ -402,6 +414,13 @@ class Airline:
|
||||
# we strip '_' to allow for mil callsigns with length <= 3
|
||||
self.pronunciation = Airline.callsigns.get(key, key.strip('_')) if len(key) > 3 \
|
||||
else Airline.callsigns.get(key, '0')
|
||||
if unique:
|
||||
midpoint = self.callsign.index('-')
|
||||
if self.pronunciation != '0':
|
||||
number = self.callsign[midpoint + 1:]
|
||||
self.pronunciation += " " + " ".join([self.phonetic[digit] for digit in number])
|
||||
self.callsign = self.callsign[:midpoint] + self.callsign[midpoint + 1:] + '-'
|
||||
|
||||
if Airline.test_callsigns:
|
||||
print(f'{self.callsign}: {self.pronunciation}')
|
||||
if Airline.callsigns is None or not Airline.use_callsigns:
|
||||
@@ -418,6 +437,9 @@ class Airline:
|
||||
raise ValueError(f'''Could not create airline from ({callsign}, {frequency}, {types}, {str(data)})
|
||||
Callsign pronunciation lookup = {Airline.use_callsigns}''') from e
|
||||
|
||||
def definition(self, frequency):
|
||||
return f"{self.callsign}, {frequency}, {self.types}, {self.pronunciation}, {self.directions}"
|
||||
|
||||
|
||||
def process_fix_line(line, fixes):
|
||||
"""Expands special commands for a fix definition in short format
|
||||
@@ -459,6 +481,15 @@ def process_fix_list(fix_list, fixes):
|
||||
yield process_fix_line(line, fixes)
|
||||
|
||||
|
||||
def fix_list_with_altitude(fix_list, altitude):
|
||||
for line in fix_list:
|
||||
if altitude is not None:
|
||||
yield line + ", " + altitude
|
||||
altitude = None
|
||||
else:
|
||||
yield line
|
||||
|
||||
|
||||
def _generate_approach(heading, starting_fix):
|
||||
return {"heading": heading, "beacon": starting_fix, "route": []}
|
||||
|
||||
@@ -534,6 +565,12 @@ def _process_approach_fix_list(fix_list, runway, fixes, tagged_routes,
|
||||
|
||||
_process_simple_approach_fix_list(fix_list[:-1], runway, fixes,
|
||||
tagged_routes[runway], generated_approaches, current_tag, top_level)
|
||||
|
||||
terminate = False
|
||||
if len(fix_list) > 1:
|
||||
last_fix = fix_list[-2].strip()
|
||||
terminate = last_fix.startswith('end') and last_fix.endswith('hold')
|
||||
|
||||
for generated_approach in generated_approaches[runway or current_tag]:
|
||||
if 'tag' in generated_approach:
|
||||
tagged_routes[runway][generated_approach['tag']].append(fix_list[-1])
|
||||
@@ -573,9 +610,10 @@ The requesting approach route was {fix_list}''')
|
||||
following_route = tagged_routes[following_tag_runway][following_tag]
|
||||
if remove_first_fix:
|
||||
following_route = following_route[1:]
|
||||
_process_approach_fix_list(tagged_routes[following_tag_runway][following_tag],
|
||||
following_tag_runway, fixes, tagged_routes, generated_approaches, following_tag,
|
||||
False, debug=debug)
|
||||
if not terminate:
|
||||
_process_approach_fix_list(following_route,
|
||||
following_tag_runway, fixes, tagged_routes, generated_approaches, following_tag,
|
||||
False, debug=debug)
|
||||
else:
|
||||
_process_simple_approach_fix_list(fix_list, runway, fixes,
|
||||
tagged_routes[runway], generated_approaches, current_tag, top_level)
|
||||
@@ -637,7 +675,7 @@ def process_approach_fix_list(fix_list, runway, fixes, tagged_routes,
|
||||
raise RuntimeError(f"Tried to process an empty approach route {fix_list} for runway {runway}")
|
||||
|
||||
|
||||
def process_departure_fix_list(fix_list, runway, fixes, tagged_routes):
|
||||
def process_departure_fix_list(fix_list, runway, airport, fixes, tagged_routes, base_runway=None):
|
||||
"""Processes special commands in a list of departure fixes
|
||||
in short format and produces an iterable of definitions as the result,
|
||||
or `None` if there is no result (fix_list was recorded in `tagged_routes`.
|
||||
@@ -656,42 +694,90 @@ def process_departure_fix_list(fix_list, runway, fixes, tagged_routes):
|
||||
Args:
|
||||
`fix_list` (list): A list of fix definitions.
|
||||
`runway` (str): The runway this departure is for.
|
||||
`airport` (str): The airport this departure is for.
|
||||
`fixes` (dict): A lookup of `Fix`es.
|
||||
`tagged_routes` (dict): A lookup of departure routes."""
|
||||
`tagged_routes` (dict): A lookup of tagged departure routes.
|
||||
`base_runway` (str): The runway to reference when looking up tagged routes."""
|
||||
if fix_list:
|
||||
if runway not in tagged_routes:
|
||||
tagged_routes[runway] = {}
|
||||
if airport not in tagged_routes:
|
||||
tagged_routes[airport] = {}
|
||||
if fix_list[0].startswith('@'):
|
||||
tag = fix_list[0].lstrip('@')
|
||||
tagged_routes[not tag.startswith('!') and runway or None][tag] = fix_list[1:]
|
||||
tag_namespace = airport
|
||||
if tag.startswith('!!'):
|
||||
tag_namespace = None
|
||||
elif tag.startswith('!'):
|
||||
tag_namespace = runway
|
||||
elif runway:
|
||||
tag_namespace = runway
|
||||
else:
|
||||
raise RuntimeError(f"departure tagged {tag} as a runway-specific route, but no runway was specified")
|
||||
tagged_routes[tag_namespace][tag] = fix_list[1:]
|
||||
return None
|
||||
else:
|
||||
return _process_departure_fix_list(fix_list, runway, fixes, tagged_routes)
|
||||
if base_runway is not None:
|
||||
runway = base_runway
|
||||
return _process_departure_fix_list(fix_list, runway, airport, fixes, tagged_routes)
|
||||
|
||||
|
||||
def _process_departure_fix_list(fix_list, runway, fixes, tagged_routes, top_level=True):
|
||||
def _process_departure_fix_list(fix_list, runway, airport, fixes, tagged_routes, top_level=True, altitude=None):
|
||||
"""Inner worker function for `process_departure_fix_list()`.
|
||||
This produces the actual generator with the route= lines.
|
||||
|
||||
Args:
|
||||
`fix_list` (list): A list of fix definitions.
|
||||
`runway` (str): The runway this departure is for.
|
||||
`airport` (str): The airport this departure is for.
|
||||
`fixes` (dict): A lookup of `Fix`es.
|
||||
`tagged_routes` (dict): A lookup of departure routes."""
|
||||
`tagged_routes` (dict): A lookup of departure routes.
|
||||
`altitude` (str): An altitude to append to the first departure fix."""
|
||||
if fix_list:
|
||||
try:
|
||||
if top_level:
|
||||
yield fix_list[0]
|
||||
fix_list = fix_list[1:]
|
||||
# climb altitude
|
||||
if fix_list[0].strip().isdigit():
|
||||
if altitude is None:
|
||||
altitude = fix_list[0].strip()
|
||||
fix_list = fix_list[1:]
|
||||
if fix_list[0].startswith('@'):
|
||||
tag = fix_list[0].lstrip('@')
|
||||
yield from _process_departure_fix_list(
|
||||
tagged_routes[not tag.startswith('!') and runway or None][fix_list[0].lstrip('@')],
|
||||
runway, fixes, tagged_routes, top_level=False)
|
||||
if tag.startswith('!') and tag in tagged_routes[None]:
|
||||
tagged_route = tagged_routes[None][tag]
|
||||
elif tag in tagged_routes[runway]:
|
||||
tagged_route = tagged_routes[runway][tag]
|
||||
elif tag in tagged_routes[airport]:
|
||||
tagged_route = tagged_routes[airport][tag]
|
||||
else:
|
||||
raise KeyError(f"Unable to find route tagged @{tag}")
|
||||
yield from _process_departure_fix_list(tagged_route,
|
||||
runway, airport, fixes, tagged_routes, top_level=False, altitude=altitude)
|
||||
yield from _process_departure_fix_list(fix_list[1:],
|
||||
runway, fixes, tagged_routes, top_level=False)
|
||||
runway, airport, fixes, tagged_routes, top_level=False)
|
||||
|
||||
elif fix_list[-1].startswith('@'):
|
||||
tag = fix_list[-1].lstrip('@')
|
||||
if tag.startswith('!') and tag in tagged_routes[None]:
|
||||
tagged_route = tagged_routes[None][tag]
|
||||
elif tag in tagged_routes[runway]:
|
||||
tagged_route = tagged_routes[runway][tag]
|
||||
elif tag in tagged_routes[airport]:
|
||||
tagged_route = tagged_routes[airport][tag]
|
||||
else:
|
||||
raise KeyError(f"Unable to find route tagged @{tag}")
|
||||
yield from _process_departure_fix_list(fix_list[:-1],
|
||||
runway, airport, fixes, tagged_routes, top_level=False, altitude=altitude)
|
||||
yield from _process_departure_fix_list(tagged_route,
|
||||
runway, airport, fixes, tagged_routes, top_level=False)
|
||||
|
||||
else:
|
||||
if altitude is not None:
|
||||
fix_list = fix_list_with_altitude(fix_list, altitude)
|
||||
yield from process_fix_list(fix_list, fixes)
|
||||
|
||||
except Exception as e:
|
||||
raise RuntimeError(
|
||||
f"Could not process departure route {fix_list} for runway {runway}"
|
||||
@@ -716,7 +802,26 @@ def process_sids_fix_list(fix_list, fixes):
|
||||
yield line
|
||||
|
||||
|
||||
def process_repeatable_departure_fix_list(fix_list, runway, fixes, tagged_routes):
|
||||
def process_entrypoints_list(entrypoints_list):
|
||||
"""Processes special commands in a list of fixes in minor format
|
||||
and produces an iterable of definitions as the result.
|
||||
|
||||
Substitute any "!<name>[, <extra_data>]" in `fix_list` with
|
||||
"lat, lon[, <extra_data>]" based on `fixes`.
|
||||
|
||||
Args:
|
||||
`entrypoints_list` (list): A list of entrypoints definitions."""
|
||||
for line in entrypoints_list:
|
||||
entrypoint_definition, _, entrypoint_last_parameter = line.rpartition(',')
|
||||
entrypoint_last_parameter = entrypoint_last_parameter.strip()
|
||||
if entrypoint_last_parameter.startswith('*'):
|
||||
for i in range(int(entrypoint_last_parameter.strip('*'))):
|
||||
yield entrypoint_definition
|
||||
else:
|
||||
yield line
|
||||
|
||||
|
||||
def process_repeatable_departure_fix_list(fix_list, runway, airport, fixes, tagged_routes, base_runway=None):
|
||||
"""`Processes special commands in a list of departure fixes
|
||||
in short format and produces an iterable of n iterables of
|
||||
definitions as the result.
|
||||
@@ -729,16 +834,20 @@ def process_repeatable_departure_fix_list(fix_list, runway, fixes, tagged_routes
|
||||
Args:
|
||||
`fix_list` (list): A list of fix definitions.
|
||||
`runway` (str): The runway this departure is for.
|
||||
`airport` (str): The airport this departure is for.
|
||||
`fixes` (dict): A lookup of `Fix`es.
|
||||
`tagged_routes` (dict): A lookup of departure routes."""
|
||||
`tagged_routes` (dict): A lookup of tagged departure routes.
|
||||
`base_runway` (str): The runway to reference when looking up tagged routes."""
|
||||
if not fix_list[0]:
|
||||
fix_list = fix_list[1:]
|
||||
if fix_list[0].startswith('*'):
|
||||
result = list(process_departure_fix_list(fix_list[1:], runway, fixes, tagged_routes))
|
||||
result = list(process_departure_fix_list(fix_list[1:], runway, airport,
|
||||
fixes, tagged_routes, base_runway))
|
||||
for i in range(int(fix_list[0].removeprefix('*'))):
|
||||
yield result
|
||||
else:
|
||||
yield process_departure_fix_list(fix_list, runway, fixes, tagged_routes)
|
||||
yield process_departure_fix_list(fix_list, runway, airport,
|
||||
fixes, tagged_routes, base_runway)
|
||||
|
||||
|
||||
def process_beacons(fixes):
|
||||
@@ -770,9 +879,9 @@ def process_airlines_list(airline_list):
|
||||
for airline in airline_list:
|
||||
n, r = divmod(airline.frequency, 10)
|
||||
for i in range(n):
|
||||
yield f"{airline.callsign}, 10, {airline.types}, {airline.pronunciation}, {airline.directions}"
|
||||
yield airline.definition(10)
|
||||
if r:
|
||||
yield f"{airline.callsign}, {r}, {airline.types}, {airline.pronunciation}, {airline.directions}"
|
||||
yield airline.definition(r)
|
||||
|
||||
|
||||
def enumerate_routes(route_list, start=1):
|
||||
@@ -803,7 +912,8 @@ def process(args, input_file=None, preprocessed_input=None):
|
||||
config = configparser.ConfigParser()
|
||||
config.read("common.ini")
|
||||
config.read(os.path.join(os.path.dirname(input_file), "common.ini"))
|
||||
Airline.callsigns = config['expand.callsigns'] if 'expand.callsigns' in config else {}
|
||||
Airline.initialize(config['expand.callsigns'] if 'expand.callsigns' in config else {},
|
||||
config['expand.phonetic'])
|
||||
default_gateways = config['expand.gateways'] if 'expand.gateways' in config else {}
|
||||
|
||||
if 'legacy' not in args or not args.legacy:
|
||||
@@ -878,9 +988,21 @@ def process(args, input_file=None, preprocessed_input=None):
|
||||
area_data['labelpos'] = Fix.fixes[area_position].short_def
|
||||
|
||||
# process airport sections
|
||||
runway_to_airport = {}
|
||||
|
||||
for airport_data in airports.values():
|
||||
|
||||
airport_code = airport_data['code']
|
||||
airport_code, _, airport_code_full = airport_code.partition(',')
|
||||
if airport_code_full:
|
||||
airport_data['code'] = airport_code
|
||||
airport_code = airport_code_full
|
||||
|
||||
runways = airport_data['runways'].strip().splitlines()
|
||||
for runway_definition in runways:
|
||||
runway_id, _, _ = runway_definition.partition(',')
|
||||
runway_to_airport[runway_id] = airport_code
|
||||
|
||||
gateways = dict((tuple(map(str.strip, gateway.split(","))) for gateway in airport_data['gateways'].strip().splitlines()),
|
||||
**default_gateways) if 'gateways' in airport_data else None
|
||||
|
||||
@@ -893,6 +1015,10 @@ def process(args, input_file=None, preprocessed_input=None):
|
||||
airport_data['sids'] = "\n".join(
|
||||
process_sids_fix_list(airport_data['sids'].splitlines(), Fix.fixes))
|
||||
|
||||
if 'entrypoints' in airport_data:
|
||||
airport_data['entrypoints'] = "\n".join(
|
||||
process_entrypoints_list(airport_data['entrypoints'].splitlines()))
|
||||
|
||||
# process approach/transition sections
|
||||
approaches = {section: source[section] for section in source
|
||||
if section.startswith('approach') or section.startswith('transition')}
|
||||
@@ -963,23 +1089,39 @@ Defined beacon was {generated_approach.beacon}, actual beacon was {approach_beac
|
||||
}
|
||||
|
||||
# process departure sections
|
||||
departures = [source[section] for section in source if section.startswith('departure')]
|
||||
common_departures = [source[section] for section in sorted(source) if section.startswith('commondeparture')]
|
||||
departures = [source[section] for section in sorted(source) if section.startswith('departure')]
|
||||
tagged_departures = {None: {}}
|
||||
|
||||
for departure_data in departures:
|
||||
departure_runway = departure_data['runway']
|
||||
departure_runway = departure_runway.partition(',')
|
||||
for departure_data in chain(common_departures, departures):
|
||||
if 'common' in departure_data:
|
||||
departure_airport = departure_data['common']
|
||||
departure_runway = None
|
||||
departure_base_runway = None
|
||||
else:
|
||||
departure_runway = departure_data['runway']
|
||||
departure_runway = departure_runway.partition(',')
|
||||
departure_airport = runway_to_airport[departure_runway[0]]
|
||||
departure_base_runway = departure_data.get('baserunway', fallback=None)
|
||||
if departure_base_runway is not None:
|
||||
departure_base_runway = departure_base_runway.partition(',')
|
||||
del departure_data['baserunway']
|
||||
routes = {int(option.removeprefix('route')): departure_data[option]
|
||||
for option in departure_data if option.startswith('route')}
|
||||
processed_routes = []
|
||||
for route_index in sorted(routes):
|
||||
processed_routes.extend("\n".join(route) for route in
|
||||
process_repeatable_departure_fix_list(
|
||||
routes[route_index].splitlines(), departure_runway, Fix.fixes, tagged_departures)
|
||||
routes[route_index].splitlines(), departure_runway, departure_airport,
|
||||
Fix.fixes, tagged_departures, departure_base_runway)
|
||||
if route)
|
||||
del departure_data[f'route{route_index}']
|
||||
departure_data.update(enumerate_routes(processed_routes, start=1))
|
||||
|
||||
common_departures = [section for section in source if section.startswith('commondeparture')]
|
||||
for section in common_departures:
|
||||
del source[section]
|
||||
|
||||
# write output file
|
||||
if not args.parse_only:
|
||||
with open(output_file, 'w', newline='') as airport_file:
|
||||
|
||||
+2
-2
@@ -16,9 +16,9 @@ def process(args, file=None):
|
||||
`args`: An `argparse.Namespace`. The command line args from the invoking module.
|
||||
`file`: The file to process. Defaults to `input_file` in `args`."""
|
||||
|
||||
header_re = re.compile(r"^(?:\[(?P<header>(?:approach)|(?:transition)|(?:departure)|(?:area)|(?:airport))\d*\])|^(?:route\d? *= *)")
|
||||
header_re = re.compile(r"^(?:\[(?P<header>(?:approach)|(?:transition)|(?:(?:common)?departure)|(?:area)|(?:airport))\d*\])|^(?:route\d? *= *)")
|
||||
|
||||
def number_approach(match, indexes={'approach': 0, 'transition': 0, 'departure': 0, 'area': 0, 'route': 0, 'airport': 0}):
|
||||
def number_approach(match, indexes={'approach': 0, 'transition': 0, 'departure': 0, 'area': 0, 'route': 0, 'airport': 0, 'commondeparture': 0}):
|
||||
header = match.group("header")
|
||||
if header:
|
||||
indexes['route'] = 0
|
||||
|
||||
Reference in New Issue
Block a user