From 6f375aed92881da292fa9c5f9302002ccdcd6075 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sat, 14 Feb 2026 15:36:20 -0700 Subject: [PATCH] feat: Log unknown METAR parts with full string for debugging Co-authored-by: aider (gemini/gemini-2.5-pro) --- metar.html | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/metar.html b/metar.html index 86a7110..9d4f7df 100644 --- a/metar.html +++ b/metar.html @@ -60,8 +60,9 @@ // --- Decoding Functions --- - function decodeAirport(code) { + function decodeAirport(code, metarString) { if (code === 'CYYC') return "CYYC: Calgary International Airport"; + console.log(`Unknown Airport code: ${code} in METAR: ${metarString}`); return `${code}: Unknown Airport`; } @@ -89,7 +90,7 @@ return `${code}: Visibility ${vis} statute miles`; } - function decodeWeather(code) { + function decodeWeather(code, metarString) { const originalCode = code; const intensityMap = { '-': 'Light ', @@ -133,6 +134,7 @@ } else if (weatherMap[chunk]) { decodedChunks.push(weatherMap[chunk]); } else { + console.log(`Unknown weather chunk: ${chunk} in code: ${code} in METAR: ${metarString}`); decodedChunks.push(`unknown (${chunk})`); } if (precipitationTypes.includes(chunk)) { @@ -149,7 +151,7 @@ return `${originalCode}: ${decoding.trim()}${vicinity}`; } - function decodeClouds(code) { + function decodeClouds(code, metarString) { const coverMap = { 'SKC': 'Sky clear', 'CLR': 'Sky clear', @@ -160,7 +162,10 @@ }; const coverType = code.substring(0, 3); const cover = coverMap[coverType]; - if (!cover) return `${code}: Unknown cloud information`; + if (!cover) { + console.log(`Unknown cloud information: ${code} in METAR: ${metarString}`); + return `${code}: Unknown cloud information`; + } if (coverType === 'SKC' || coverType === 'CLR') { return `${code}: ${cover}`; @@ -188,7 +193,7 @@ return `SLP${pressure}: Sea-level pressure ${hpa.toFixed(1)} hPa`; } - function decodeCloudTypesRemark(code) { + function decodeCloudTypesRemark(code, metarString) { const cloudTypes = { 'CI': 'Cirrus', 'CC': 'Cirrocumulus', @@ -208,7 +213,11 @@ for (const match of matches) { const cloudCode = match[1]; const oktas = match[2]; - const cloudName = cloudTypes[cloudCode] || `Unknown cloud type (${cloudCode})`; + let cloudName = cloudTypes[cloudCode]; + if (!cloudName) { + cloudName = `Unknown cloud type (${cloudCode})`; + console.log(`Unknown cloud type in remark: ${cloudCode} in METAR: ${metarString}`); + } decodedParts.push(`${oktas}/8 ${cloudName}`); } @@ -230,7 +239,7 @@ return `${code}: ${weatherName} obscuring ${oktas}/8 of the sky`; } - function decodeRemarks(parts) { + function decodeRemarks(parts, metarString) { let decoded = ["RMK: Remarks"]; const weatherRegex = /^([+-]|VC)?(MI|PR|BC|DR|BL|SH|TS|FZ|DZ|RA|SN|SG|IC|PL|GR|GS|UP|BR|FG|FU|VA|DU|SA|HZ|PY|PO|SQ|FC|SS|DS|TR)+$/; const obscurationRegex = /^(FG|BR|FU|HZ|DU|SA)\d$/; @@ -263,7 +272,7 @@ } else if (obscurationRegex.test(part)) { decoded.push(` - ${decodeObscurationRemark(part)}`); } else if (part.match(/^([A-Z]{2}\d)+$/)) { - decoded.push(` - ${decodeCloudTypesRemark(part)}`); + decoded.push(` - ${decodeCloudTypesRemark(part, metarString)}`); } else if (part === 'VIRGA' && i + 1 < parts.length && directions[parts[i+1]]) { const direction = directions[parts[i+1]]; decoded.push(` - VIRGA ${parts[i+1]}: Virga to the ${direction.toLowerCase()}`); @@ -279,10 +288,11 @@ decoded.push(` - ${part} ${parts[i+1]}: ${cloudName} clouds are translucent (thin)`); i++; // Consume 'TR' part } else if (weatherRegex.test(part)) { - decoded.push(` - ${decodeWeather(part)}`); + decoded.push(` - ${decodeWeather(part, metarString)}`); } else if (part.match(/^(AC|CI|CC)/)) { decoded.push(` - ${part}: Cloud types and coverage details`); } else { + console.log(`Other remark: ${part} in METAR: ${metarString}`); decoded.push(` - ${part}: Other remark`); } } @@ -300,7 +310,7 @@ if (parts[index] && parts[index].length === 4 && parts[index].match(/^[A-Z][A-Z0-9]{3}$/)) { airportTimeAutoRaw.push(parts[index]); - airportTimeAutoDecoded.push(decodeAirport(parts[index])); + airportTimeAutoDecoded.push(decodeAirport(parts[index], metarString)); index++; } if (parts[index] && parts[index].endsWith('Z')) { @@ -331,9 +341,9 @@ } else if (part.endsWith('SM') || part.match(/^\d+$/) || part.match(/^\d\/\dSM$/)) { sections.push({ raw: part, decoded: decodeVisibility(part) }); } else if (part.match(/^([+-]|VC)?(MI|PR|BC|DR|BL|SH|TS|FZ|DZ|RA|SN|SG|IC|PL|GR|GS|UP|BR|FG|FU|VA|DU|SA|HZ|PY|PO|SQ|FC|SS|DS|TR)+$/)) { - sections.push({ raw: part, decoded: decodeWeather(part) }); + sections.push({ raw: part, decoded: decodeWeather(part, metarString) }); } else if (part.match(/^(SKC|CLR|FEW|SCT|BKN|OVC)/)) { - sections.push({ raw: part, decoded: decodeClouds(part) }); + sections.push({ raw: part, decoded: decodeClouds(part, metarString) }); } else if (part.match(/^(M?\d{2})\/(M?\d{2})$/)) { sections.push({ raw: part, decoded: decodeTempDew(part) }); } else if (part.startsWith('A') && part.length === 5 && !isNaN(part.substring(1))) { @@ -346,7 +356,7 @@ const remarkParts = parts.slice(rmkIndex + 1); sections.push({ raw: `RMK ${remarkParts.join(' ')}`, - decoded: decodeRemarks(remarkParts) + decoded: decodeRemarks(remarkParts, metarString) }); }