Summary
Building on the existing runtime extraction pipeline (a DO SCRIPT Lua trigger writes tagged lines to dcs.log, then a Python pipeline parses/exports them — currently used to calibrate each map's projection), we can extract airfield data with the same mechanism:
- Airbases — name, callsign, position, category, ID
- Runways — heading, length, width, position
- Parking — parking spots (type, position, distance to runway)
This is a feasibility-confirmed proposal. Frequencies / nav beacons are explicitly out of scope here (different mechanism — see below).
Motivation
The data above is static per map and broadly useful (airbase coordinates, runway headings & dimensions, parking layouts) for tooling, briefings, kneeboards, GIS overlays, etc. It fits the project's existing runtime-extraction approach with no new tech, and is actually simpler than the current map calibration because no model-fitting / calibration step is needed — the data is read directly.
What's extractable at runtime (verified against the DCS scripting API)
Entry point: world.getAirbases() → all airbases (+ ships + FARPs) on the map. Per Airbase object:
| Data |
Method |
Content |
| Airbase |
getName(), getCallsign() |
name / ATC callsign (e.g. "Batumi") |
|
getPoint() |
{x=North, y=alt, z=East} → lat/lon via coord.LOtoLL() |
|
getID() |
scripting ID (stable join key for runways/parking) |
|
getCategory() |
AIRDROME / HELIPAD / SHIP (lets us filter out ships & FARPs) |
|
getDesc() |
descriptor (attributes, …) |
| Runways |
getRunways() (DCS 2.7+) |
list of {Name, course, position{x,y,z}, length, width} |
| Parking |
getParking([available]) (2.5.2+) |
list of {Term_Index, Term_Type, vTerminalPos{x,y,z}, fDistToRW, TO_AC} |
Notes on the structures:
- Runway
course is in radians (multiply by -1, then convert to degrees); it is a true heading. Name (e.g. 08/26) is the magnetic designator provided by DCS. length/width in metres; position is the runway centre.
- Parking
Term_Type is an enum (16/40/68/72/100/104 — helipad / parking / takeoff point…; mapping is community-documented). fDistToRW = distance to the takeoff point.
How it fits the existing pipeline
DCS (Mission Editor) Python
+------------------------+ +----------------------+ +-----------------+
| tools/export_airbases | | parse tagged lines | | export 4 formats |
| .lua (DO SCRIPT ONCE) | ---> | from dcs.log | --> | yaml/json/md/py |
| world.getAirbases() | dcs.log | (regex, like | | (reuse export.py |
| + coord.LOtoLL() | | DCSXFORM_RE) | | writers) |
+------------------------+ +----------------------+ +-----------------+
NO calibration step <-- key difference vs. maps coordinates
- Reuse the exact runtime mechanism (
env.info into dcs.log, no MissionScripting.lua de-sanitisation needed — only env.info and coord.LOtoLL, always allowed).
- The
calibrate.py stage disappears for this data — it's read directly.
- Emit lat/lon directly from Lua via
coord.LOtoLL(point), exactly like today's DCSXFORM lines. Bonus: also emitting raw x/z gives a free cross-check of the calibrated tmerc projection (convert.py) against DCS's own LOtoLL (expect < 1 m error).
Proposed flat, tagged log format (parseable by a regex, like DCSXFORM):
DCSAB;THEATRE;Caucasus
DCSAB;AIRBASE;<id>;<category>;<name>;<callsign>;<lat>;<lon>;<alt>
DCSAB;RUNWAY;<airbase_id>;<name>;<course_deg>;<length>;<width>;<lat>;<lon>
DCSAB;PARKING;<airbase_id>;<term_index>;<term_type>;<dist_rw>;<lat>;<lon>
(Lua has no native JSON in the mission env; flat tagged lines are the most robust. Escape the delimiter if a name can contain ;.)
Proposed shape (non-prescriptive)
tools/export_airbases.lua — new runtime export script (twin of export_points.lua).
- a Python parsing module (modeled on
ingest.py / DCSXFORM_RE).
- new data file(s) + a generator reusing the
export.py multi-format writers (to_yaml/json/md/python).
- optionally a dedicated CLI subcommand (e.g.
dcs-coords airbases).
Candidate schema (per map, nested):
Caucasus:
airbases:
- id: 12
name: Batumi
category: AIRDROME
lat: 41.6103
lon: 41.5997
alt: 9.0
runways:
- name: "13"
heading_true: 126.0
length_m: 2438
width_m: 45
lat: ...
lon: ...
parking: # large volume — consider a separate file
- index: 100
type: 104
lat: ...
lon: ...
Out of scope: frequencies & nav beacons (separate future work)
- The scripting engine does not expose ATC frequencies at runtime (you can silence the ATC but not read its frequency — that's why SRS/LotAtc ship their own frequency tables).
- Nav beacons (TACAN, ILS, VOR, NDB, PRMG, RSBN) and frequencies live in terrain data files:
<DCS>/Mods/terrains/<Theatre>/Beacons.lua (+ Scripts/World/Radio/BeaconSites.lua / BeaconTypes.lua).
- Getting these programmatically means parsing those
.lua files offline, not runtime extraction → a separate, complementary track. Out of scope for this issue.
Caveats / notes
getRunways() requires DCS 2.7+; getParking() has been marked WIP since 2.5.2 (structure may evolve).
world.getAirbases() also returns ships and FARPs → filter on getCategory().
- No magnetic declination is available at runtime (no API); the magnetic runway designator is the
Name field.
- Parking volume is large (hundreds of slots per map x ~14 maps) → consider a separate file / compact representation so the airbase+runway file stays light.
Acceptance criteria / validation
References
Summary
Building on the existing runtime extraction pipeline (a
DO SCRIPTLua trigger writes tagged lines todcs.log, then a Python pipeline parses/exports them — currently used to calibrate each map's projection), we can extract airfield data with the same mechanism:This is a feasibility-confirmed proposal. Frequencies / nav beacons are explicitly out of scope here (different mechanism — see below).
Motivation
The data above is static per map and broadly useful (airbase coordinates, runway headings & dimensions, parking layouts) for tooling, briefings, kneeboards, GIS overlays, etc. It fits the project's existing runtime-extraction approach with no new tech, and is actually simpler than the current map calibration because no model-fitting / calibration step is needed — the data is read directly.
What's extractable at runtime (verified against the DCS scripting API)
Entry point:
world.getAirbases()→ all airbases (+ ships + FARPs) on the map. PerAirbaseobject:getName(),getCallsign()"Batumi")getPoint(){x=North, y=alt, z=East}→ lat/lon viacoord.LOtoLL()getID()getCategory()AIRDROME/HELIPAD/SHIP(lets us filter out ships & FARPs)getDesc()getRunways()(DCS 2.7+){Name, course, position{x,y,z}, length, width}getParking([available])(2.5.2+){Term_Index, Term_Type, vTerminalPos{x,y,z}, fDistToRW, TO_AC}Notes on the structures:
courseis in radians (multiply by-1, then convert to degrees); it is a true heading.Name(e.g.08/26) is the magnetic designator provided by DCS.length/widthin metres;positionis the runway centre.Term_Typeis an enum (16/40/68/72/100/104— helipad / parking / takeoff point…; mapping is community-documented).fDistToRW= distance to the takeoff point.How it fits the existing pipeline
env.infointodcs.log, noMissionScripting.luade-sanitisation needed — onlyenv.infoandcoord.LOtoLL, always allowed).calibrate.pystage disappears for this data — it's read directly.coord.LOtoLL(point), exactly like today'sDCSXFORMlines. Bonus: also emitting rawx/zgives a free cross-check of the calibratedtmercprojection (convert.py) against DCS's ownLOtoLL(expect < 1 m error).Proposed flat, tagged log format (parseable by a regex, like
DCSXFORM):(Lua has no native JSON in the mission env; flat tagged lines are the most robust. Escape the delimiter if a name can contain
;.)Proposed shape (non-prescriptive)
tools/export_airbases.lua— new runtime export script (twin ofexport_points.lua).ingest.py/DCSXFORM_RE).export.pymulti-format writers (to_yaml/json/md/python).dcs-coords airbases).Candidate schema (per map, nested):
Out of scope: frequencies & nav beacons (separate future work)
<DCS>/Mods/terrains/<Theatre>/Beacons.lua(+Scripts/World/Radio/BeaconSites.lua/BeaconTypes.lua)..luafiles offline, not runtime extraction → a separate, complementary track. Out of scope for this issue.Caveats / notes
getRunways()requires DCS 2.7+;getParking()has been marked WIP since 2.5.2 (structure may evolve).world.getAirbases()also returns ships and FARPs → filter ongetCategory().Namefield.Acceptance criteria / validation
tools/export_airbases.luaemits taggedDCSAB;…lines for airbases, runways, parking.convert.py(< 1 m vscoord.LOtoLL).References
world.getAirbases()— https://wiki.hoggitworld.com/view/DCS_func_getAirbasesAirbase.getRunways()— https://wiki.hoggitworld.com/view/DCS_func_getRunwaysAirbase.getParking()— https://wiki.hoggitworld.com/view/DCS_func_getParking