From 25de89c5f545b89d7d4b3173df3cff01513885cf Mon Sep 17 00:00:00 2001 From: leubeem Date: Wed, 20 May 2026 18:16:32 +0200 Subject: [PATCH] fix: correct OpenAIP v2 airport frequency parsing (3 bugs) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The frequency panel was always empty when using OpenAIP because of three compounding bugs in frequencies.get.ts: 1. Wrong query parameter — ?icao=EDDF performs an unfiltered full-text search and returns all 46 000+ airports paginated; EDDF wasn't even on the first page. The correct parameter is ?search=EDDF, which returns exactly the one matching airport. 2. Wrong ICAO field name — the code checked airport.icao but the real field in the v2 API response is icaoCode. Even on a correctly filtered response the match would always fail. 3. Wrong frequency field names and numeric types — each frequency item exposes the MHz value in a value field (not frequency / frequencyMHz), and the service type is a numeric code (5=Delivery, 9=Ground, 14=Tower, 15=ATIS) rather than a string. Added OPENAIP_TYPE_MAP to translate these numeric codes to the internal DEL/GND/TWR/ATIS codes the rest of the pipeline expects. With these fixes EDDF now returns all 8 frequencies (Delivery 122.035, Ground 121.805, three Tower variants, two ATIS) which are then stored in delivery_freq / ground_freq / tower_freq / atis_freq and used for both the frequency overview panel and the per-state frequency validation. Co-Authored-By: Claude Sonnet 4.6 --- server/api/airports/[icao]/frequencies.get.ts | 54 +++++++++++++------ 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/server/api/airports/[icao]/frequencies.get.ts b/server/api/airports/[icao]/frequencies.get.ts index fb9e696..5d9e90e 100644 --- a/server/api/airports/[icao]/frequencies.get.ts +++ b/server/api/airports/[icao]/frequencies.get.ts @@ -174,11 +174,27 @@ export default defineEventHandler(async (event): Promise => { console.warn('[OpenSquawk] Failed to fetch VATSIM frequencies:', err) } + // OpenAIP v2 numeric frequency type → our internal type code. + // Derived from real API responses (EDDF: 5=Delivery, 9=Ground, 14=Tower, 15=ATIS). + const OPENAIP_TYPE_MAP: Record = { + 4: 'GND', // Ground (some older entries) + 5: 'DEL', // Clearance Delivery + 6: 'APP', // Approach + 7: 'DEP', // Departure + 8: 'CTR', // Centre / ACC + 9: 'GND', // Ground + 14: 'TWR', // Tower + 15: 'ATIS', // ATIS + } + const { openaipApiKey } = getServerRuntimeConfig() if (openaipApiKey) { try { + // Must use `search` (not `icao`) — the `icao` param does a full-text search + // across all fields and returns the entire 46k-airport dataset unpaged. + // `search=` returns exactly the matching airport. const openaipData: any = await $fetch('https://api.core.openaip.net/api/airports', { - query: { icao }, + query: { search: icao }, headers: { Accept: 'application/json', 'x-openaip-api-key': openaipApiKey @@ -187,22 +203,28 @@ export default defineEventHandler(async (event): Promise => { const items = Array.isArray(openaipData?.items) ? openaipData.items : [] for (const airport of items) { - if ((airport?.icao || '').toUpperCase() !== icao) continue - const freqCollections = [airport?.frequencies, airport?.radio, airport?.communication] - .filter(Array.isArray) as any[][] + // Real field is `icaoCode`, not `icao` + if ((airport?.icaoCode || '').toUpperCase() !== icao) continue + // Frequencies live under `airport.frequencies[]`; each item has: + // value (MHz string, e.g. "122.035") + // type (numeric code, e.g. 5 for Delivery) + // name (human label, e.g. "FRANKFURT DELIVERY") + const freqItems: any[] = Array.isArray(airport?.frequencies) ? airport.frequencies : [] - for (const collection of freqCollections) { - for (const freqItem of collection) { - const frequency = normalizeFrequency(freqItem?.frequency ?? freqItem?.frequencyMHz ?? freqItem?.frequency_mhz) - if (!frequency) continue - const { type, label } = toTypeLabel(freqItem?.type, freqItem?.description || freqItem?.name) - addFrequencyEntry(frequencyMap, { - type, - label, - frequency, - source: 'openaip' - }) - } + for (const freqItem of freqItems) { + // Real field is `value`, not `frequency` / `frequencyMHz` + const frequency = normalizeFrequency(freqItem?.value ?? freqItem?.frequency) + if (!frequency) continue + + const numericType: number | undefined = typeof freqItem?.type === 'number' ? freqItem.type : undefined + const typeCode = numericType !== undefined ? (OPENAIP_TYPE_MAP[numericType] ?? 'UNK') : 'UNK' + const { type, label } = toTypeLabel(typeCode, freqItem?.name || freqItem?.description) + addFrequencyEntry(frequencyMap, { + type, + label, + frequency, + source: 'openaip' + }) } }