Weather, Runway in Use & ATIS
pkg/nav holds the navigation side of an airport environment: navigation data, weather and ATIS, flight plans. This page covers weather, the runway in use and the ATIS.
import "github.com/mrlm-net/simconnect/pkg/nav"
| Type / function | What it is |
|---|---|
Weather |
Surface weather: wind, gusts, visibility, ceiling, temperature, dewpoint, QNH, precipitation |
WeatherReader |
Reads the ambient weather at the user aircraft from your message loop |
StaticWeather |
Weather set by the application (tests, fixed scenarios) |
ActiveRunways |
Departure and arrival runway ends for the wind, with an approach hint |
RunwaySelector |
Keeps the runway in use until it is out of limits or another has been better for a while |
ATIS |
One broadcast: Text() with digits, Spoken() spelled for a voice |
ATISService |
Keeps the current ATIS and advances its letter on significant changes |
Reading the weather
WeatherReader follows the airport.Loader pattern: it never reads client.Stream() itself. Call Request (once) or Subscribe (every second, only when changed), and pass every message to Handle:
wx := nav.NewWeatherReader(client, 7400, 7401) // data definition ID, request ID
wx.Request()
for msg := range client.Stream() {
if w, ok := wx.Handle(msg); ok {
fmt.Printf("wind %03.0f°T %.0f kt, QNH %.0f\n", w.WindDirTrue, w.WindKts, w.QNHhPa)
}
}
It reads these SimVars of the user aircraft (SIMCONNECT_OBJECT_ID_USER):
| SimVar | Unit | Weather field |
|---|---|---|
AMBIENT WIND DIRECTION |
degrees (true) | WindDirTrue |
AMBIENT WIND VELOCITY |
knots | WindKts |
AMBIENT VISIBILITY |
meters | VisibilityM |
AMBIENT TEMPERATURE |
celsius | TempC |
SEA LEVEL PRESSURE |
millibars | QNHhPa |
AMBIENT PRECIP STATE |
mask | Precip (none, rain, snow) |
AMBIENT IN CLOUD |
bool | InCloud |
Limitation: SimConnect gives the ambient weather where the user aircraft is, not per airport. That is the airport’s weather while the user is on the ground there or close by, which is the case when the airport is the world centre around the user; for other airports it is only an approximation. Gusts, ceiling and dewpoint have no SimVar: the reader leaves GustKts and CeilingFt at 0 and DewpointC NaN, and the ATIS leaves them out. Set them yourself (or build the whole Weather with StaticWeather) when you have them from elsewhere.
The same Weather sets the spacing on final: traffic.ConditionsFrom(weather, runwayHeadingTrue) turns it into approach conditions (low visibility, runway surface, headwind), see Weather on final.
Runway in use
lim := nav.RunwayLimits{
Preferred: []string{"24", "06"}, // LKPR's preferential runways
// MaxTailwindKts: 5 (default), MaxCrosswindKts: 25 (default), MinLengthM: 0
}
use := nav.ActiveRunways(layout, w, lim)
fmt.Println(use.Departure.Name, use.Arrival.Name, use.Approach)
For every runway end of the layout (at least MinLengthM long), the headwind and crosswind components come from the end’s true heading and the true wind; the limits are checked with the gusts when they are stronger than the mean wind. Then:
- The first
Preferredend within the limits wins, even when another end has more headwind. That is how preferential runway systems work: LKPR keeps 24 in calm wind and with up to 5 kt of tailwind. - Otherwise the end with the most headwind among those within the limits, ties (within 1 kt) going to the longer runway.
- When no end is within the limits, the one with the most headwind is taken and
WithinLimitsis false.
PreferredArrival gives arrivals their own preference list, for split operations (RunwayUse.Single() is then false). Approach is ApproachILS when visibility is below 5000 m or the ceiling below 1500 ft, else ApproachVisual (“visual/RNAV”); pick the actual procedure from airport.Procedures.
Parallel runways used together
With parallel runways (headings within 15°), ActiveRunways also uses the parallels of the chosen end that are in the same direction and within the wind limits. RunwayUse.Departures and Arrivals list every end in use; Departure and Arrival are the first of them. Parallel says how they work together, and SpacingM is the distance between their centre lines.
The mode follows the spacing (ParallelModeFor). The figures are from the ICAO draft manual on simultaneous operations on parallel instrument runways (AN-Conf/11-IP/3):
| Spacing | Mode | Use |
|---|---|---|
| under 760 m | ParallelNone |
one runway, for wake turbulence (2.3.3.2) |
| 760 m | ParallelSegregated |
arrivals on one runway, departures on the other (departures side by side from 760 m, 3.3.2) |
| 915 m | ParallelDependent |
both runways mixed; approaches dependent, 2 NM diagonally between adjacent finals (2.3.1.1, 2.3.2.2) |
| 1035 m | ParallelIndependent |
both runways mixed; each final on its own (the Annex 14 distance) |
Runways that cross are never used together. A third parallel joins only if it is far enough from both. RunwayLimits.Parallel sets an airport’s own mode, never more than the spacing allows; for example ParallelSegregated for an airport that keeps one runway for arrivals, or ParallelNone for one runway only. Nearest(layout, ends, p) is the parallel nearest a point, such as a stand. The ATIS names every runway in use: “runways in use 26L and 26R”, or “landing runway 27R, departure runway 27L”.
Keeping the runway in use
An airport does not change runways with every wind shift. RunwaySelector keeps the runway in use until one of two things happens:
- the runway in use is out of its tailwind or crosswind limits, gusts included (
MaxTailwindKts,MaxCrosswindKts); - another runway has been the better choice for
ChangeAfter(RunwayChangeAfter, 10 min).
var sel nav.RunwaySelector // one per airport, kept
use := sel.Choose(time.Now(), layout, weather, limits)
While it holds, the headwind and crosswind it reports are those of the runway kept. The airport map uses a selector per airport for traffic and for the Airport panel. Near a tailwind limit in light, variable wind, the runway had flipped between 06 and 24 from one minute to the next.
ATIS
svc := nav.NewATISService("Ruzyne", layout, lim, 5000, nav.ATISWithMagVar(procs.MagVar))
a, changed := svc.Update(w, time.Now())
if changed {
fmt.Println(a.Text())
speak(a.Spoken())
}
Text() gives the broadcast in ICAO phraseology:
Ruzyne information Alpha, time 1320, runway in use 24, wind 240 degrees 8 knots, visibility 10 kilometers or more, temperature 15, dewpoint 8, QNH 1013, transition level 70, advise on initial contact you have information Alpha.
Spoken() spells the numbers for a voice library: digits one by one with “niner”, whole hundreds and thousands as words and runway suffixes as words:
Ruzyne information Alpha, time one three two zero, runway in use two four, wind two four zero degrees eight knots, visibility one zero kilometers or more, temperature one five, dewpoint eight, QNH one zero one three, transition level seven zero, advise on initial contact you have information Alpha.
The broadcast includes, when known: split landing/departure runways, “expect ILS approach”, gusts (10 kt or more above the mean wind), visibility in kilometers from 5 km and in meters below, rain or snow, the ceiling, and a negative temperature as “minus”. The wind is reported magnetic when a magnetic variation is given, in the facility data convention of airport.Procedures.MagVar (magnetic = true + MagVar; LKPR 356).
QNH and transition level. QNH is rounded down to a whole hPa. TransitionLevel(ta, qnh) is the lowest flight level in tens at least 1000 ft above the transition altitude at that QNH (27 ft per hPa); at LKPR (TA 5000 ft) that is FL 60 from QNH 1014 up, FL 70 from 1013 down to 977, FL 80 below.
Letters. ATISService starts at Alpha (ATISWithLetter changes it) and wraps from Zulu to Alpha. Update issues the next letter when:
- the departure or arrival runway, the approach hint, the QNH or the transition level changes;
- the wind turns 60° or more with 10 kt or more, or the wind or gust speed changes by 10 kt or more;
- visibility crosses 800, 1500, 3000 or 5000 m, or precipitation starts or stops;
- the broadcast is older than
DefaultATISMaxAge(1 hour;ATISWithMaxAge).
The runway in use holds through wind shifts near a limit: the service keeps a RunwaySelector, or shares the traffic’s with ATISWithSelector(sel), so the ATIS says the runway the traffic uses (#454).
Smaller changes keep the current broadcast, as a real ATIS does between reports.
Example
examples/atis connects, reads the weather at the user aircraft, loads the airport layout and prints the ATIS once:
go run ./examples/atis -icao LKPR -name Ruzyne -prefer 24,06 -ta 5000 -magvar 356
Ruzyne information Alpha, time 1646, runway in use 24, wind 120 degrees 6 knots, visibility 10 kilometers or more, temperature 21, QNH 1023, transition level 60, advise on initial contact you have information Alpha.
With the wind from 120° at 6 kt the preferred 24 has 3.6 kt of tailwind, within the 5 kt limit, so it stays in use.
Icing
IcingConditions(w) reports weather in which departures need de-icing: at or below IcingMaxTempC (+3 °C) with visible moisture — precipitation, visibility below IcingVisibilityM (1500 m) or cloud at the aircraft. See De-icing.