mirror of
https://github.com/OpenSquawk/OpenSquawk
synced 2026-08-10 19:36:04 +08:00
feat(live-atc): drive ATIS letter, runway, QNH and wind from the resolved report
The broadcast and the flow variables used to read different sources: the audio came from VATSIM while `information` was genATIS() — a random A-Z letter — and the runway came from genRunway(), a hard-coded list unrelated to the airport. So the pilot could hear "information Q" and be expected to call "information T". Both now read the same AtisReport. The frequency list backfills the letter and broadcast text from it, so an ATIS station always announces an information letter even when no VATSIM controller is online. QNH and surface wind come from the same observation, so the controller stops contradicting the ATIS. A synthesised ATIS is labelled "Simulated ATIS" in the frequency picker rather than being credited to VATSIM. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -55,6 +55,10 @@ export interface AtisReport {
|
||||
runwayDep: string | null
|
||||
runwayArr: string | null
|
||||
runwaySource: 'atis' | 'wind' | 'main' | null
|
||||
/** Observed QNH, so the flow quotes the same pressure the ATIS just read. */
|
||||
qnhHpa: number | null
|
||||
/** Observed surface wind as "260/14", or null without an observation. */
|
||||
surfaceWind: string | null
|
||||
observedAt: string | null
|
||||
stations: AtisStation[]
|
||||
}
|
||||
@@ -382,6 +386,8 @@ export function resolveAtisReport(input: ResolveAtisInput): AtisReport {
|
||||
runwaySource: fromText.dep || fromText.arr
|
||||
? 'atis'
|
||||
: byWind.dep?.source ?? byWind.arr?.source ?? null,
|
||||
qnhHpa: observation?.qnhHpa ?? null,
|
||||
surfaceWind: formatSurfaceWind(observation),
|
||||
observedAt,
|
||||
stations: liveStations,
|
||||
}
|
||||
@@ -413,6 +419,8 @@ export function resolveAtisReport(input: ResolveAtisInput): AtisReport {
|
||||
runwayDep: dep?.designator ?? null,
|
||||
runwayArr: arr?.designator ?? null,
|
||||
runwaySource: dep?.source ?? arr?.source ?? null,
|
||||
qnhHpa: observation?.qnhHpa ?? null,
|
||||
surfaceWind: formatSurfaceWind(observation),
|
||||
observedAt,
|
||||
stations: [{
|
||||
frequency: silentStation?.frequency || input.atisFrequency || FREQUENCY_UNKNOWN,
|
||||
@@ -424,6 +432,20 @@ export function resolveAtisReport(input: ResolveAtisInput): AtisReport {
|
||||
}
|
||||
}
|
||||
|
||||
/** Surface wind in the "260/14" form the flows use, or null when unobserved. */
|
||||
function formatSurfaceWind(observation: MetarObservation | null): string | null {
|
||||
if (!observation || observation.windKt === null) return null
|
||||
const direction = observation.windVariable
|
||||
? 'VRB'
|
||||
: observation.windDeg === null ? null : pad3(observation.windDeg)
|
||||
if (direction === null) return null
|
||||
return `${direction}/${pad2(observation.windKt)}`
|
||||
}
|
||||
|
||||
function pad3(value: number): string {
|
||||
return String(value).padStart(3, '0')
|
||||
}
|
||||
|
||||
/**
|
||||
* Absolute time for a METAR day-of-month stamp, resolved against `now`.
|
||||
* A report near a month boundary belongs to the previous month.
|
||||
|
||||
Reference in New Issue
Block a user