Weather METARs

Screenshots, descriptions and links to applications compiled with LB Booster, to illustrate how it is being used
User avatar
Posts: 63
Joined: Fri Apr 06, 2018 2:38 am
Location: Rome NY, USA

Weather METARs

Post by JackKelly »

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
WindowWidth = 1200: WindowHeight = 600
BackgroundColor$ = "191 191 255"

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"


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"
        #1.AirportCode Airport$
        goto [ProcessAirport]
end if
#1.AirportCode "!SetFocus"
#1.AirportCode "!SelectAll"

#1.Display "!cls"
#1.AirportCode "!Contents? AirportCode$"
for x=1 to len(AirportCode$)
    y$=mid$(AirportCode$, x, 1)
    if y$=>"A" and y$=<"Z" then z$=z$+y$
next x
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$

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)

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$=AddLeadingZeros$(UTC.hour, 2) + AddLeadingZeros$(UTC.min, 2)

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 ";

MetarWord$=word$(Metar$, MetarWordNumber)
if MetarWord$="AUTO" then
    #1.Display "(automated reporting)"
    MetarWord$=word$(Metar$, MetarWordNumber)
    if MetarWord$="COR" then
        #1.Display "(corrected)"
        MetarWord$=word$(Metar$, MetarWordNumber)
        #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]"
    #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 ";
    #1.Display "Wind from "; WindDirection$; " degrees (true) ";
end if

#1.Display "at "; WindSpeed$; " knots";
if Gusting then
    Gusting$=mid$(WW1$,Gusting+1, 2)
    #1.Display ", gusting to "; Gusting$;
end if
#1.Display ""

MetarVisibilityPosEnd=instr(Metar$, "SM ")-1
Visibility$="": x$=""
do until x$="T"
    x$=mid$(Metar$, MetarVisibilityPos, 1)
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)"

MetarSkyPos=instr(Metar$, "SM ")+3
SkyCondition$=right$(Metar$, len(Metar$)-MetarSkyPos+1)
for x=1 to 10
    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
next x

dim SkyCondition$(10): x=1: y=FirstCC
    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

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$=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)
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$=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)"

#1.Display "!Origin 0 0"
#1.AirportCode "!SetFocus"
#1.AirportCode "!SelectAll"

sub Quit
    close #1
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

end function

function AddLeadingZeros$(Number, Length)
    for x=1 to ZerosNeeded
    next x
end function