Every hour or so, hundreds of weather stations throughout the world, mostly at airports, send data to a public server run by NOAA, an agency of the US government. Most of the stations are at least partially automated. Anyone can then access the current data from a station but the coding is bewildering. This program decodes the most commonly used codes and reports them in an understandable way.
Updated 11/9/23 for vertical visibility
Code: Select all
' Airport Weather Information, 11/11/23
Version$="1.1.2"
NoMainWin
WindowWidth = 1200: WindowHeight = 600
BackgroundColor$ = "191 191 255"
Today$=date$("mm/dd/yyyy")
TodaysDate$=mid$(Today$,4,2)
Tomorrow=date$("days")+1
Tomorrow$=date$(Tomorrow)
TomorrowsDate$=mid$(Tomorrow$,4,2)
Statictext #1.Debug "", 0, 0, 500, 30
Textbox #1.AirportCode 50, 50, 200, 30
Statictext #1.Text "", 70, 85, 200, 30
TextEditor #1.Display 50, 150, 1100, 400
Button #1.Default "Enter", [ProcessAirport], UL, 280, 45, 70, 40
Button #1.Quit "Quit", Quit, UL, 380, 45, 70, 40
Statictext #1.Version "v. " + Version$, 1110, 552, 100, 15
open "AIRPORT WEATHER IFORMATION (USA & Canada Only)" for dialog as #1
#1 "Font ariel 14"
#1.Version "!Font ariel 10"
#1 "TrapClose Quit"
[Top]
'Airport$="KMGJ"
'Airport$="TEST"
TestMETAR$="TEST 310325Z COR 05012G18KT 300V100 M1 3/4SM R30L/5000VP6000FT -SN XYFG VV007 M24/M30 A2993" 'maximum
'TestMETAR$="KRME 060953Z AUTO 00000KT 10SM OVC080 02/M01 A3019" 'normal
'TestMETAR$="PANC 090553Z 00000KT 1/2SM R07R/4500VP6000FT SN FG VV007 01/00 A2882" ' vertical visibility
'TestMETAR$="CYYZ 112100Z 14004KT 360V170 15SM FEW030 OVC038 03/M02 A3043" 'north wind direction
if Airport$="" then
#1.Text " Enter Airport Code"
else
#1.AirportCode Airport$
goto [ProcessAirport]
end if
#1.AirportCode "!SetFocus"
#1.AirportCode "!SelectAll"
wait
[ProcessAirport]
TestRun=0
#1.Display "!cls"
#1.AirportCode "!Contents? AirportCode$"
AirportCode$=upper$(AirportCode$)
z$=""
for x=1 to len(AirportCode$)
y$=mid$(AirportCode$, x, 1)
if y$=>"A" and y$=<"Z" then z$=z$+y$
next x
AirportCode$=left$(z$,4)
if AirportCode$="TEST" then
if TestMETAR$<>"" then Metar$=TestMETAR$: TestRun=1: goto [TestEntry] else goto [Top]
end if
if len(AirportCode$)=3 then AirportCode$="K"+AirportCode$
if len(AirportCode$)<3 then goto [Top]
CD$=left$(AirportCode$, 1) 'Weather Station Area Designator
if CD$<>"K" and CD$<>"C" and CD$<>"P" and CD$<>"T" then goto [Top]
#1.AirportCode AirportCode$
URL$="http://tgftp.nws.noaa.gov/data/observations/metar/stations/"+AirportCode$+".TXT"
FileName$="METAR.txt"
ReturnCode=DownloadToFile(URL$, FileName$)
if ReturnCode<>0 then #1.Display "No METAR for Airport Code "; AirportCode$: goto [Bottom]
open FileName$ for input as #f
line input #f METAR$
close #f
MetarStart=instr(METAR$, AirportCode$)
MetarEnd=instr(METAR$, " RMK ")
Metar$=mid$(METAR$, MetarStart, MetarEnd-MetarStart)
#1.Display date$("mmmm d, yyyy"); " at "; left$(time$("hh:mm"), 5)
struct SYSTEMTIME,_
Year as short, Month as short,_
DayOfWeek as short, Day as short,_
Hour as short, Minute as short, Second as short,_
Milliseconds as short
CallDLL #kernel32, "GetSystemTime",_
SYSTEMTIME as struct,_
ret as void
UTC.hour=SYSTEMTIME.Hour.struct
UTC.min=SYSTEMTIME.Minute.struct
UTC$=AddLeadingZeros$(UTC.hour, 2) + AddLeadingZeros$(UTC.min, 2)
[TestEntry]
MetarDate$=mid$(Metar$, 6, 2)
MetarTime$=mid$(Metar$, 8, 4)
#1.Display Metar$
#1.Display ""
#1.Display "Weather Information for "; AirportCode$; ", "; MetarDate$; " at "; MetarTime$; " zulu ";
[Wind]
MetarWordNumber=3
MetarWord$=word$(Metar$, MetarWordNumber)
if MetarWord$="AUTO" then
#1.Display "(automated reporting)"
MetarWordNumber+=1
MetarWord$=word$(Metar$, MetarWordNumber)
else
if MetarWord$="COR" then
#1.Display "(corrected)"
MetarWordNumber+=1
MetarWord$=word$(Metar$, MetarWordNumber)
else
#1.Display ""
end if
end if
if not(TestRun) then
if MetarDate$<>TodaysDate$ and MetarDate$<>TomorrowsDate$ then
#1.Display "*** WARNING - METAR not current ***"
end if
TD$=TimeDifference$(UTC$, MetarTime$)
if val(TD$)<2 then TD$="2"
#1.Display " ["; TD$; " minutes ago]"
else
#1.Display "*** TEST DATA ONLY ***"
end if
WW1$=MetarWord$ 'Metar Wind Word #1
WindSpeed$=mid$(WW1$, 4, 2)
WindDirection$=left$(WW1$, 3): WindDir=val(WindDirection$)
if WindSpeed$="00" then #1.Display "Wind calm": goto [Visibility]
WW2$=word$(Metar$, MetarWordNumber+1)
if mid$(WW2$, 4, 1)="V" then 'variable wind direction
FromDir=val(left$(WW2$, 3)): ToDir=val(right$(WW2$, 3))
#1.Display "Wind variable from "; FromDir; " to "; ToDir; " degrees ";
if FromDir=360 then FromDir=000
if FromDir>ToDir then ToDir+=360
if WindDir<FromDir then WindDir+=360
if WindDir>FromDir and WindDir<ToDir then
#1.Display "(median "; WindDirection$; ") ";
end if
x=instr(Metar$, WW2$)
Metar$="T "+right$(Metar$, len(Metar$)-x-len(WW2$))
goto [WindSpeed]
end if
if WindDirection$="VRB" then
#1.Display "Wind variable ";
else
#1.Display "Wind from "; WindDirection$; " degrees (true) ";
end if
[WindSpeed]
#1.Display "at "; WindSpeed$; " knots";
Gusting=instr(WW1$,"G")
if Gusting then
Gusting$=mid$(WW1$,Gusting+1, 2)
#1.Display ", gusting to "; Gusting$;
end if
#1.Display ""
[Visibility]
MetarVisibilityPosEnd=instr(Metar$, "SM ")-1
MetarVisibilityPos=MetarVisibilityPosEnd
Visibility$="": x$=""
do until x$="T"
x$=mid$(Metar$, MetarVisibilityPos, 1)
Visibility$=x$+Visibility$
MetarVisibilityPos-=1
loop
Visibility$=right$(Visibility$, len(Visibility$)-2)
if left$(Visibility$,1)="M" then Visibility$="Less than " + right$(Visibility$, len(Visibility$)-1)
#1.Display Visibility$; " mile visibility (statute miles)"
[GroundConditions]
MetarSkyPos=instr(Metar$, "SM ")+3
SkyCondition$=right$(Metar$, len(Metar$)-MetarSkyPos+1)
for x=1 to 10
y$=word$(SkyCondition$,x)
if x=1 and left$(y$,1)="R" and instr(y$,"/")>0 and right$(y$,2)="FT" then
Runway$=mid$(y$, 2, instr(y$,"/")-2)
#1.Display "*** WARNING - Reduced visibility on runway "; Runway$; " *** ["; y$; "]"
goto [BumpFOR]
end if
CC$=left$(y$,2) 'Cloud Condition Code
if CC$="CL" or CC$="SK" or CC$="FE" or CC$="SC" or CC$="BK" or CC$="OV" or CC$="VV" then
FirstCC=x: exit for
end if
select case y$
case "-DZ": #1.Display "light drizzle"
case "DZ": #1.Display "drizzle"
case "+DZ": #1.Display "heavy drizzle"
case "-RA": #1.Display "light rain"
case "RA": #1.Display "rain"
case "+RA": #1.Display "heavy rain"
case "-SHRA": #1.Display "light rainshowers"
case "SHRA": #1.Display "rainshowers"
case "+SHRA": #1.Display "heavy rainshowers"
case "-FZRA": #1.Display "light freezing rain"
case "FZRA": #1.Display "freezing rain"
case "+FZRA": #1.Display "heavy freezing rain"
case "-SN": #1.Display "light snow"
case "SN": #1.Display "snow"
case "+SN": #1.Display "heavy snow"
case "DRSN": #1.Display "drifting snow"
case "GR": #1.Display "hail"
case "GS": #1.Display "sleet"
case "UP": #1.Display "precipitation"
case "-FZFG": #1.Display "light freezing fog"
case "FZFG": #1.Display "freezing fog"
case "+FZFG": #1.Display "heavy freezing fog"
case "FG": #1.Display "fog"
case "BCFG": #1.Display "patches of fog"
case "BR": #1.Display "mist"
case "FU": #1.Display "smoke"
case "HZ": #1.Display "haze"
case "SQ": #1.Display "squalls"
case "TS": #1.Display "thunderstorm"
case "FC": #1.Display "funnel cloud"
case "VA": #1.Display "volcanic ash"
case else : #1.Display y$
end select
[BumpFOR]
next x
dim SkyCondition$(10): x=1: y=FirstCC
[Loop1]
SkyCondition$(x)=word$(SkyCondition$, y)
if instr(word$(SkyCondition$, y), "/")=0 then
x+=1: y+=1: goto [Loop1]
end if
TemperatureDewpoint$=SkyCondition$(x) 'save for later
[SkyConditions]
MinBroken=99999: MinOvercast=99999
#1.Display "Cloud Conditions: ";
for y=1 to x-1
'#1.Debug SkyCondition$(y);wait
Clouds$=left$(SkyCondition$(y), 2)
Altitude=val(right$(SkyCondition$(y),3))*100
Altitude$=trim$(using("##,### feet", Altitude))
select case Clouds$
case "CL": #1.Display "clear";
case "SK": #1.Display "clear";
case "FE": #1.Display "a few at "; Altitude$;
case "SC": #1.Display "scattered at "; Altitude$;
case "BK": #1.Display "broken at "; Altitude$;
MinBroken=min(Altitude, MinBroken)
case "OV": #1.Display "overcast at "; Altitude$;
MinOvercast=min(Altitude, MinOvercast)
case "VV" : #1.Display "vertical visibility "; Altitude$;
MinOvercast=min(Altitude, MinOvercast)
end select
if y=1 and Altitude<10000 and Altitude<>0 then #1.Display " (agl)";
if y<x-1 then #1.Display ", ";
next y
#1.Display ""
Ceiling=min(MinBroken, MinOvercast)
MaxReportedCeiling=10000
if Ceiling<MaxReportedCeiling then #1.Display " [Ceiling is "; trim$(using("##,### feet", Ceiling)); "]"
[Temperature] 'TemperatureDewpoint$=SkyCondition$(x)
Dewpoint$=mid$(TemperatureDewpoint$, instr(TemperatureDewpoint$, "/")+1)
Temperature$=left$(TemperatureDewpoint$, len(TemperatureDewpoint$)-len(Dewpoint$)-1)
if right$(Dewpoint$,2)="00" then Dewpoint$="zero"
if left$(Dewpoint$,1)="M" then left$(Dewpoint$,1)="-"
if right$(Temperature$,2)="00" then Temperature$="zero"
if left$(Temperature$,1)="M" then left$(Temperature$,1)="-"
#1.Display "Temperature "; Temperature$; " (celsius), dewpoint "; Dewpoint$
#1.Display " ["; trim$(using("+###", (val(Temperature$)*9/5+32))); " farenheit]"
[Pressure]
Pressure$=right$(Metar$, 5)
#1.Display "Altimeter "; right$(Pressure$, 4);
if left$(Pressure$, 1)="A" then #1.Display " (in. hg)"
if left$(Pressure$, 1)="Q" then #1.Display " (mm. hg)"
[Bottom]
#1.Display "!Origin 0 0"
#1.AirportCode "!SetFocus"
#1.AirportCode "!SelectAll"
wait
sub Quit
close #1
end
end sub
function DownloadToFile(urlfile$, localfile$)
open "URLmon" for dll as #url
calldll #url, "URLDownloadToFileA",_
0 as long,_ 'null
urlfile$ as ptr,_ 'url to download
localfile$ as ptr,_ 'save file name
0 as long,_ 'reserved, must be 0
0 as long,_ 'callback address, can be 0
DownloadToFile as ulong '0=success
close #url
end function
function TimeDifference$(Time1$, Time2$)
Hour1$=left$(Time1$,2): Hour1=val(Hour1$)
Min1$= right$(Time1$,2): Min1=val(Min1$)
Hour2$=left$(Time2$,2): Hour2=val(Hour2$)
Min2$= right$(Time2$,2): Min2=val(Min2$)
if Hour1<Hour2 then Hour1+=24
if Min1<Min2 then Hour1-=1: Min1+=60
HourDiff=Hour1-Hour2: MinDiff=Min1-Min2
TotalDiff=(HourDiff*60)+MinDiff
TimeDifference$=str$(TotalDiff)
end function
function AddLeadingZeros$(Number, Length)
AddLeadingZeros$=str$(Number)
ZerosNeeded=Length-len(AddLeadingZeros$)
for x=1 to ZerosNeeded
AddLeadingZeros$="0"+AddLeadingZeros$
next x
end function