Generated: Tue Feb 2 17:54:41 2010 from geturl02.pl 2008/01/31 87.3 KB.
#!/perl -w # NAME: geturl02.pl # AIM: Get the text from a URL page ... # 22/01/2008 - geoff mclane # references viewed - SEE END OF FILE # Decodes built with LOTS of empirical testsing, and an account has been taken for # quite a number of EXCEPTIONS found in decoding the 2600 TAF from the NOAA site. use strict; use warnings; ###use Socket; ###use LWP::Simple; require 'logfile.pl' or die "Unable to load logfile.pl ...\n"; require 'tafacronyms.pl' or die "Unable to load tafacronyms.pl ...\n"; # log file stuff my ($LF); my $pgmname = $0; if ($pgmname =~ /\w{1}:\\.*/) { my @tmpsp = split(/\\/,$pgmname); $pgmname = $tmpsp[-1]; } my $outfile = "temp.$pgmname.txt"; open_log($outfile); prt( "$0 ... Hello, World ...\n" ); my $addorder = 1; my $minkey = 18; my $taf_indent = '# '; my $rem_indent = '#R '; my @aptlist = (); my $aptcnt = 0; my @taflist = (); my @exceptions = (); # This NEEDS to be adjusted to YOUR particular default location of these files. my $FGROOT = (exists $ENV{FG_ROOT}) ? $ENV{FG_ROOT} : "C:/FGCVS/FlightGear/data"; my $APTFILE = "$FGROOT/Airports/apt.dat.gz"; # the airports data file my $NAVFILE = "$FGROOT/Navaids/nav.dat.gz"; # the NAV, NDB, etc. data file my $testurl = 'http://weather.noaa.gov/pub/data/forecasts/taf/stations/LFPO.TXT'; my $testurl2 = 'http://weather.noaa.gov/pub/data/forecasts/taf/stations/XS50.TXT'; my $testtaf = 'KSFO 231456Z 03009KT 10SM FEW009 OVC020 06/04 A2999 RMK AO2 RAB25E33 SLP156 P0000 60003 T00610039 55000'; my $testtaf2 = '2008/01/09 04:09 NZWD TAF 090303 15022G32KT 0400 -SN BLSN OVC007 650079 520005 QNH2910INS GRID32022G32KT BECMG 1214 16017KT 1600 -SN BLSN BKN008 OVC012 620089 QNH2908INS GRID33017KT BECMG 1719 16015KT 3200 -SN SCT010 BKN010 OVC025 610159 QNH2912INS GRID33015KT BECMG 2301 16020KT 1200 -SN BLSN BKN008 OVC010 620089 510005 QNH2915INS GRID33020KT'; my $testtaf3 = '2007/09/26 09:43 TAF AMD TAF AMD CYKF 260943Z 261004 23012KT P6SM SCT015 BKN025 TEMPO 1014 6SM -SHRA BR BKN008 OVC015 FM1400Z 31012KT P6SM SCT015 BKN025 TEMPO 1420 6SM -SHRA BR BKN015 RMK FCST BASED ON AUTO OBS. NXT FCST BY 14Z'; my $testtaf4 = '2008/01/25 03:02 TAF ANYN 250302Z 250606 03010KT 9999 -SHRA SCT018 SCT050 BKN140 T 30 28 26 25 Q 1005 1006 1005 1004='; my $testtaf5 = 'TAF KPIT 091730Z 091818 15005KT 5SM HZ FEW020 WS010/31022KT FM1930 30015G25KT 3SM SHRA OVC015 TEMPO 2022 1/2SM +TSRA OVC008CB FM0100 27008KT 5SM SHRA BKN020 0VC040 PROB40 0407 1SM -RA BR FM1015 18005KT 6SM -SHRA OVC020 BECMG 1315 P6SM NSW SKC'; my $testtaf6 = 'DFFD.TXT 2008/01/28 16:00 TAF DFFD 281600Z 281818 06010KT 8000 NSC TEMPO 0610 2000 HZ= TAF DFOO 281600Z 281818 06008KT CAVOK TEMPO 0610 2500 HZ'; my $testtaf7 = 'VIPK.TXT 2008/01/09 03:00 VIAR/VIJU/VICG/VIPK 090615 15005KT 3000 HZ SCT030 SCT100 BEC 0810 27006KT 4000 HZ BEC 1315 24005KT 3000 HZ TEMPO 0615 1200 TSRA FEW030CB'; my $testtaf8 = 'AEZS.TXT 2007/12/24 10:00 TAF SAEZS 241000Z 241212 12008KT 5000 BRDZ FEW010 SCT015 BECMG 1618 14010KT 9999 NSC TEMPO 0811 0800 BCFG BKN001'; my $testtaf9 = 'AAWE.TXT 2007/09/01 04:00 TAF SAAWE 010400Z SCT030 BKNB070 BEDCMG 1518 34015KT BKN020 BKN040 TERMPO 1824 03010KT'; my $testtaf10 = 'KSFO 291756Z 13003KT 10SM -RA SCT027 OVC034 07/04 A3031 RMK AO2 RAB52 SLP262 P0001 60001 T00720044 10072 20044 53010='; my $testtaf11 = 'EDDF 250450Z 18004KT 4800 BR SCT018 BKN032 00/00 Q1014 8829//95 NOSIG'; my $testtaf12 = 'YSSY 301644Z 301818 35008KT 9999 FEW012 FM23 04015KT 9999 FEW040 FM08 18020G30KT 9999 -SHRA SCT012 BKN020 PROB30 INTER 0212 3000 TSRA SCT015 SCT050CB INTER 1218 4000 SHRA BKN010'; my $testtaf13 = 'LEAL.TXT 2008/01/28 17:00 TAF LEAL 281700Z 290024 VRB04KT CAVOK TX18/13Z TN02/06Z'; # I think TX is maximum temp, and TN is minimum temp forecast my $testtaf14 = 'AMDA.TXT 2008/01/16 17:00 TAF AMDA 161700Z 170024 10004KT 6000 SCT018 BECMG 0103 32010KT 7000 TEMPO 0407 5000 RA FEW017CB BKN018 BECMG 1012 10005KT 6000'; my $testtaf15 = 'DRRB.TXT 2007/06/30 12:00 301200Z METAR DRRB 301200Z 28004KT 9/9 FEW040 BKN300 35/22 QNIL'; my $testtaf16 = 'DGKT.TXT 2007/12/20 09:00 TAF DGKT 201212 ?0010KT 9999 SCP016 TEMPO F?W028CB B?CMG 1820 VRB05KT SCT009 TEMPO 0406 4000 BR FM 0900 22005KT 9999 SCT014'; my $testtaf17 = 'DITY.TXT 2008/01/28 07:30 TAF VALIDITY 280730Z 282109 --------------------------- WAMG 00000KT 9000 SCT018 BECMG 0103 24010KT TEMPO 0609 4000 RA FEW017CB'; my $testtaf18 = 'BNAA.TXT 2007/12/30 04:45 TAF DBNAA 300445Z 300606 06005KT 6000 NSW NSC BECMG1218 12006KT CAVOK'; my $testtaf19 = 'ECMG.TXT 2007/09/15 21:00 VAAH 15210Z 160024 29004KT 3000 FEW020 BECMG 040008KT 6000 FEW020 SCT025 BECVG 1315 00000KT 5000 HZ FEW020 SCT080 ) BECMG 1820 4000 HZ'; my $testtaf20 = 'DXFG.TXT 2007/10/11 15:30 DXFG 111530Z 111818 00000KP 999) FEW013 PROB30 TEMPO 1820 TS SCT013 FEW023CB TEMPO 1418 36010KT TS SCT016 ?EW0?3CB'; my $testtaf21 = 'BGTL.TXT 2008/01/28 11:03 TAF TAF BGTL 281111 09009KT 9999 FEW030 QNH2978INS TEMPO 1806 13016KT 9999 -SHSN FEW015 BKN025 510003 TM21/03Z TM26/13Z'; # debugging stuff my $dodebug = 1; my $dotest = 0; my $showall = 1; # show each entry decoded my $verbose = 0; my $beginatt = 0; my $endattaf = 100; if ($dodebug) { $dotest = 1; $showall = 1; ##$verbose = 1; } my $acttaf = ''; my @warnings = (); #my $cannedtaf = 'taf20080125.txt'; my $cannedtaf = 'taf20080128.txt'; # apt.dat.gz CODES - see http://x-plane.org/home/robinp/Apt810.htm for DETAILS my $aln = '1'; # airport line my $rln = '10'; # runways/taxiways line my $sealn = '16'; # Seaplane base header data. my $heliln = '17'; # Heliport header data. my $twrln = '14'; # Tower view location. my $rampln = '15'; # Ramp startup position(s) my $bcnln = '18'; # Airport light beacons my $wsln = '19'; # windsock my $minatc = '50'; my $twrfrq = '54'; # like 12210 TWR my $appfrq = '55'; # like 11970 ROTTERDAM APP my $maxatc = '56'; my $lastln = '99'; # end of file ##load_airport_file($APTFILE); $aptcnt = scalar @aptlist; prt( "Got $aptcnt airports loaded ...\n" ); for (my $i = 1; $i <= $aptcnt; $i++) { show_airport($i); } if ($dotest) { #push(@taflist, $testtaf2); #push(@taflist, $testtaf2); #push(@taflist, $testtaf3); #push(@taflist, $testtaf4); #push(@taflist, $testtaf5); #push(@taflist, $testtaf6); #push(@taflist, $testtaf7); #push(@taflist, $testtaf8); #push(@taflist, $testtaf9); #push(@taflist, $testtaf10); #push(@taflist, $testtaf11); #push(@taflist, $testtaf12); #push(@taflist, $testtaf13); #push(@taflist, $testtaf14); #push(@taflist, $testtaf15); #push(@taflist, $testtaf16); #push(@taflist, $testtaf17); #push(@taflist, $testtaf18); #push(@taflist, $testtaf19); #push(@taflist, $testtaf20); push(@taflist, $testtaf21); prt( "Doing TEST of ".scalar @taflist." TAF records ...\n" ); } else { ###fetch_url( $testurl ); if (open TF, "<$cannedtaf") { @taflist = <TF>; close TF; prt( "Doing ".scalar @taflist." TAF records from $cannedtaf file ...\n" ); } else { prt( "WARNING: Can NOT locate file $cannedtaf ...\n" ); ###push(@taflist, $testtaf2); } } process_taf_list(); if (@warnings) { prt( "\nWARNINGS FOUND ".scalar @warnings." ...\n" ); foreach my $warn (@warnings) { prt( "$warn\n" ); } prt("\n"); } else { prt( "No warnings listed ...\n\n" ); } if (@exceptions) { prt( "List of ".scalar @exceptions." Exceptions ...\n" ); prt( "my \@tafexceptions = qw(\n" ); my $wrap = 0; foreach my $ex (@exceptions) { prt( "$ex " ); $wrap++; if ($wrap > 6) { prt( "\n" ); $wrap = 0; } } prt( ");\n" ); } close_log($outfile,1); exit(0); sub process_taf_list { my ($ttcnt, $dcnt, @tafarr, $tafcnt, $t); $ttcnt = scalar @taflist; prt("Processing list of $ttcnt TAF entries ... (begin=$beginatt, end=$endattaf, showall=". ($showall ? 'Yes' : 'No'). ", verbose=".($verbose ? "Yes" : "No").")\n"); $dcnt = 0; $ttcnt = 0; foreach my $taf (@taflist) { $ttcnt++; if ($endattaf > 0) { if ($ttcnt < $beginatt) { next; } if ($ttcnt > $endattaf) { last; } } chomp $taf; if ($taf =~ /=/) { @tafarr = split(/=/,$taf); $tafcnt = scalar @tafarr; for ($t = 0; $t < $tafcnt; $t++) { $taf = trim_all($tafarr[$t]); if ((length($taf) > 12) && ($taf =~ /^\S+\s+\S+\s+\S+\s+/)) { $dcnt += show_taf( $taf, $ttcnt ); } } } else { $dcnt += show_taf($taf, $ttcnt); } } prt( "\nShown $dcnt TAF entries ...\n" ); } sub fetch_url_NOT_USED { # see gettaf01.pl my ($url) = shift; prt( "Fetching: $url\n" ); my $txt = get($url); if ($txt && length($txt)) { prt( "$txt\n" ); my $taftxt = $txt; $taftxt =~ s/\n/ /gm; $taftxt =~ s/\r/ /gm; $taftxt =~ s/\t/ /gm; $taftxt = trim_all($taftxt); prt( "$taftxt\n" ); push(@taflist,$taftxt); } else { prt( "FAILED to get URL $url ...\n" ); } } sub show_remarks { my ($hrem, $rem) = @_; my $cnt = scalar keys(%{$hrem}); my $msg = "REMARKS: $cnt [$rem]"; prt( "$msg ...\n" ); my ($mg); foreach my $key (sort keys( %{$hrem} )) { my $hash = $$hrem{$key}; ##prt( " $key: $hash\n" ); $msg = "$key "; $msg = substr($msg,4) if ($addorder); $msg = "$rem_indent$msg"; $msg .= '.' while (length($msg) < $minkey); $msg .= ': '; if (ref($hash) eq "HASH") { $mg = ''; foreach my $k (keys %{$hash}) { my $v = ${%{$hash}}{$k}; $mg .= ', ' if length($mg); $mg .= "$k=$v"; } } else { $mg = $hash; } $msg .= $mg; prt( "$msg\n" ); } } sub show_taf { my ($taftxt, $tnum) = @_; my $msg = "DECODE:$tnum: $taftxt"; my $mg = ''; my $rmk = ''; my %metar = decode_metar($taftxt); if ($showall || (defined $metar{'999.UNDECODED'}) || (defined $metar{'UNDECODED'})) { prt( "\n$msg\n" ); foreach my $key (sort keys(%metar)) { my $hash = $metar{$key}; #if (($key eq 'wind')||($key eq 'visibility')) { # ||($key eq 'clouds')) $msg = "$key "; $msg = substr($msg,4) if ($addorder); $msg = "$taf_indent$msg"; $msg .= '.' while (length($msg) < $minkey); $msg .= ': '; if (ref($hash) eq "HASH") { $mg = ''; foreach my $k (keys %{$hash}) { my $v = ${%{$hash}}{$k}; $mg .= ', ' if length($mg); $mg .= "$k=$v"; } } else { #$mg = $metar{$key}; $mg = $hash; } $msg .= $mg; prt( "$msg\n" ); if ($key =~ /remarks/) { $rmk .= ' ' if (length($rmk)); $rmk .= $mg; } } if (length($rmk)) { my %remarks = decode_remarks($rmk); show_remarks(\%remarks, $rmk); } return 1; } return 0; } sub add_2_exceptions { my ($pt) = shift; foreach my $ex (@exceptions) { if ($pt eq $ex) { return 0; } } push(@exceptions,$pt); return 1; } sub number_format { my ($num, $len) = @_; my $rnum = $num; if ($len == 0) { $rnum = int($num); } else { my $val = 10; my $n = $len - 1; while ($n) { $val = $val * 10; $n--; } $rnum = (int($num * $val)) / $val; } return $rnum; } sub get_descriptor { my ($desc) = shift; # '(MI|PR|BC|DR|BL|SH|TS|FZ)?' = Descriptor if ($desc eq 'MI') { return 'MI (Shallow)'; } elsif ($desc eq 'BC') { return 'BC (Patches)'; } elsif ($desc eq 'PR') { return 'PR (Partial)'; } elsif ($desc eq 'TS') { return 'TS (Thunderstorm)'; } elsif ($desc eq 'BL') { return 'BL (Blowing)'; } elsif ($desc eq 'SH') { return 'SH (Showers)'; } elsif ($desc eq 'DR') { return 'DR (Drifting)'; } elsif ($desc eq 'FZ') { return 'FZ (Freezing)'; } return "$desc (CHECKME)"; } sub get_obscuration { my ($obs) = shift; if ($obs eq 'BR') { return 'BR (Mist >= 5/8SM)'; } elsif ($obs eq 'FG') { return 'FG (Fog < 5/8SM)'; } elsif ($obs eq 'FU') { return 'FU (Smoke)'; } elsif ($obs eq 'VA') { return 'VA (Volcanic Ash)'; } elsif ($obs eq 'SA') { return 'SA (Sand)'; } elsif ($obs eq 'HZ') { return 'HZ (Haze)'; } elsif ($obs eq 'PY') { return 'PY (Spray)'; } elsif ($obs eq 'DU') { return 'DU (Widespread dust)'; } ###return "$obs (CHECKME)"; return get_precipitation($obs); } sub get_precipitation { my ($prc) = shift; if ($prc eq 'DZ') { return 'DZ (Drizzle)'; } elsif ($prc eq 'RA') { return 'RA (Rain)'; } elsif ($prc eq 'SN') { return 'SN (Snow)'; } elsif ($prc eq 'SG') { return 'SG (Snow grains)'; } elsif ($prc eq 'IC') { return 'IC (Ice crystals)'; } elsif ($prc eq 'PL') { return 'PL (Ice pellets)'; } elsif ($prc eq 'GR') { return 'GR (Hail)'; } elsif ($prc eq 'GS') { return 'GS (Small hail/snow pellets)'; } elsif ($prc eq 'UP') { return 'UP (Unknown precipitation in automated observations)'; } elsif ($prc eq 'BRDZ') { return 'BRDZ (Mist/Drizzle)'; } return "$prc (CHECKME)"; } sub get_other { my ($oth) = shift; if ($oth eq 'SQ') { return 'SQ (Squall)'; } elsif ($oth eq 'SS') { return 'SS (Sandstorm)'; } elsif ($oth eq 'DS') { return 'DS (Duststorm)'; } elsif ($oth eq 'PO') { return 'PO (Well developed)'; } elsif ($oth eq 'FC') { return 'FC (Funnel cloud)'; } elsif ($oth eq '+FC') { return '+FC (tornado/waterspout)'; } ### ??? dust/sand whirls return "$oth (CHECKME)"; } sub get_weather { my ($wth) = shift; my $rwth = get_obscuration($wth); if ($rwth =~ /CHECKME/) { $rwth = get_descriptor($wth); if ($rwth =~ /CHECKME/) { $rwth = get_other($wth); } } return $rwth; } sub prtw { my ($mg, $tf) = @_; if ($tf ne $acttaf) { $acttaf = $tf; push(@warnings,$tf); prt("\nDECODE: $tf\n"); } prt($mg); chomp $mg; push(@warnings,$mg); } sub get_next_hr { my ($ref, $href) = @_; my $tmphr = $ref; my $num = 0; my $ic = ''; if ($addorder) { my @tarr = keys %{$href}; my $fnd = 0; foreach $ic (@tarr) { if ($ic =~ /\.$tmphr$/) { $fnd = 1; last; } } while ($fnd) { $num++; $tmphr = $ref.$num; $fnd = 0; foreach $ic (@tarr) { if ($ic =~ /\.$tmphr$/) { $fnd = 1; last; } } } } else { while ( defined $href->{$tmphr} ) { $num++; $tmphr = $ref.$num; } } return $tmphr; } sub no_icao_found { my ($href) = shift; if ($addorder) { my @tarr = keys %{$href}; foreach my $ic (@tarr) { if ($ic =~ /\.icao$/) { return 0; } } } else { if (defined $$href{'icao'}) { return 0; } } return 1; } sub decode_remarks { my ($tt) = shift; # TEMPO 2022 = TEMPOrary: changes expected for < 1 hour and in # total, < half of 2-digit hour beginning and 2-digit hour ending time period # PROB40 0407 = PROBability and 2-digit percent (30 or 40): probable # condition during 2-digit hour beginning and 2-digit hour ending time period # BECMG 1315 = BECoMinG: change expected during 2-digit # hour beginning and 2-digit hour ending time period prt( "\nDECODE OF:$tt\n" ) if ($verbose); my @parts = split(/\s/, $tt); my $num_parts = scalar @parts; my %decoded_remarks = (); my ($part, $i2, $v1, $v2, $v3, $v4, $v5, $v6, $info, $tr, $cord, $msg, $npart, $tpart); my $excnum = 0; for (my $i = 0; $i < $num_parts; $i++) { $part = $parts[$i]; $cord = sprintf("%03d", $i); $i2 = $i + 1; $msg = $part; ###prt( "START PART $part\n" ); $npart = ($i2 < $num_parts) ? $parts[$i2] : ''; ### very special EXCEPTIONS found ### $part = 'BECMG' if (($part eq 'B?CMG')||($part eq 'BECMG?')||($part eq 'RECMG')); if (($part eq 'FM') && ($npart =~ /^\d{4}$/)) { $part = "FM$npart"; $i = $i2; $i2 = $i + 1; } # F?W028CB $part =~ s/F\?W/FEW/; $part = 'BECMG' if ($part eq 'BECVG'); if ($part eq ')') { next; # just ignore it } $part = '9999' if ($part eq '999)'); if ($part =~ /^FUW(\d{3}.*)$/) { $part =~ s/^FUW/FEW/; } if ($part =~ /^\?EW/) { $part =~ s/^\?EW/FEW/; $part =~ s/\?/0/; } # GRID15006KT if ($part =~ /^GRID(\d{5}KT)$/) { $part = $1; } $part = 'TEMPO' if ($part eq 'TERMPO'); ### end special exceptions ### $msg .= ":$part" if ($msg ne $part); $msg .= ' '; ###################################################################### ### THE MAIN DECODE TUMBLE ### ###################################################################### if ($part =~ /^TEMPO/) { $info = 'No begin, end hours!'; if ($i2 < $num_parts) { $npart = $parts[$i2]; # get 2-digit hour begin, 2-digit hour end # or exception TEMPO 082020 1100 if ($npart =~ /^(\d{2})(\d{2})(\d{2})?$/ ) { $v1 = $1; $v2 = $2; $v3 = $3; if ($v3 && length($v3)) { $info = "Day:$v3, Begin:$v1, End:$v2"; } else { $info = "Begin:$v1, End:$v2"; } $i = $i2; $i2 = $i + 1; if ($i2 < $num_parts) { $npart = $parts[$i2]; # visability if ($npart =~ /^(\d{4})$/ ) { $v1 = $1; $info .= ", Vis. $v1 meters"; $i = $i2; $i2 = $i + 1; } } } } $tr = get_next_hr('temporary', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $decoded_remarks{$tr} = $info; $msg .= "$tr, $info"; } elsif ($part =~ /^PROB(\d{1,2})/ ) { $v1 = $1; $info = "Percentage $v1"; if ($i2 < $num_parts) { $part = $parts[$i2]; # get 2-digit hour begin, 2-digit hour end if ($part =~ /(\d{2})(\d{2})/ ) { $v1 = $1; $v2 = $2; $info .= " Begin:$v1, End:$v2"; $i = $i2; } } $tr = get_next_hr('probability', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $decoded_remarks{$tr} = $info; $msg .= "$tr, $info"; } elsif ($part =~ /^BEC(MG)?(\d{4})?$/ ) { $v1 = $1; $v2 = $2; $npart = ''; ###prt( "R$i2 - DOING $part ...\n" ); $info = "No times given."; if ( $v2 && ($v2 =~ /(\d{2})(\d{2})/) ) { # has number attached $v3 = $1; $v4 = $2; $info = "BeginH:$v3, EndH:$v4"; } else { if ($i2 < $num_parts) { $npart = $parts[$i2]; # get 2-digit hour begin, 2-digit hour end if ($npart =~ /^(\d{2})(\d{2})$/ ) { $v1 = $1; $v2 = $2; $info = "BeginH:$v1, EndH:$v2"; $i = $i2; } } } $tr = get_next_hr('becoming', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $decoded_remarks{$tr} = $info; $msg .= "$tr, $info"; # BECMG 1820 4000 if ($i == $i2) { # if BECMG followed by BEGIN/END HOURS, then $i2 = $i + 1; if ($i2 < $num_parts) { # mayb also followed by VISIBILITY $npart = $parts[$i2]; # get vis., if any if ($npart =~ /^(\d{4}|9\/9)$/ ) { $v1 = $1; $i = $i2; $cord = sprintf("%03d", $i); $tr = get_next_hr('visibility', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $info = "$tr - Vis. $v1 meters."; if ($v1 eq '0000') { # Special low value $v2 = '-1 (less than)'; $v3 = 50; $v4 = 0.05; $v5 = 164; $v6 = 0.031; } elsif (($v1 eq '9999')||($v1 eq '9/9')) { # Special high value $v2 = '1 (greater than)'; $v3 = 10000; $v4 = 10; $v5 = 32800; $v6 = 6.2; } else { # Normal visibility, returned in both small and large units. $v2 = '0 (measured)'; $v3 = number_format($v1, 1); $v4 = number_format($v1 / 1000, 1); $v5 = number_format($v1 * 3.28084, 1); $v6 = number_format($v1 / 1609.344, 1); } #$decoded_remarks{$tr}{'visibility'} = "$v1 meters"; $decoded_remarks{$tr}{'prefix'} = $v2; $decoded_remarks{$tr}{'meter'} = $v3; $decoded_remarks{$tr}{'km'} = $v4; $decoded_remarks{$tr}{'ft'} = $v5; $decoded_remarks{$tr}{'mile'} = $v6; $i2 = $i + 1; $msg .= ", $tr, $info"; } } $i2 = $i + 1; } } elsif ($part =~ /^INTER$/) { # INTER 0212 3000 TSRA SCT015 SCT050CB INTER 1218 4000 SHRA BKN010 # INTER 0153/0453 6000 $info = "INTER - no times!"; if ($i2 < $num_parts) { $part = $parts[$i2]; # get 2-digit hour begin, 2-digit hour end if ($part =~ /^(\d{2})(\d{2})\/?(\d{2})?(\d{2})?$/ ) { $v1 = $1; $v2 = $2; $info = "BeginH:$v1, EndH:$v2"; $i = $i2; } elsif ($part =~ /^(\d{2})(\d{2})\/(\d{2})(\d{2})$/ ) { $v1 = $1; $v2 = $2; $v3 = $3; $v4 = $4; $info = "BeginH:$v1:$v2, EndH:$v3:$v4"; $i = $i2; # update to next token } } $tr = get_next_hr('intermediate', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $decoded_remarks{$tr} = $info; $msg .= "$tr, $info"; if ($i == $i2) { $cord = sprintf("%03d", $i); $i2 = $i + 1; if ($i2 < $num_parts) { $part = $parts[$i2]; # get 4-digit visibility if ($part =~ /^\d{4}$/) { $tr = get_next_hr('visibility', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $info = "$part meters."; $decoded_remarks{$tr} = $info; $i = $i2; $i2 = $i + 1; $msg .= ", $tr, $info"; } } } ###} elsif ($part =~ /^NSW$/) { ### # NWS TAFs exclude turbulence, icing & temperature forecasts; ### # NWS METARs exclude trend fcsts ### $tr = get_next_hr('nsw', \%decoded_remarks); ### $tr = "$cord.$tr" if ($addorder); ### $decoded_remarks{$tr} = 'exclude trend fcst'; ### $msg .= "$tr, $info"; } elsif ($part =~ /^[MP]?(([0-9]?)[ ]?([0-9])(\/?)([0-9]*))SM$/ ) { # Examples: # 1/2SM - Visibility one-half statute mile # 2 1/4SM - Visibility two and one-quarter statute miles # 5SM - Visibility five statute miles # P6SM - Visibility more than six statute miles $v1 = $1; $v2 = $2; $v3 = $3; $v4 = $4; $tr = get_next_hr('visibility', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $info = "$1 $v2 $v3 $v4 statute miles."; $decoded_remarks{$tr} = $info; $msg .= "$tr, $info"; } elsif (($part eq 'SKC') || ($part eq 'CLR') || ($part eq 'NSC')) { $v1 = $part; $tr = get_next_hr('conditions', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $info = "$v1 (Clear)"; $decoded_remarks{$tr} = $info; $msg .= "$tr, $info"; } elsif ($part =~ /^T([0-9]{2})\/([0-9]{2})Z$/) { # T07/18Z $v1 = $1; $v2 = $2; $tr = get_next_hr('time', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $info = "T HOUR/MINUTES Z. $v1,$v2"; $decoded_remarks{$tr}{'hour'} = $v1; $decoded_remarks{$tr}{'mins'} = $v2; if ($i2 < $num_parts) { $npart = $parts[$i2]; if ($npart =~ /([0-9]{2})([0-9]{2})([0-9]{2})/) { $v1 = $1; $v2 = $2; $v3 = $3; $tr = get_next_hr('time2', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $msg .= " - $tr"; $decoded_remarks{$tr}{'day'} = $v1; $decoded_remarks{$tr}{'begin-hour'} = $v2; $decoded_remarks{$tr}{'end-hour'} = $v3; $i = $i2; $i2 = $i + 1; } } $msg .= "$tr, $info"; } elsif ($part =~ /^([0-9]{3})V([0-9]{3})$/) { $v1 = $1; $v2 = $2; $tr = get_next_hr('wind', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $info = "Variable wind-direction"; $decoded_remarks{$tr}{'var_beg'} = $1; $decoded_remarks{$tr}{'var_end'} = $2; $msg .= "$tr, $info"; } elsif ($part =~ /^(VC)?(-|\+)?(MI|PR|BC|DR|BL|SH|TS|FZ)?((DZ|RA|SN|SG|IC|PL|GR|GS|UP|BR)+)?(BR|FG|FU|VA|DU|SA|HZ|PY)?(PO|SQ|FC|SS)?$/ ) { # } elseif (ereg('^(VC)?' . /* Proximity */ # '(-|\+)?' . /* Intensity */ # '(MI|PR|BC|DR|BL|SH|TS|FZ)?' . /* Descriptor */ # '((DZ|RA|SN|SG|IC|PL|GR|GS|UP)+)?' . /* Precipitation */ # '(BR|FG|FU|VA|DU|SA|HZ|PY)?' . /* Obscuration */ # '(PO|SQ|FC|SS)?$', /* Other */ $v1 = $1; $v2 = $2; $v3 = $3; $v4 = $4; $v5 = $5; $v6 = $6; $tr = get_next_hr('weather', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $info = "prox."; if ($v1 && length($v1)&& ($v1 eq 'VC')) { $info .= " outside (5-10km)" } else { $info .= ' at'; } $info .= ' aerodrome'; $info .= ', inten.'; if ($v2 && length($v2)) { if ($v2 eq '-') { $info .= ' light (-)'; } elsif ($v2 eq '+') { $info .= ' heavy (+)'; } else { $info .= 'unknown ($v2) CHECKME'; } } else { # no sign $info .= ' moderate (no sign)'; } $info .= ', desc. '.get_descriptor($v3) if ($v3 && length($v3)); $info .= ', precip. '.get_precipitation($v4) if ($v4 && length($v4)); $info .= ', obscur. '. get_obscuration($v5) if ($v5 && length($v5)); $info .= ', other '.get_weather($v6) if ($v6 && length($v6)); $decoded_remarks{$tr} = $info; $msg .= "$tr, $info"; } elsif ($part =~ /^(VV|FEW|SCT|BKN|OVC|0VC)([0-9]{2,3}|\/\/\/)(CB|TCU)?$/ ) { $v1 = $1; $v2 = $2; $v3 = $3; $tr = get_next_hr('clouds', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $info = 'altitude feet '; if (($v2 eq '000')||($v2 eq '00')) { # if ($regs[2] == '000') # '000' is a special height. $v4 = 100; $v5 = 30; $info .= $v4; #$decoded_remarks{$tr}{'prefix'} = -1; #/* Less than */ } elsif ($v2 eq '///') { # '///' means height nil $v4 = 'nil'; $v5 = $v4; $info .= $v4; } else { $v4 = $v2 * 100; $v5 = int($v2 * 30.48); $info .= $v4; } $decoded_remarks{$tr}{'feet'} = $v4; $decoded_remarks{$tr}{'meter'} = $v5; $npart = ''; if ($v1 && length($v1)) { $npart = $v1; $tpart = get_acro_desc($v1); if ($tpart ne 'NONE') { $npart .= " $tpart"; } } $npart .= " $v3" if ($v3 && length($v3)); $decoded_remarks{$tr}{'conditions'} = $npart; $info .= ", $npart"; if ($i2 < $num_parts) { $npart = $parts[$i2]; if ($npart =~ /([0-9]{2})([0-9]{2})([0-9]{2})/) { $v1 = $1; $v2 = $2; $v3 = $3; $i = $i2; $cord = sprintf("%03d", $i); $tr = get_next_hr('clouds2', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $msg .= " - $tr"; $decoded_remarks{$tr}{'item1'} = $v1; $decoded_remarks{$tr}{'item2'} = $v2; $decoded_remarks{$tr}{'item3'} = $v3; $info .= ", 6-digit CHECKME $npart"; $i2 = $i + 1; if ($i2 < $num_parts) { $npart = $parts[$i2]; if ($npart =~ /([0-9]{2})([0-9]{2})([0-9]{2})/) { $v1 = $1; $v2 = $2; $v3 = $3; $i = $i2; $cord = sprintf("%03d", $i); $tr = get_next_hr('clouds3', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $msg .= " - $tr"; $decoded_remarks{$tr}{'item1'} = $v1; $decoded_remarks{$tr}{'item2'} = $v2; $decoded_remarks{$tr}{'item3'} = $v3; $info .= ", 6-digit CHECKME $npart"; $i2 = $i + 1; } } } } $msg .= "$tr, $info"; } elsif ($part =~ /^FM([0-9]{2})([0-9]{2})/) { $v1 = $1; $v2 = $2; # eg FM1930 = FroM and 2-digit hour and 2-digit minute # beginning time: indicates significant change. # Each FM starts on a new line, indented 5 spaces. $tr = get_next_hr('time-range', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $info = "Hour=$v1, Min=$v2"; $decoded_remarks{$tr}{'begin-hour'} = $v1; $decoded_remarks{$tr}{'begin-mins'} = $v2; $msg .= "$tr, $info"; } elsif ($part =~ /([0-9]{3}|VRB)([0-9]{2,3})G?([0-9]{2,3})?(KT|MPS|KMH)/) { # Wind Group = 20006KT # Examples: # 18010KT - Wind one eight zero at one zero knots # 35012G20KT - Wind three five zero at one two gust two zero knots # 00000KT - Wind calm # VRB16G28KT - Wind variable at one six gust two eight knots $v1 = $1; # direction, or VRB = variable $v2 = $2; # wind speed $v3 = $3; # gusts, if any $v4 = $4; # units $tr = get_next_hr('wind', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); ###prt( "R$i2 - DOING $part ...\n" ); $decoded_remarks{$tr}{'deg_true'} = $v1; # $tr = 'wind'.[nn] $info = "deg $v1, knots="; if ($v2 == 0) { $v1 = 0; $v5 = 0; $v6 = 0; } else { if ($v4 eq 'KT') { $v1 = "$v2"; # The windspeed measured in meters per second, rounded to one decimal place # $meterspersec = number_format($value * 0.5144, 1); $v5 = number_format( ($v2 * 0.5144), 1 ); # The windspeed measured in miles per hour, rounded to one decimal place $v6 = number_format( ($v2 * 1.1508), 1 ); } elsif ($v4 eq 'MPS') { # The windspeed measured in meters per second */ $v5 = number_format( $v2, 1 ); # The windspeed measured in knots, rounded to one decimal place $v1 = number_format($v2 / 0.5144, 1); # The windspeed measured in miles per hour, rounded to one decimal place $v6 = number_format($v2 / 0.5144 * 1.1508, 1); } elsif ($v4 eq 'KMH') { # The windspeed measured in kilometers per hour $v5 = number_format($v2 * 1000 / 3600, 1); # mps $v1 = number_format($v2 * 1000 / 3600 / 0.5144, 1); # knots # The windspeed measured in miles per hour, rounded to one decimal place $v6 = number_format(($v2 * 1000 / 3600 / 0.5144) * 1.1508, 1); } else { $v1 = "$v2 (KTS assumed!)"; $v5 = number_format( ($v2 * 0.5144), 1 ); $v6 = number_format( ($v2 * 1.1508), 1 ); $info .= "(KTS Assumed) "; } } $info .= "$v1"; $decoded_remarks{$tr}{'knots'} = $v1; $decoded_remarks{$tr}{'meters_per_second'} = $v5; $decoded_remarks{$tr}{'miles_per_hour'} = $v6; ###prt( "R$i2 - DONE $part ...\n" ); # get VISIBILITY after WIND DIRECTION/SPEED if ($i2 < $num_parts) { $npart = $parts[$i2]; # get 4-digit visibility (meters) (I GUESS!) if ($npart =~ /^(\d{4}|9\/9)$/ ) { $v1 = $1; $i = $i2; $cord = sprintf("%03d", $i); $tr = get_next_hr('visibility', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $info .= " - $tr - Vis. $v1 meters."; if ($v1 eq '0000') { # Special low value $v2 = '-1 (less than)'; $v3 = 50; $v4 = 0.05; $v5 = 164; $v6 = 0.031; } elsif (($v1 eq '9999')||($v1 eq '9/9')) { # Special high value $v2 = '1 (greater than)'; $v3 = 10000; $v4 = 10; $v5 = 32800; $v6 = 6.2; } else { # Normal visibility, returned in both small and large units. $v2 = '0 (measured)'; $v3 = number_format($v1, 1); $v4 = number_format($v1 / 1000, 1); $v5 = number_format($v1 * 3.28084, 1); $v6 = number_format($v1 / 1609.344, 1); } #$decoded_remarks{$tr}{'visibility'} = "$v1 meters"; $decoded_remarks{$tr}{'prefix'} = $v2; $decoded_remarks{$tr}{'meter'} = $v3; $decoded_remarks{$tr}{'km'} = $v4; $decoded_remarks{$tr}{'ft'} = $v5; $decoded_remarks{$tr}{'mile'} = $v6; $i2 = $i + 1; ###prt( "R$i2 - DONE $npart ...\n" ); } } $msg .= "$tr, $info"; } elsif ( $part =~ /^QNH([0-9]{4})INS/ ) { $v1 = $1; $tr = get_next_hr('altimeter', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $info = 'inhg '.number_format($v1 / 100, 2); $decoded_remarks{$tr}{'inhg'} = number_format($v1 / 100, 2); # $tr = 'altimeter'.[nn] $decoded_remarks{$tr}{'mmhg'} = number_format($v1 * 0.254, 2); # 100 inch to mm $decoded_remarks{$tr}{'hpa'} = number_format($v1 * 0.338639, 2); $decoded_remarks{$tr}{'atm'} = number_format(($v1 * 3.3421e-4), 3); $msg .= "$tr, $info"; } elsif ($part eq 'RMK') { $info = "$part (ReMarK)"; $i++; for ( ;$i < $num_parts; $i++) { $part = $parts[$i]; $info .= " $part"; } $tr = get_next_hr('remarks', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $decoded_remarks{$tr} = $info; $msg .= "$tr, $info"; } elsif ($part =~ /^AMD(.*)$/) { $v1 = $1; $tr = get_next_hr('qualifier', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $info = "Qualifier of report."; if ($v1 && length($v1)) { $npart = "Amendment $v1"; # $tr = 'qualifier'; } else { $npart = 'Amended'; } if ($i2 < $num_parts) { $part = $parts[$i2]; # get 2-digit hour, 2 digit minutes (I GUESS!) if ($part =~ /(\d{2})(\d{2})/ ) { $v1 = $1; $v2 = $2; $npart .= " H:$v1 M:$v2"; $info .= " H:$v1 M:$v2"; $i = $i2; $i2 = $i + 1; } } $decoded_remarks{$tr} = $npart; $msg .= "$tr, $info"; } elsif ($part =~ /^(TX|TN|T)(M)?(\d{2})\/(\d{2})Z$/) { # TX18/13Z TN02/06Z'; # TM21/03Z TM26/13Z # I think TX is maximum temp, and TN is minimum temp forecast $v1 = $1; $v2 = $2; $v3 = $3; $v4 = $4; if ($v1 eq 'TX') { $npart = 'max_temp_c'; } elsif ($v1 eq 'TN') { $npart = 'min_temp_c'; } else { $npart = 'temp_c'; } if ($v3 && ($v2 eq 'M')) { $v3 *= -1; } $tr = get_next_hr('temperature', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $msg .= " - Max/Min Temperature."; $msg .= " - $tr"; $decoded_remarks{$tr}{$npart} = "$v3 at $v4 hours."; } else { if (length($part) && (substr($part,0,1) eq '(') ) { # begin bracket open - go until closed $npart = $part; if ( !($npart =~ /\)$/) ) { while ($i2 < $num_parts) { $npart .= ' '.$parts[$i2]; if ($npart =~ /\)$/) { last; } $i2++; } } if ( $npart =~ /\)$/) { $tr = get_next_hr('brackets', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $msg .= " - $npart"; $msg .= " - $tr"; $decoded_remarks{$tr} = $npart; # $tr = 'brackets'.[nn] $i = $i2; $part = ''; } } if (length($part)) { $npart = get_acro_desc($part); if ($npart ne 'NONE') { $tr = get_next_hr('acronym', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $msg .= " - $npart"; $msg .= " - $tr"; $decoded_remarks{$tr}{$part} = $npart; # $tr = 'acronym'.[nn] #while ($i2 < $num_parts) { # $part = $parts[$i2]; #} $msg .= " - acronym for $npart - $tr"; $part = ''; } } if (length($part) && ( is_in_taf_exceptions($part) || is_in_taf_spl_exceptions($part) || (length($part) == 1) ) ) { $excnum++; $tr = 'exception'; $tr = "998.$tr" if ($addorder); $decoded_remarks{$tr}{$excnum} = $part; $msg .= " - $tr:$excnum"; $part = ''; } if (length($part)) { $tr = get_next_hr('UNDECODED', \%decoded_remarks); $tr = "$cord.$tr" if ($addorder); $decoded_remarks{$tr} = $part; $msg = "WARNING:ERMK: [$part] NO CASE FOR THIS!"; prtw("$msg\n", $tt ); } } prt( "R$i2 = $msg\n" ) if ($verbose); } return %decoded_remarks; } sub all_hyphens { my ($tx) = shift; if ($tx =~ /[^-]/) { return 0; } return 1; } sub decode_metar { my ($tt) = shift; my ($part, $had_dc, $msg, $cord, $had_z); my ($i,$i2,$v1,$v2,$v3,$v4,$v5,$v6,$tr,$icao,$npart,$tpart); my @parts = split(/\s/, $tt); my $num_parts = scalar @parts; my %decoded_metar = (); my $excnum = 0; $part = ''; $had_dc = 0; $msg = ''; $had_z = 0; $i = 0; $part = $parts[$i]; $icao = ''; if ($part =~ /^(\w{4})\.TXT$/) { $icao = $1; $cord = sprintf("%03d", $i); $tr = 'file'; $tr = "$cord.$tr" if ($addorder); $msg = "$part - This is the FILE from the noaa site. ($icao)"; $msg .= " - $tr"; $decoded_metar{$tr} = "$part - ICAO=$icao"; prt( "$msg\n" ) if ($verbose); $i++; } for ( ; $i < $num_parts; $i++) { $part = $parts[$i]; $cord = sprintf("%03d", $i); $had_dc = 0; # no DECODE yet $i2 = $i + 1; $npart = ($i2 < $num_parts) ? $parts[$i2] : ''; $msg = $part; ### SOME EXCEPTIONS SEEN ### $part = 'BECMG' if (($part eq 'BEDCMG')||($part eq 'BUCMG')||($part eq 'BEC')); $part = 'TEMPO' if ($part eq 'TERMPO'); if ($part =~ /^BKNB([0-9]{2,3})$/ ) { $v1 = $1; $part = "BKN$v1"; } $part = 'BOOL' if ($part eq 'TAF?BOOL'); if (all_hyphens($part)) { $msg .= ' DISCARDED'; prt( "$msg\n" ) if ($verbose); $had_z = 0; # clear any ZULU time, after this discard next; # and LOOP } # GRID15006KT if ($part =~ /^GRID(\d{5}(G\d{2})?KT)$/) { $part = $1; } if (($part eq 'PROB')&&($npart =~ /^\d{2}$/)) { $part .= $npart; $i = $i2; $i2 = $i + 1; } if (($part eq 'FM')&&($npart =~ /^\d{4}$/)) { $part .= $npart; $i = $i2; $i2 = $i + 1; } # HVH677 if ($part =~ /^HVH(\d{3})/) { $part = substr($part,2); } ### END EXCEPTIONS SEEN ### $msg .= ":$part" if ($part ne $msg); ### THE DECODE TUMBLE ### # if (ereg('RMK|AFT|TEMPO|BECMG|INTER', $part)) #if ($part =~ /(RMK|AFT|TEMPO|BECMG|INTER)/) { if (($part =~ /^(RMK|AFT|TEMPO|TEMP|BECMG|INTER)(\d{4})?$/)||($part =~ /^BEC([0-9]{2,4})/) ) { $v1 = $1; $tr = 'remarks'; $tr = "$cord.$tr" if ($addorder); $msg .= " - The rest of the METAR is either a remark or temporary information."; $msg .= " - $tr"; $decoded_metar{$tr} = $part; $i++; for ( ; $i < $num_parts; $i++) { $part = $parts[$i]; $decoded_metar{$tr} .= ' '.$part; } $decoded_metar{$tr} = trim_all($decoded_metar{$tr}); $had_dc = 1; last; } elsif (($part eq 'METAR')||($part eq 'SPECI')||($part eq 'TAF')) { $tr = get_next_hr('type', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Type of Report: METAR, SPECI, TAF"; $msg .= " - $tr"; $decoded_metar{$tr} = $part; $had_dc = 1; $had_z = 0; # clear any ZULU time, after TYPE } elsif ($part =~ /^AMD$/) { $v1 = $1; $tr = get_next_hr('qualifier', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Qualifier of report."; $msg .= " - $tr"; if ($v1 && length($v1)) { $decoded_metar{$tr} = "Amendment $v1"; # $tr = 'qualifier'; } else { $decoded_metar{$tr} = 'Amended'; } $had_dc = 1; } elsif ($part =~ /^CAVOK(=)?/) { $v1 = $1; $msg .= " - indicates Ceiling And Visibility OKay"; # (no cloud below 5000 feet, a visibility of 6 Statute Miles or more and # no precipitation, thunderstorms, shallow fog, or low drifting snow) # = indicates the end of the METAR report if ($v1 && length($v1)) { $tr = 'end-report'; $tr = "$cord.$tr" if ($addorder); $decoded_metar{$tr} = $part.' (Clear, no cloud 5000 feet, vis 6 miles+, no rain ...)'; last; } else { $tr = get_next_hr('indication', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $decoded_metar{$tr} = $part.' (Clear, no cloud 5000 feet, vis 6 miles+, no rain ...)'; } $msg .= " - $tr"; $had_dc = 1; } elsif ( !$had_z && (($part =~ /^[A-Z]{4}$/)||($part =~ /^[A-Z]{1}\w{3}$/)) ) { $v1 = $part; $tr = get_next_hr('icao', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Station Identifier."; $decoded_metar{$tr} = $v1; # $tr = 'icao'.[nn] $msg .= " - $tr"; $had_dc = 1; } elsif ($part =~ /^AMD(.*)$/) { $v1 = $1; $tr = get_next_hr('qualifier', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Qualifier of report."; $msg .= " - $tr"; if ($v1 && length($v1)) { $decoded_metar{$tr} = "Amendment $v1"; # $tr = 'qualifier'; } else { $decoded_metar{$tr} = 'Amended'; } $had_dc = 1; } elsif ($part =~ /^([0-9]{2})([0-9]{2})([0-9]{1,2})Z$/) { # ZULU TIME - plus TIME following ... $v1 = $1; $v2 = $2; $v3 = $3; $tr = get_next_hr('time', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - DATE, HOUR, MINUTES (ZULU)."; $msg .= " - $tr"; $decoded_metar{$tr}{'day'} = $v1; $decoded_metar{$tr}{'hour'} = $v2; $decoded_metar{$tr}{'mins'} = $v3; # this is followed by, like # 260943Z 261004, 250302Z 250606, 281600Z 281818, 091730Z 091818 # but in one case - 121100Z 12221 - ASSUME 122201 # OR EVEN 181400Z 1815 if ($i2 < $num_parts) { $npart = $parts[$i2]; if ($npart =~ /^([0-9]{2})([0-9]{2})([0-9]{1,2})?$/) { $v1 = $1; $v2 = $2; $v3 = $3; #$tr = get_next_hr('time2', \%decoded_metar); #$tr = "$cord.$tr" if ($addorder); #$msg .= " - $tr"; $decoded_metar{$tr}{'day2'} = $v1; $decoded_metar{$tr}{'begin-hour'} = $v2; $decoded_metar{$tr}{'end-hour'} = $v3 if ($v3 && length($v3)); $i = $i2; $i2 = $i + 1; $msg .= ", plus $npart - BEGIN, END $v1, $v2, $v3"; } } $had_z = 1; $had_dc = 1; } elsif ($part =~ /^T([0-9]{2})\/([0-9]{2})Z$/) { # T07/18Z $v1 = $1; $v2 = $2; $tr = get_next_hr('time', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - T HOUR/MINUTES Z."; $msg .= " - $tr"; $decoded_metar{$tr}{'hour'} = $v1; $decoded_metar{$tr}{'mins'} = $v2; if ($i2 < $num_parts) { $npart = $parts[$i2]; if ($npart =~ /([0-9]{2})([0-9]{2})([0-9]{2})/) { $v1 = $1; $v2 = $2; $v3 = $3; $tr = get_next_hr('time2', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - $tr"; $decoded_metar{$tr}{'day'} = $v1; $decoded_metar{$tr}{'begin-hour'} = $v2; $decoded_metar{$tr}{'end-hour'} = $v3; $i = $i2; $i2 = $i + 1; $msg .= ", plus $npart - BEGIN, END $v1, $v2, $v3"; } } $had_z = 1; $had_dc = 1; } elsif ($part =~ /([0-9]{4})\/([0-9]{2})\/([0-9]{2})/) { $v1 = $1; $v2 = $2; $v3 = $3; $tr = get_next_hr('record_date', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - got a DATE YYYY/MM/DD"; $msg .= " - $tr"; $decoded_metar{$tr}{'year'} = $v1; $decoded_metar{$tr}{'month'} = $v2; $decoded_metar{$tr}{'day'} = $v3; if ($i2 < $num_parts) { $npart = $parts[$i2]; if ($npart =~ /([0-9]{2}):([0-9]{2})/) { $v1 = $1; $v2 = $2; $decoded_metar{$tr}{'hour'} = $v1; $decoded_metar{$tr}{'minutes'} = $v2; $i = $i2; $msg .= ", plus $npart - HOUR, MINUTES $v1, $v2"; } } $had_dc = 1; } elsif ($part =~ /(AUTO|COR|RTD|CC[A-Z]|RR[A-Z])/ ) { $v1 = $1; $tr = get_next_hr('report_mod', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Report Modifier: AUTO, COR, CCx or RRx"; $msg .= " - $tr"; $decoded_metar{$tr} = $v1; $had_dc = 1; ###} elsif ($part =~ /([0-9]{3}|VRB|\?00)([0-9]{2,3})G?([0-9]{2,3})?(KT|MPS|KMH)/) { ###} elsif ($part =~ /^([0-9]{3}|VRB|\?00)([0-9]{2,3})G?([0-9]{2,3})?(KT|MPS|KMH|KP)$/) { } elsif ($part =~ /^([0-9]{3}|VRB|\?00)([0-9]{1,3})G?([0-9]{2,3})?(KT|MPS|KMH|KP)$/) { # Wind Group = 20006KT # Examples: # 18010KT - Wind one eight zero at one zero knots # 35012G20KT - Wind three five zero at one two gust two zero knots # 00000KT - Wind calm # VRB16G28KT - Wind variable at one six gust two eight knots $v1 = $1; # direction, or VRB = variable $v2 = $2; # wind speed $v3 = $3; # gusts, if any $v4 = $4; # units $v4 = 'KT' if ($v4 eq 'KP'); # EXCEPTION SEEN $tr = get_next_hr('wind', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Wind Group "; $msg .= "(v1-4:"; $msg .= ($v1 && length($v1)) ? $v1 : '-'; $msg .= ','; $msg .= ($v2 && length($v2)) ? $v2 : '-'; $msg .= ','; $msg .= ($v3 && length($v3)) ? $v3 : '-'; $msg .= ','; $msg .= ($v4 && length($v4)) ? $v4 : '-'; $msg .= ')'; $msg .= " - $tr"; $decoded_metar{$tr}{'degs_true'} = $v1; # $tr = 'wind'.[nn] if ($v2 == 0) { $decoded_metar{$tr}{'knots'} = 0; $decoded_metar{$tr}{'meters_per_second'} = 0; $decoded_metar{$tr}{'miles_per_hour'} = 0; } else { if ($v4 eq 'KT') { $decoded_metar{$tr}{'knots'} = $v2; # The windspeed measured in meters per second, rounded to one decimal place # $meterspersec = number_format($value * 0.5144, 1); $decoded_metar{$tr}{'meters_per_second'} = number_format( ($v2 * 0.5144), 1 ); # The windspeed measured in miles per hour, rounded to one decimal place $decoded_metar{$tr}{'miles_per_hour'} = number_format( ($v2 * 1.1508), 1 ); } elsif ($v4 eq 'MPS') { # The windspeed measured in meters per second */ $decoded_metar{$tr}{'meters_per_second'} = number_format( $v2, 1 ); # The windspeed measured in knots, rounded to one decimal place $decoded_metar{$tr}{'knots'} = number_format($v2 / 0.5144, 1); # The windspeed measured in miles per hour, rounded to one decimal place $decoded_metar{$tr}{'miles_per_hour'} = number_format($v2 / 0.5144 * 1.1508, 1); } elsif ($v4 eq 'KMH') { # The windspeed measured in kilometers per hour $decoded_metar{$tr}{'meters_per_second'} = number_format($v2 * 1000 / 3600, 1); $decoded_metar{$tr}{'knots'} = number_format($v2 * 1000 / 3600 / 0.5144, 1); # The windspeed measured in miles per hour, rounded to one decimal place $decoded_metar{$tr}{'miles_per_hour'} = number_format($decoded_metar{'wind'}{'knots'} * 1.1508, 1); } else { $decoded_metar{$tr}{'knots'} = "$v2 (KTS assumed!)"; } } $decoded_metar{$tr}{'gusts'} = "$v3 $v4" if ($v3 && length($v3)); # Visability usually follows this wind speed and direction # get VISIBILITY after WIND DIRECTION/SPEED if ($i2 < $num_parts) { $part = $parts[$i2]; # get 4-digit visibility (meters) (I GUESS!) ###if ($part =~ /^(\d{4}|9\/9)$/ ) { if ($part =~ /^(\d{4}|9\/9|999\))$/ ) { $v1 = $1; $tr = get_next_hr('visibility', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= ", - $tr - Vis. $v1 meters."; $v1 = '9999' if ($v1 eq '999)'); # EXCEPTION SEEN if ($v1 eq '0000') { # Special low value $v2 = '-1 (less than)'; $v3 = 50; $v4 = 0.05; $v5 = 164; $v6 = 0.031; } elsif (($v1 eq '9999')||($v1 eq '9/9')) { # Special high value $v2 = '1 (greater than)'; $v3 = 10000; $v4 = 10; $v5 = 32800; $v6 = 6.2; } else { # Normal visibility, returned in both small and large units. $v2 = '0 (measured)'; $v3 = number_format($v1, 1); $v4 = number_format($v1 / 1000, 1); $v5 = number_format($v1 * 3.28084, 1); $v6 = number_format($v1 / 1609.344, 1); } $decoded_metar{$tr}{'prefix'} = $v2; $decoded_metar{$tr}{'meter'} = $v3; $decoded_metar{$tr}{'km'} = $v4; $decoded_metar{$tr}{'ft'} = $v5; $decoded_metar{$tr}{'mile'} = $v6; $i = $i2; $i2 = $i + 1; } } $had_dc = 1; } elsif ($part =~ /^([0-9]{3})V([0-9]{3})$/) { # !empty($decoded_metar['wind'])) { $v1 = $1; $v2 = $2; $tr = get_next_hr('wind', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Variable wind-direction"; $msg .= " - $tr"; $decoded_metar{$tr}{'var_beg'} = $1; $decoded_metar{$tr}{'var_end'} = $2; $had_dc = 1; } elsif ($part =~ /^([0-9]{4}|9\/9)([NS]?[EW]?)$/) { # or exception 04007KT 5000HZ $v1 = $1; $tpart = $2; $tr = get_next_hr('visibility', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Visibility in meters (4 digits)."; $msg .= " - $tr"; if ($v1 eq '0000') { # Special low value $v2 = '-1 (less than)'; $v3 = 50; $v4 = 0.05; $v5 = 164; $v6 = 0.031; } elsif (($v1 eq '9999')||($v1 eq '9/9')) { # Special high value $v2 = '1 (greater than)'; $v3 = 10000; $v4 = 10; $v5 = 32800; $v6 = 6.2; } else { # Normal visibility, returned in both small and large units. $v2 = '0 (measured)'; $v3 = number_format($v1, 1); $v4 = number_format($v1 / 1000, 1); $v5 = number_format($v1 * 3.28084, 1); $v6 = number_format($v1 / 1609.344, 1); } #$decoded_remarks{$tr}{'visibility'} = "$v1 meters"; $decoded_metar{$tr}{'prefix'} = $v2; $decoded_metar{$tr}{'meter'} = $v3; $decoded_metar{$tr}{'km'} = $v4; $decoded_metar{$tr}{'ft'} = $v5; $decoded_metar{$tr}{'mile'} = $v6; $had_dc = 1; } elsif ($part =~ /^(VC)?(-|\+)?(MI|PR|BC|DR|BL|SH|TS|FZ)?((DZ|RA|SN|SG|IC|PL|GR|GS|UP|BR)+)?(BR|FG|FU|VA|DU|SA|HZ|PY)?(PO|SQ|FC|SS)?$/ ) { # } elseif (ereg('^(VC)?' . /* Proximity */ # '(-|\+)?' . /* Intensity */ # '(MI|PR|BC|DR|BL|SH|TS|FZ)?' . /* Descriptor */ # '((DZ|RA|SN|SG|IC|PL|GR|GS|UP)+)?' . /* Precipitation */ # '(BR|FG|FU|VA|DU|SA|HZ|PY)?' . /* Obscuration */ # '(PO|SQ|FC|SS)?$', /* Other */ $v1 = $1; $v2 = $2; $v3 = $3; $v4 = $4; $v5 = $5; $v6 = $6; $tr = get_next_hr('weather', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Weather (v1-6:"; $msg .= ($v1 && length($v1)) ? $v1 : '-'; $msg .= ','; $msg .= ($v2 && length($v2)) ? $v2 : '-'; $msg .= ','; $msg .= ($v3 && length($v3)) ? $v3 : '-'; $msg .= ','; $msg .= ($v4 && length($v4)) ? $v4 : '-'; $msg .= ','; $msg .= ($v5 && length($v5)) ? $v5 : '-'; $msg .= ','; $msg .= ($v6 && length($v6)) ? $v6 : '-'; $msg .= ')'; $msg .= " - $tr"; # $decoded_metar{'weather'}{'proximity'} = $v1 if ($v1 && length($v1)); if ($v1 && length($v1)) { if ($v1 eq 'VC') { $npart = 'outside aero(5-10km)'; # 'weather'.[nn] } else { $npart = 'outside aero(5-10km) $v1 CHECKME'; } } else { $npart = 'at aero'; } $decoded_metar{$tr}{'prox.'} = $npart; ###$decoded_metar{'weather'}{'intensity'} = $v2 if ($v2 && length($v2)); if ($v2 && length($v2)) { if ($v2 eq '-') { $npart = 'light (-)'; } elsif ($v2 eq '+') { $npart = 'heavy (+)'; } else { $npart = 'unknown ($v2) CHECKME'; } } else { # no sign $npart = 'mod. (no sign)'; } $decoded_metar{$tr}{'inten.'} = $npart; $decoded_metar{$tr}{'desc.'} = get_descriptor($v3) if ($v3 && length($v3)); $decoded_metar{$tr}{'precip.'} = get_precipitation($v4) if ($v4 && length($v4)); $decoded_metar{$tr}{'obscur.'} = get_obscuration($v5) if ($v5 && length($v5)); ###$decoded_metar{$tr}{'other'} = get_other($v6) if ($v6 && length($v6)); $decoded_metar{$tr}{'other'} = get_weather($v6) if ($v6 && length($v6)); $had_dc = 1; # ================================================================================ } elsif (($part eq 'SKC') || ($part eq 'CLR') || ($part eq 'NSC')) { $v1 = $part; $tr = get_next_hr('conditions', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Cloud-group."; $msg .= " - $tr"; #$decoded_metar{'clouds'}{'condition'} = $part; $decoded_metar{$tr} = $v1.' (Clear)'; $had_dc = 1; ###} elsif ($part =~ /^(VV|FEW|SCT|BKN|OVC)([0-9]{3}|\/\/\/)(CB|TCU)?$/ ) { } elsif ($part =~ /^(VV|FEW|SCT|BKN|OVC|SCP)([0-9]{2,3}|\/\/\/)(CB|TCU)?$/ ) { $v1 = $1; $v2 = $2; $v3 = $3; $v1 = 'SCT' if ($v1 eq 'SCP'); # special exception case seen $tr = get_next_hr('clouds', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - We have found (another) a cloud-layer-group."; $msg .= " - $tr"; if ($v2 eq '000') { # if ($regs[2] == '000') # '000' is a special height. $decoded_metar{$tr}{'ft'} = 100; # $tr = 'clouds'.[nn] $decoded_metar{$tr}{'meter'} = 30; $decoded_metar{$tr}{'prefix'} = 'Less than (-1)'; } elsif ($v2 eq '///') { # '///' means height nil $decoded_metar{$tr}{'ft'} = 'nil'; $decoded_metar{$tr}{'meter'} = 'nil'; } else { $decoded_metar{$tr}{'ft'} = $v2 * 100; $decoded_metar{$tr}{'meter'} = int($v2 * 30.48); } ###$decoded_metar{$tr}{'cond'} = $v3 if ($v3 && length($v3)); $npart = ''; if ($v1 && length($v1)) { $npart = $v1; $tpart = get_acro_desc($v1); if ($tpart ne 'NONE') { $npart .= " $tpart"; } } $npart .= " $v3" if ($v3 && length($v3)); $decoded_metar{$tr}{'conditions'} = $npart; $msg .= ", $npart"; $had_dc = 1; ################################################################ } elsif ($part =~ /^(M?[0-9]{2})\/(M?[0-9]{2}|\/\/)?$/) { $v1 = $1; $v2 = $2; $tr = get_next_hr('temperature', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Temperature/Dew Point."; $msg .= " - $tr"; # eg 12/08-Temperature and Dewpoint # 12 represents the temperature in Celcius # 08 represents the dewpoint in Celcius # If the temperature or dewpoint falls below 0 # there will be an "M" before it (i.e. 03/M02). "M" means minus. $v6 = 0; $v3 = ''; if ($v1 && length($v1) && ($v1 ne '//')) { $v6++; if (substr($v1,0,1) eq 'M') { $v1 = substr($v1,1); $v4 = -(($v1 * (9/5)) + 32); $v3 = '-'; } else { $v4 = ($v1 * (9/5)) + 32; } } $decoded_metar{$tr}{'temp_c'} = $v3.int($v1); $decoded_metar{$tr}{'temp_f'} = $v3.int( ($v1 * (9/5)) + 32.5 ); # The dewpoint could be missing, this is indicated by the # second group being empty at most places, but in the UK they # use '//' instead of the missing temperature... */ $v3 = ''; if ($v2 && length($v2) && ($v2 ne '//')) { $v6++; if (substr($v2,0,1) eq 'M') { $v2 = substr($v2,1); $v5 = -(($v2 * (9/5)) + 32); $v3 = '-'; } else { $v5 = ($v2 * (9/5)) + 32; } } if (length($v2) && ($v2 ne '//')) { $decoded_metar{$tr}{'dew_c'} = $v3.int($v2); $decoded_metar{$tr}{'dew_f'} = $v3.int(($v2 * (9/5)) + 32.5); if ($v6 == 2) { $v6 = int(get_relative_humidity_f( $v4, $v5 ) + 0.5); $decoded_metar{$tr}{'relative_humidity'} = "$v6\% appx."; } } $had_dc = 1; } elsif ($part =~ /^T([0-9]{4})([0-9]{4})/ ) { # * Temperature/Dew Point Group, coded to tenth of degree Celsius. # $this->store_temp($regs[1] / 10, # $decoded_metar['temperature']['temp_c'], # $decoded_metar['temperature']['temp_f']); # $this->store_temp($regs[2] / 10, # $decoded_metar['temperature']['dew_c'], # $decoded_metar['temperature']['dew_f']); $v1 = $1; $v2 = $2; $tr = get_next_hr('temperature', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Temperature/Dew Point."; $msg .= " - $tr"; $v6 = 0; $v3 = ''; if ($v1 && length($v1)) { $v6++; if (substr($v1,0,1) eq 'M') { $v1 = substr($v1,1); $v3 = '-'; $v4 = -((($v1 / 10) * (9/5)) + 32); } else { $v4 = ((($v1 / 10) * (9/5)) + 32); } $v1 /= 10; } $decoded_metar{$tr}{'temp_c'} = $v3.int($v1); $decoded_metar{$tr}{'temp_f'} = $v3.int( ($v1 * (9/5)) + 32.5 ); # The dewpoint could be missing, this is indicated by the # second group being empty at most places, but in the UK they # use '//' instead of the missing temperature... */ $v3 = ''; if ($v2 && length($v2)&& ($v2 ne '//')) { $v6++; if (substr($v2,0,1) eq 'M') { $v2 = substr($v2,1); $v3 = '-'; $v5 = -((($v2 / 10) * (9/5)) + 32); } else { $v5 = ((($v2 / 10) * (9/5)) + 32); } $v2 /= 10; } if (length($v2) && ($v2 ne '//')) { $decoded_metar{$tr}{'dew_c'} = $v3.int($v2)." (CHECKME)"; $decoded_metar{$tr}{'dew_f'} = $v3.int(($v2 * (9/5)) + 32.5); if ($v6 == 2) { $v6 = int(get_relative_humidity_f( $v4, $v5 ) + 0.5); $decoded_metar{$tr}{'relative_humidity'} = "$v6\% appx."; } } $had_dc = 1; } elsif ($part =~ /^(TX|TN|T)(M)?(\d{2})\/(\d{2})Z$/) { # TX18/13Z TN02/06Z'; # I think TX is maximum temp, and TN is minimum temp forecast $v1 = $1; $v2 = $2; $v3 = $3; $v4 = $4; if ($v1 eq 'TX') { $npart = 'max_temp_c'; } elsif ($v1 eq 'TN') { $npart = 'min_temp_c'; } else { $npart = 'temp_c'; } if ($v3 && ($v2 eq 'M')) { $v3 *= -1; } $tr = get_next_hr('temperature', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Max/Min Temperature."; $msg .= " - $tr"; $decoded_metar{$tr}{$npart} = "$v3 at $v4 hours."; $had_dc = 1; } elsif ($part =~ /^A([0-9]{4})/) { # The United States reports the altimeter setting in inches of mercury (e.g., A2992) # The pressure measured in inHg. $v1 = $1; $tr = get_next_hr('altimeter', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Altimeter (A)."; $msg .= " - $tr"; $decoded_metar{$tr}{'inhg'} = number_format($v1/100, 2); # $tr = 'altimeter'.[nn] # The pressure measured in mmHg, hPa and atm */ $decoded_metar{$tr}{'mmhg'} = number_format($v1 * 0.254, 1); $decoded_metar{$tr}{'hpa'} = int($v1 * 0.33864); $decoded_metar{$tr}{'atm'} = number_format(($v1 * 3.3421e-4), 3); $msg .= " ($tr)"; $had_dc = 1; } elsif ( $part =~ /^Q([0-9]{4}|NIL|\/{4})$/ ) { # internationally it will be reported in hectoPascals (milibars) (e.g., Q1016). # EXCEPTION SEEN 'Q////' $v1 = $1; $tr = get_next_hr('altimeter', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Altimeter (Q)."; $msg .= " - $tr"; # The specification doesn't say anything about # the Qxxxx-form, but it's in the METARs. # /* The pressure measured in hPa */ if (($v1 eq 'NIL')||($v1 eq '////')) { $decoded_metar{$tr}{'hPa'} = 'NOT AVAILABLE'; } else { $decoded_metar{$tr}{'hPa'} = int($v1); # $tr = 'altimeter'.[nn] # /* The pressure measured in mmHg, inHg and atm */ $decoded_metar{$tr}{'mmhg'} = number_format($v1 * 0.75006, 1); $decoded_metar{$tr}{'inhg'} = number_format($v1 * 0.02953, 2); $decoded_metar{$tr}{'atm'} = number_format($v1 * 9.8692e-4, 3); } $had_dc = 1; } elsif ( $part =~ /^QNH([0-9]{4})INS/ ) { $v1 = $1; $tr = get_next_hr('altimeter', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Altimeter (QNH)."; $msg .= " - $tr"; $decoded_metar{$tr}{'inhg'} = number_format($v1 / 100, 2); # $tr = 'altimeter'.[nn] $decoded_metar{$tr}{'mmhg'} = number_format($v1 * 0.254, 2); # 100 inch to mm $decoded_metar{$tr}{'hpa'} = number_format($v1 * 0.338639, 2); $decoded_metar{$tr}{'atm'} = number_format(($v1 * 3.3421e-4), 3); $had_dc = 1; } elsif ($part =~ /^T([0-9]{4}$)/) { $v1 = $1; $msg = "WARNING:2: [$msg] NOT YET HANDLED! $v1"; prtw("$msg\n", $tt ); # $this->store_temp($regs[1], # $decoded_metar['temperature']['temp_c'], # $decoded_metar['temperature']['temp_f']); } elsif ($part =~ /^1([0-9]{4}$)/) { $v1 = $1; $msg = "WARNING:3: [$msg] NOT YET HANDLED! $v1"; prtw("$msg\n", $tt ); # * 6 hour maximum temperature Celsius, coded to tenth of degree # $this->store_temp($regs[1] / 10, # $decoded_metar['temp_min_max']['max6h_c'], # $decoded_metar['temp_min_max']['max6h_f']); } elsif ($part =~ /^2([0-9]{4}$)/) { $v1 = $1; $msg = "WARNING:4: [$msg] NOT YET HANDLED! $v1"; prtw("$msg\n", $tt ); # * 6 hour minimum temperature Celsius, coded to tenth of degree # $this->store_temp($regs[1] / 10, # $decoded_metar['temp_min_max']['min6h_c'], # $decoded_metar['temp_min_max']['min6h_f']); } elsif ($part =~ /^4([0-9]{4})([0-9]{4})$/) { $v1 = $1; $msg = "WARNING:5: [$msg] NOT YET HANDLED! $v1"; prtw("$msg\n", $tt ); # * 24 hour maximum and minimum temperature Celsius, coded to # * tenth of degree # $this->store_temp($regs[1] / 10, # $decoded_metar['temp_min_max']['max24h_c'], # $decoded_metar['temp_min_max']['max24h_f']); # $this->store_temp($regs[2] / 10, # $decoded_metar['temp_min_max']['min24h_c'], # $decoded_metar['temp_min_max']['min24h_f']); } elsif ($part =~ /^P([0-9]{4})/) { $v1 = $1; $msg = "WARNING:6: [$msg] NOT YET HANDLED! $v1"; prtw("$msg\n", $tt ); # * Precipitation during last hour in hundredths of an inch # if ($regs[1] == '0000') { # $decoded_metar['precipitation']['in'] = -1; # $decoded_metar['precipitation']['mm'] = -1; # } else { # $decoded_metar['precipitation']['in'] = # number_format($regs[1]/100, 2); # $decoded_metar['precipitation']['mm'] = # number_format($regs[1]*0.254, 2); # } } elsif ($part =~ /^6([0-9]{4})/) { $v1 = $1; $tr = get_next_hr('precititation', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Precipitation during last 3 or 6 hours in hundredths of an inch."; $msg .= " - $tr"; if ($v1 eq '0000') { $decoded_metar{$tr}{'in_6h'} = -1; $decoded_metar{$tr}{'mm_6h'} = -1; } else { $decoded_metar{$tr}{'in_6h'} = number_format($v1/100, 2); $decoded_metar{$tr}{'mm_6h'} = number_format($v1*0.254, 2); } $had_dc = 1; } elsif ($part =~ /^7([0-9]{4})/ ) { $v1 = $1; $msg = "WARNING:8: [$msg] NOT YET HANDLED! $v1"; prtw("$msg\n", $tt ); # * Precipitation during last 24 hours in hundredths of an inch. # if ($regs[1] == '0000') { # $decoded_metar['precipitation']['in_24h'] = -1; # $decoded_metar['precipitation']['mm_24h'] = -1; # } else { # $decoded_metar['precipitation']['in_24h'] = # number_format($regs[1]/100, 2, '.', ''); # $decoded_metar['precipitation']['mm_24h'] = # number_format($regs[1]*0.254, 2, '.', ''); # } } elsif ($part =~ /^4\/([0-9]{3})/) { $v1 = $1; $msg = "WARNING:9: [$msg] NOT YET HANDLED! $v1"; prtw("$msg\n", $tt ); # * Snow depth in inches # if ($regs[1] == '0000') { # $decoded_metar['precipitation']['snow_in'] = -1; # $decoded_metar['precipitation']['snow_mm'] = -1; # } else { # $decoded_metar['precipitation']['snow_in'] = $regs[1] * 1; # $decoded_metar['precipitation']['snow_mm'] = round($regs[1] * 25.4); # } } elsif ($part =~ /^PROB([0-9]{1,2})$/) { $v1 = $1; $tr = get_next_hr('probability', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Probability."; $msg .= " - $tr"; # PROB40 2022 Probability. The probability of a weather event occurring is given in percent # along with the 2-digit Zulu hour start and end of the event. $decoded_metar{$tr}{'percent'} = $v1; if ($i2 < $num_parts) { $part = $parts[$i+1]; if ($part =~ /^([0-9]{2})([0-9]{2})$/) { $v1 = $1; $v2 = $2; $decoded_metar{$tr}{'start'} = $v1; $decoded_metar{$tr}{'end'} = $v2; $msg .= " (start=$v1, end=$v2)"; $i = $i2; } } $had_dc = 1; } elsif ($part =~ /^[MP]?(([0-9]?)[ ]?([0-9])(\/?)([0-9]*))SM$/ ) { # Examples: # 1/2SM - Visibility one-half statute mile # 2 1/4SM - Visibility two and one-quarter statute miles # 5SM - Visibility five statute miles # P6SM - Visibility more than six statute miles $v1 = $1; $v2 = $2; $v3 = $3; $v4 = $4; $tr = get_next_hr('visibility', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Visibility.(v1-4:"; $msg .= ($v1 && length($v1)) ? $v1 : '-'; $msg .= ','; $msg .= ($v2 && length($v2)) ? $v2 : '-'; $msg .= ','; $msg .= ($v3 && length($v3)) ? $v3 : '-'; $msg .= ','; $msg .= ($v4 && length($v4)) ? $v4 : '-'; $msg .= ')'; $msg .= " - $tr"; $decoded_metar{$tr}{'statute-miles'} = $v1; $had_dc = 1; } elsif ($part =~ /([0-9]{2})([0-9]{2})([0-9]{2})/) { $v1 = $1; $v2 = $2; $v3 = $3; $tr = get_next_hr('time-range', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Time Range"; $msg .= " - $tr"; $decoded_metar{$tr}{'day'} = $v1; $decoded_metar{$tr}{'begin-hour'} = $v2; $decoded_metar{$tr}{'end-hour'} = $v3; $had_dc = 1; } elsif ($part =~ /^FM([0-9]{2})([0-9]{2})/) { $v1 = $1; $v2 = $2; # eg FM1930 = FroM and 2-digit hour and 2-digit minute # beginning time: indicates significant change. # Each FM starts on a new line, indented 5 spaces. $tr = get_next_hr('time-range', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Time Range FroM(2:2)"; $msg .= " - $tr"; $decoded_metar{$tr}{'begin-hour'} = $v1; $decoded_metar{$tr}{'begin-mins'} = $v2; $had_dc = 1; } elsif ($part =~ /^FM([0-9]{2})$/) { # eg FM19 = FroM and 2-digit hour assume 00 minute # beginning time: indicates significant change. # Each FM starts on a new line, indented 5 spaces. $v1 = $1; $v2 = '00'; $tr = get_next_hr('time-range', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Time Range FroM(2)"; $msg .= " - $tr"; $decoded_metar{$tr}{'begin-hour'} = $v1; $decoded_metar{$tr}{'begin-mins'} = $v2; $had_dc = 1; } else { # If we couldn't match the group, so we could assume that it was a remark. # $decoded_metar['remarks'] .= ' ' . $part; if ($part eq 'T') { #prt( "Processing a 'T' ...\n" ); while ($i2 < $num_parts) { my $tmppt = $parts[$i2]; #prt( "Processing a [$tmppt] ...\n" ); if ($tmppt =~ /^[0-9]{2}$/) { #prt( "Adding [$tmppt] to [$part]...\n" ); $part .= $tmppt; if ($part =~ /^T([0-9]{4})([0-9]{4})/ ) { #prt( "Got [$part] $1 $2 ...\n" ); $i = $i2; last; } $i2++; } else { last; } } if ($part =~ /^T([0-9]{4})([0-9]{4})/ ) { $v1 = $1 / 10; $v2 = $2 / 10; $tr = get_next_hr('temperature', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg = "$part - Temperature/Dewpoint - $v1, $v2"; $msg .= " - $tr"; $v3 = ''; if ($v1 && length($v1) && (substr($v1,0,1) eq 'M')) { $v3 = '-'; } $decoded_metar{$tr}{'temp_c'} = $v3.int($v1/100); $decoded_metar{$tr}{'temp_f'} = $v3.int( (($v1/100) * (9/5)) + 32.5 ); # The dewpoint could be missing, this is indicated by the # second group being empty at most places, but in the UK they # use '//' instead of the missing temperature... */ $v3 = ''; if ($v2 && length($v2) && (substr($v2,0,1) eq 'M')) { $v3 = '-'; } if (length($v2) && ($v2 ne '//')) { $decoded_metar{$tr}{'dew_c'} = $v3.int($v2/100); $decoded_metar{$tr}{'dew_f'} = $v3.int((($v2/100) * (9/5)) + 32.5); } $had_dc = 1; $part = ''; } } # to handle # Q 1007 1009 1008 1006 if ($part eq 'Q') { #prt( "Processing a 'T' ...\n" ); while ($i2 < $num_parts) { $npart = $parts[$i2]; if ($npart =~ /\d{4}/) { $v1 = $npart; $tr = get_next_hr('altimeter', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Altimeter (Qsp)."; $msg .= " - $tr"; # /* The pressure measured in hPa */ $decoded_metar{$tr}{'hpa'} = int($v1); # $tr = 'altimeter'.[nn] # /* The pressure measured in mmHg, inHg and atm */ $decoded_metar{$tr}{'mmhg'} = number_format($v1 * 0.75006, 1); $decoded_metar{$tr}{'inhg'} = number_format($v1 * 0.02953, 2); $decoded_metar{$tr}{'atm'} = number_format($v1 * 9.8692e-4, 3); $i = $i2; $i2 = $i + 1; } else { last; } } $had_dc = 1; $part = ''; } if (length($part) && (substr($part,0,1) eq '(') ) { # begin bracket open - go until closed $npart = $part; if ( !($npart =~ /\)$/) ) { while ($i2 < $num_parts) { $npart .= ' '.$parts[$i2]; if ($npart =~ /\)$/) { last; } $i2++; } } if ( $npart =~ /\)$/) { $tr = get_next_hr('brackets', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - $npart"; $msg .= " - $tr"; $decoded_metar{$tr} = $npart; # $tr = 'brackets'.[nn] $i = $i2; $part = ''; } } if (length($part)) { $npart = get_acro_desc($part); if ($npart ne 'NONE') { $tr = get_next_hr('acronym', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - $npart"; $msg .= " - $tr"; $decoded_metar{$tr}{$part} = $npart; # $tr = 'acronym'.[nn] #while ($i2 < $num_parts) { # $part = $parts[$i2]; #} $msg .= " - acronym for $npart - $tr"; $part = ''; } } if (length($part) && ( is_in_taf_exceptions($part) || is_in_taf_spl_exceptions($part) || (length($part) == 1) ) ) { $excnum++; $tr = 'exception'; $tr = "998.$tr" if ($addorder); $decoded_metar{$tr}{$excnum} = $part; $msg .= " - $tr:$excnum"; $had_z = 0 if ($part eq 'BY'); # EXCEPTION SEEN - BY ETGL $part = ''; } if (length($part) && ($part =~ /^NSW$/)) { # NWS TAFs exclude turbulence, icing & temperature forecasts; # NWS METARs exclude trend fcsts $tr = get_next_hr('nsw', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $decoded_metar{$tr} = 'exclude trend fcst'; $msg .= " - $tr"; $part = ''; $had_dc = 1; } if ($part =~ /^[A-Z]{5}$/) { $v1 = $part; # $decoded_metar{'icao'} = $part; $tr = get_next_hr('icao', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - Station Identifier(5)."; $msg .= " - $tr"; $decoded_metar{$tr} = "$v1 (CHECKME)"; # $tr = 'icao'.[nn] $part = ''; $had_dc = 1; } if ($part =~ /^(\w{4})\/(\w{4})/) { my @arr = split(/\//,$part); $msg .= ' - Station Ident (mult)'; foreach $npart (@arr) { $tr = get_next_hr('icao', \%decoded_metar); $tr = "$cord.$tr" if ($addorder); $msg .= " - $npart"; $msg .= " - $tr"; $decoded_metar{$tr} = $npart; # $tr = 'icao'.[nn] } $part = ''; $had_dc = 1; } ############################ if (length($part)) { $tr = 'UNDECODED'; $tr = "999.$tr" if ($addorder); $msg = "WARNING:ELSE: [$msg] *** NO CASE FOR THIS! ***"; $msg .= " - $tr"; prtw("$msg\n", $tt ); if ($part =~ /^[A-Z]{5,9}$/) { add_2_exceptions($part); } if (defined $decoded_metar{$tr}) { $decoded_metar{$tr} .= ' '.$part; } else { $decoded_metar{$tr} = $part; } } } prt( "$msg\n" ) if ($verbose); } if (no_icao_found(\%decoded_metar)) { $msg = "WARNING:ICAO: NO ICAO FOUND IN THIS METAR/TAF!"; prtw("$msg\n", $tt ); } return %decoded_metar; } ################################################# sub load_airport_file { my ($aptdat) = shift; # = $APTFILE; my ($line, $alat, $alon, $icao, $name, $rlat, $rlon); my (@arr, @arr2); prt("\nLoading $aptdat file ...\n"); mydie("ERROR: Can NOT locate $aptdat ...$!...\n") if ( !( -f $aptdat) ); ###open IF, "<$aptdat" or mydie("OOPS, failed to open [$aptdat] ... check name and location ...\n"); open IF, "gzip -d -c $aptdat|" or mydie( "ERROR: CAN NOT OPEN $aptdat...$!...\n" ); my @lines = <IF>; close IF; my $cnt = scalar @lines; prt( "Processing $cnt lines ...\n" ); my $apt = ''; my $rwycnt = 0; my $glat = 0; my $glon = 0; my $totaptcnt = 0; # count another AIRPORT foreach $line (@lines) { $line = trim_all($line); ###prt("$line\n"); @arr = split(/ /,$line); if ($line =~ /^$aln\s+/) { # start with '1' if (length($apt) && ($rwycnt > 0)) { $alat = $glat / $rwycnt; $alon = $glon / $rwycnt; @arr2 = split(/ /,$apt); $icao = $arr2[4]; $name = join(' ', splice(@arr2,5)); push(@aptlist, [$icao, $name, $alat, $alon]); } $apt = $line; $rwycnt = 0; $glat = 0; $glon = 0; $totaptcnt++; # count another AIRPORT } elsif ($line =~ /^$rln\s+/) { $rlat = $arr[1]; $rlon = $arr[2]; ###prt( "$line [$rlat, $rlon]\n" ); $glat += $rlat; $glon += $rlon; $rwycnt++; } elsif ($line =~ /^$lastln\s?/) { # 99, followed by space, count 0 or more ... prt( "Reached END OF FILE ... \n" ); last; } } if (length($apt) && ($rwycnt > 0)) { $alat = $glat / $rwycnt; $alon = $glon / $rwycnt; @arr2 = split(/ /,$apt); $icao = $arr2[4]; $name = join(' ', splice(@arr2,5)); push(@aptlist, [$icao, $name, $alat, $alon]); } } sub get_fix_len { my ($d) = shift; my $stg = sprintf("%03.7f", $d); my @arr = split(/\./,$stg); if (scalar @arr == 2) { $arr[0] = ' '.$arr[0] while ( length($arr[0]) < 4 ); $stg = $arr[0].'.'.$arr[1]; } return $stg; } sub show_airport { my ($off) = shift; # push(@aptlist, [$icao, $name, $alat, $alon]); my $cnt = scalar @aptlist; if (($off > 0) && ($off <= $cnt)) { $off--; # back up to logical my $icao = $aptlist[$off][0]; $icao .= ' ' while (length($icao) < 4); my $name = $aptlist[$off][1]; my $alat = $aptlist[$off][2]; my $alon = $aptlist[$off][3]; my $slat = get_fix_len($alat); my $slon = get_fix_len($alon); prt( "$icao $slat $slon $name\n" ); } } # references viewed # from : http://aviationweather.gov/static/help/taf-decode.php - see html/taf-decode.htm # A TAF report contains the following sequence of elements in the following order: # 1. Type of Report # 2. ICAO Station Identifier # 3. Date and Time of Origin # 4. Valid Period Date and Time # 5. Forecast Meteorological Conditions # from : http://mbev.net/wikka/METARSandTAFS # KJAX 020256Z 02003KT 10SM TSRA OVC01OCB SCT100 BKN130 18/17 A2996 # METAR's will always be published in the same order: see: html/METARSandTAFS.htm # see from : http://www.nws.noaa.gov/oso/oso1/oso12/document/guide.shtml # also from : http://www.faa.gov/about/office_org/field_offices/fsdo/orl/local_more/media/ppt/metar.ppt # from : http://www.srh.noaa.gov/srh/cwwd/msd/note6.html # from : http://www.wunderground.com/metarFAQ.asp # general ORDER seems to be - # TYPE ID TIME WIND VIS WX SKY T/TD ALT REMARK # METAR KORD 041656Z 19020G26KT 6SM -SHRA BKN070 12/08 A3016 RMK AO2 # from : http://www.nws.noaa.gov/oso/oso1/oso12/overview.htm # from : http://www.flyingineurope.be/metar_taf_decode.htm # from : http://www.alaska.faa.gov/fai/afss/metar%20taf/metcont.htm # from : http://www.geocities.com/CapeCanaveral/Lab/6799/metarpg.htm # from : http://www.alaska.faa.gov/fai/afss/metar%20taf/sametar1.htm # from : http://weather.cod.edu/notes/metar.html # from : http://aviation.weathersa.co.za/codesexpl.php # a) Code form. # Add “AMD” and “COR” after “TAF” (before ICAO ID); # Add “NIL” or “CNL” after “Y1Y1G1G1G2G2”. # Reason: there is an aeronautical requirement to identify amended, corrected, missing # and cancelled aerodrome forecasts (Amendment 72 and draft Amendment 73 to Annex 3); # a) 51.1.1 Amend to read as follows: “The code name TAF shall be included at the beginning # of each individual aerodrome forecast. (Amendment 72 to Annex 3); # AMD = Amended COR = Corrected CNL = Cancelled Aviation Code Manual # from : http://www.pprune.org/forums/showthread.php?s=822dc56eacab626fce1992990baa3247&p=3732431#post3732431 # http://aviation.weathersa.co.za/codesexpl.php # I think TX is maximum temp, and TN is minimum temp forecast # UK - You need a login (it is free) # login - http://secure.metoffice.com/logon.jsp # from : http://www.met.tamu.edu/Weather_Interface/ # can fetch METAR/TAF for 3-digit USA codes - FSO # KSFO 291756Z 13003KT 10SM -RA SCT027 OVC034 07/04 A3031 RMK AO2 RAB52 SLP262 P0001 60001 T00720044 10072 20044 53010= # The weather observed at SAN FRANCISCO, CA (KSFO) at 09:56 AM PST was: # The skies were cloudy. = YES # The weather reported was light rain. = YES # Temperature: 45F ( 7C) Dewpoint: 40F ( 4C) Relative Humidity: 82% # Winds from the SE (130 degs) at 3 mph. # Pressure: 1026.2 millibars. Altimeter:30.31 inches of mercury. = YES # The prevailing visibility was 10 miles. = YES # The maximum temperature in the past 6 hours was 45F. # The minimum temperature in the past 6 hours was 40F. # There was 0.01 inches of precipitation in the past 6 hours. # MY DECODE #- icao .........: KSFO #- time .........: mins=56, hour=17, day=29 = ERROR!!! #- wind .........: miles_per_hour=3.4, meters_per_second=1.5, knots=03, deg=130 = OK #- visibility ...: statute-miles=10 = OK #- weather ......: proximity=at aerodrome, obscuration=RA (Rain), precipitation=RA (Rain), intensity=light (-) = OK #- clouds .......: ft=2700, meter=822 = OK #- clouds1 ......: ft=3400, meter=1036 = OK #- temperature ..: dew_f=39, temp_f=45, temp_c=7, dew_c=4 MISSING Rel.Hum: 82% #- altimeter ....: inhg=30.31, mmhg=769.8, atm=1.012, hpa=1026 = OK #- remarks ......: RMK AO2 RAB52 SLP262 P0001 60001 T00720044 10072 20044 53010 # To CALCULATE RELATIVE HUMIDITY from TEMPERATURE AND DUE POINT # function run_dp(IncomingForm) { # t= 0.0; td=0.0; e= 0.0; es=0.0; # CheckChar(IncomingForm); CheckChar1(IncomingForm); # t = parseFloat(x); td = parseFloat(y); # if (t<td) { alert("Temp less than Dewpoint, please check values and retry"); } else { # e = Math.exp((17.269 * td) / (237.3 + td)); # es= Math.exp((17.269 * t) / (237.3 + t)); # if (es!=0){ z = e * 100 / es; # if (z > 100.0) z =100; # if (z < 0) z=0; # z = Math.round(z); # document.inputform.rh.value = z;} } } sub get_relative_humidity_f { my ($ta, $td) = @_; if ($ta < $td) { return "T < DP! ($ta < $td)"; } my $e = (17.269 * $td) / (237.3 + $td); my $es = (17.269 * $ta) / (237.3 + $ta); if ($es != 0) { my $z = ($e * 100) / $es; $z = 100 if ($z > 100); $z = 0 if ($z < 0); return $z; } return 'failed'; } # from : http://www.luftpiraten.de/mtd.html # EDDF 250450Z 18004KT 4800 BR SCT018 BKN032 00/00 Q1014 8829//95 NOSIG # METAR Report für: Frankfurt, Deutschland (Rhein Main) - EDDF/FRA # Beobachtungszeit: 25.xx.xxxx 04:50 Uhr UTC #Wind ...........: aus 180° mit 4 kt = YES #Sicht ..........: 4800 m #Wetter .........: feuchter Dunst #Wolken .........: 3/8 - 4/8 in 1800 ft über Flugplatzniveau # 5/8 - 7/8 in 3200 ft über Flugplatzniveau #Temperatur .....: 00°C #Taupunkt .......: 00°C #QNH ............: 1014 hPa = YES #Pistenzustand...: alle Pisten: naß oder Wasserpfützen, 51% bis 100% bedeckt, # Höhe der Ablagerungen betrieblich nicht signifikant oder # nicht meßbar, gute Bremswirkung # keine wesentliche Änderung erwartet #- icao .........: EDDF #- time .........: mins=50, hour=04, day=25 #- wind .........: miles_per_hour=4.6, meters_per_second=2, knots=04, deg=180 = OK #- visibility ...: km=4.8, ft=15748, meter=4800, mile=2.9, prefix=0 #- weather ......: proximity=at aerodrome, obscuration=BR (Mist >= 5/8SM), precipitation=BR (CHECKME), intensity=moderate (no sign) #- clouds .......: ft=1800, meter=548 #- clouds1 ......: ft=3200, meter=975 #- temperature ..: relative_humidity=100% appx., dew_f=32, temp_f=32, temp_c=0, dew_c=0 #- altimeter ....: inhg=29.94, mmhg=760.5, atm=1, hpa=1014 = OK #- icao1 ........: NOSIG #- UNDECODED ....: 8829//95 # from : http://adds.aviationweather.gov/tafs/ - fetches and decodes METAR/TAF # KSFO 301456Z 29003KT 10SM FEW015 SCT075 07/04 A3032 RMK AO2 SLP266 60000 T00670039 53009 # KSFO 301303Z 301312 27005KT P6SM VCSH SCT070 OVC090 # TEMPO 1317 5SM BR BKN040 # FM1900 29010G15KT P6SM FEW100 # FM0200 28010KT P6SM SCT080 # FM0900 17004KT P6SM SCT010 OVC060 #Aviation Digital Data Service (ADDS) #Output produced by TAFs form (1503 UTC 30 January 2008) #found at http://adds.aviationweather.gov/tafs/index.php #METAR text: KSFO 301456Z 29003KT 10SM FEW015 SCT075 07/04 A3032 RMK AO2 SLP266 60000 T00670039 53009 #Conditions at: KSFO (SAN FRANCISCO, CA, US) observed 1456 UTC 30 January 2008 #Temperature: 6.7°C (44°F) #Dewpoint: 3.9°C (39°F) [RH = 82%] #Pressure (altimeter): 30.32 inches Hg (1026.8 mb) #[Sea-level pressure: 1026.6 mb] #Winds: from the WNW (290 degrees) at 3 MPH (3 knots; 1.6 m/s) #Visibility: 10 or more miles (16+ km) #Ceiling: at least 12,000 feet AGL #Clouds: few clouds at 1500 feet AGL #scattered clouds at 7500 feet AGL #Weather: no significant weather observed at this time #-------------------------------------------------------------------------------- #Forecast for: KSFO (SAN FRANCISCO, CA, US) #Text: KSFO 301303Z 301312 27005KT P6SM VCSH SCT070 OVC090 #Forecast period: 1300 to 1900 UTC 30 January 2008 #Forecast type: FROM: standard forecast or significant change #Winds: from the W (270 degrees) at 6 MPH (5 knots; 2.6 m/s) #Visibility: 6 or more miles (10+ km) #Ceiling: 9000 feet AGL #Clouds: scattered clouds at 7000 feet AGL #overcast cloud deck at 9000 feet AGL #Weather: VCSH (showers in vicinity) #Text: TEMPO 1317 5SM BR BKN040 #Forecast period: 1300 to 1700 UTC 30 January 2008 #Forecast type: TEMPORARY: The following changes expected for less than half the time period #Visibility: 5 miles (8 km) #Ceiling: 4000 feet AGL #Clouds: broken clouds at 4000 feet AGL #Weather: BR (mist) #Text: FM1900 29010G15KT P6SM FEW100 #Forecast period: 1900 UTC 30 January 2008 to 0200 UTC 31 January 2008 #Forecast type: FROM: standard forecast or significant change #Winds: from the WNW (290 degrees) at 12 MPH (10 knots; 5.2 m/s) #gusting to 17 MPH (15 knots; 7.8 m/s) #Visibility: 6 or more miles (10+ km) #Clouds: few clouds at 10000 feet AGL #Weather: no significant weather forecast for this period #Text: FM0200 28010KT P6SM SCT080 #Forecast period: 0200 to 0900 UTC 31 January 2008 #Forecast type: FROM: standard forecast or significant change #Winds: from the W (280 degrees) at 12 MPH (10 knots; 5.2 m/s) #Visibility: 6 or more miles (10+ km) #Clouds: scattered clouds at 8000 feet AGL #Weather: no significant weather forecast for this period #Text: FM0900 17004KT P6SM SCT010 OVC060 #Forecast period: 0900 to 1200 UTC 31 January 2008 #Forecast type: FROM: standard forecast or significant change #Winds: from the S (170 degrees) at 5 MPH (4 knots; 2.1 m/s) #Visibility: 6 or more miles (10+ km) #Ceiling: 6000 feet AGL #Clouds: scattered clouds at 1000 feet AGL #overcast cloud deck at 6000 feet AGL #Weather: no significant weather forecast for this period #-------------------------------------------------------------------------------- # from : http://www.skystef.be/metar-decoder.htm # paste or copy METAR / TAF and get decode # eg # KSFO 301303Z 301312 27005KT P6SM VCSH SCT070 OVC090 # TEMPO 1317 5SM BR BKN040 # FM1900 29010G15KT P6SM FEW100 # FM0200 28010KT P6SM SCT080 # FM0900 17004KT P6SM SCT010 OVC060 # gives #Location...........: KSFO #Day of month.......: 30 #Time...............: 13:03 UTC #Wind...............: true direction = 270 degrees; speed = 5 knots #Weather............: in the vicinity shower(s) of #Cloud coverage.....: scattered (3 to 4 oktas) at 7000 feet above aerodrome level #Cloud coverage.....: overcast (8 oktas) at 9000 feet above aerodrome level #Next 2hrs temporary: #Visibility.........: 1317 meter #Weather............: mist #Cloud coverage.....: broken (5 to 7 oktas) at 4000 feet above aerodrome level #From 19:00 UTC.....: #Wind...............: true direction = 290 degrees; speed = 10 knots with gusts of 15 knots #Cloud coverage.....: few (1 to 2 oktas) at 10000 feet above aerodrome level #From 02:00 UTC.....: #Wind...............: true direction = 280 degrees; speed = 10 knots #Cloud coverage.....: scattered (3 to 4 oktas) at 8000 feet above aerodrome level #From 09:00 UTC.....: #Wind...............: true direction = 170 degrees; speed = 4 knots #Cloud coverage.....: scattered (3 to 4 oktas) at 1000 feet above aerodrome level # *AND* #EDDF 250450Z 18004KT 4800 BR SCT018 BKN032 00/00 Q1014 8829//95 NOSIG #Location...........: EDDF #Day of month.......: 25 #Time...............: 04:50 UTC #Wind...............: true direction = 180 degrees; speed = 4 knots #Visibility.........: 4800 meter #Weather............: mist #Cloud coverage.....: scattered (3 to 4 oktas) at 1800 feet above aerodrome level #Cloud coverage.....: broken (5 to 7 oktas) at 3200 feet above aerodrome level #Temperature........: 00 degrees Celsius #Dewpoint...........: 00 degrees Celsius #QNH (msl pressure).: 1014 hectopascal #Next 2 hours.......: no significant changes #- icao .........: EDDF #- time .........: mins=50, hour=04, day=25 #- wind .........: miles_per_hour=4.6, meters_per_second=2, knots=04, deg=180 #- visibility ...: km=4.8, ft=15748, meter=4800, mile=2.9, prefix=0 #- weather ......: proximity=at aerodrome, obscuration=BR (Mist >= 5/8SM), precipitation=BR (CHECKME), intensity=moderate (no sign) #- clouds .......: ft=1800, meter=548, conditions=SCT scattered (3-4 oktas) #- clouds1 ......: ft=3200, meter=975, conditions=BKN broken (5-7 oktas) #- temperature ..: relative_humidity=100% appx., dew_f=32, temp_f=32, temp_c=0, dew_c=0 #- altimeter ....: inhg=29.94, mmhg=760.5, atm=1, hpa=1014 #- icao1 ........: NOSIG #- UNDECODED ....: 8829//95 # *AND* # LIRL 121100Z 12221 17007KT 9999 SCT018 SCT070 TEMPO 1218 FEW020CB #Location...........: LIRL #Day of month.......: 12 #Time...............: 11:00 UTC #Wind...............: true direction = 170 degrees; speed = 7 knots #Visibility.........: 10 kilometers or more #Cloud coverage.....: scattered (3 to 4 oktas) at 1800 feet above aerodrome level #Cloud coverage.....: scattered (3 to 4 oktas) at 7000 feet above aerodrome level #Next 2hrs temporary: #Visibility.........: 1218 meter #Cloud coverage.....: few (1 to 2 oktas) at 2000 feet above aerodrome level cumulonimbus # *AND* # YSSY 301644Z 301818 35008KT 9999 FEW012 FM23 04015KT 9999 FEW040 FM08 18020G30KT 9999 -SHRA SCT012 BKN020 PROB30 INTER 0212 3000 TSRA SCT015 SCT050CB INTER 1218 4000 SHRA BKN010 #Location...........: YSSY #Day of month.......: 30 #Time...............: 16:44 UTC #Wind...............: true direction = 350 degrees; speed = 8 knots #Visibility.........: 10 kilometers or more #Cloud coverage.....: few (1 to 2 oktas) at 1200 feet above aerodrome level #Wind...............: true direction = 040 degrees; speed = 15 knots #Visibility.........: 10 kilometers or more #Cloud coverage.....: few (1 to 2 oktas) at 4000 feet above aerodrome level #Wind...............: true direction = 180 degrees; speed = 20 knots with gusts of 30 knots #Visibility.........: 10 kilometers or more #Weather............: light shower(s) of rain #Cloud coverage.....: scattered (3 to 4 oktas) at 1200 feet above aerodrome level #Cloud coverage.....: broken (5 to 7 oktas) at 2000 feet above aerodrome level #Visibility.........: 212 meter #Visibility.........: 3000 meter #Weather............: thunderstorm rain #Cloud coverage.....: scattered (3 to 4 oktas) at 1500 feet above aerodrome level #Cloud coverage.....: scattered (3 to 4 oktas) at 5000 feet above aerodrome level cumulonimbus #Visibility.........: 1218 meter #Visibility.........: 4000 meter #Weather............: shower(s) of rain #Cloud coverage.....: broken (5 to 7 oktas) at 1000 feet above aerodrome level # and mine #- icao .........: YSSY #- time .........: mins=44, hour=16, day2=30, day=30, end-hour=18, begin-hour=18 #- wind .........: miles_per_hour=9.2, meters_per_second=4.1, knots=08, deg=350 #- visibility ...: km=10, ft=32800, meter=10000, mile=6.2, prefix=1 #- clouds .......: ft=1200, meter=365, conditions=FEW few clouds (1-2 oktas) #- time-range ...: begin-mins=00, begin-hour=23 #- wind1 ........: miles_per_hour=17.2, meters_per_second=7.7, knots=15, deg=040 #- visibility1 ..: km=10, ft=32800, meter=10000, mile=6.2, prefix=1 #- clouds1 ......: ft=4000, meter=1219, conditions=FEW few clouds (1-2 oktas) #- time-range1 ..: begin-mins=00, begin-hour=08 #- wind2 ........: miles_per_hour=23, meters_per_second=10.2, knots=20, deg=180 #- visibility2 ..: km=10, ft=32800, meter=10000, mile=6.2, prefix=1 #- weather ......: proximity=at aerodrome, obscuration=RA (Rain), precipitation=RA (Rain), intensity=light (-), decriptor=SH (Showers) #- clouds2 ......: ft=1200, meter=365, conditions=SCT scattered (3-4 oktas) #- clouds3 ......: ft=2000, meter=609, conditions=BKN broken (5-7 oktas) #- probability ..: percent=30 #- remarks ......: INTER 0212 3000 TSRA SCT015 SCT050CB INTER 1218 4000 SHRA BKN010 # from : http://www.astro.keele.ac.uk/oldusers/rno/Aviation/metar_codes.html # eof - geturl02.pl