#!/perl -w
# NAME: geturl.pl
# AIM: Get the text from a URL page ...
# 22/01/2008 - geoff mclane
# references
# see from : http://aviationweather.gov/static/help/taf-decode.php - see html/taf-decode.htm
# 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
# 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
use strict;
use warnings;
use Socket;
use LWP::Simple;
require 'logfile.pl' or die "Unable to load logfile.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 $minkey = 16;

# 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

my @aptlist = ();
my $aptcnt  = 0;
my @taflist = ();

# 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 $acttaf = '';
my @warnings = ();

my $cannedtaf = 'taf200825.txt';
##load_airport_file($APTFILE);
$aptcnt = scalar @aptlist;
prt( "Got $aptcnt airports loaded ...\n" );
for (my $i = 1; $i <= $aptcnt; $i++) {
	show_airport($i);
}

###fetch_url( $testurl );
if (open TF, "<$cannedtaf") {
	@taflist = <TF>;
	close TF;
} else {
	push(@taflist, $testtaf2);
}
foreach my $taf (@taflist) {
	chomp $taf;
	show_taf($taf);
}

foreach my $warn (@warnings) {
	prt( "$warn\n" );
}
close_log($outfile,1);
exit(0);

sub fetch_url {
	my ($url) = shift;
	prt( "Fetching: $url\n" );
	my $txt = get($url);
	if ($txt && length($txt)) {
		prt( "$txt\n" );
		# the TEXT - something like
		# 2008/01/22 11:00
		# TAF LFPO 221100Z 221818 20006KT 9999 SCT025 SCT040 
		#      BECMG 1820 6000 SCT004 
		#      TEMPO 2002 2000 BR BKN003 
		#      TEMPO 0408 -RADZ BKN006 
		#      BECMG 0810 BKN012 
		#      BECMG 1214 BKN018
		my $taftxt = $txt;
		$taftxt =~ s/\n/ /gm;
		$taftxt = trim_all($taftxt);
		prt( "$taftxt\n" );
		# 2008/01/22 11:00 TAF LFPO 221100Z 221818 20006KT 9999 SCT025 SCT040 BECMG 1820 6000 SCT004 TEMPO 2002 2000 BR BKN003 TEMPO 0408 -RADZ BKN006 BECMG 0810 BKN012 BECMG 1214 BKN018
		# 2008/01/22 17:00 TAF LFPO 221700Z 230024 20005KT 9999 SCT040 BECMG 0002 4000 BR BKN040 TEMPO 0205 2500 BR SCT006 BECMG 0810 19012KT BECMG 1416 BKN025
		push(@taflist,$taftxt);
	} else {
		prt( "FAILED to get URL $url ...\n" );
	}
}


sub show_taf {
	my ($taftxt) = shift;
	prt( "\nDECODE: $taftxt\n" );
	my %metar = decode_metar($taftxt);
	my $msg = '';
	foreach my $key (keys %metar) {
		my $hash = $metar{$key};
		#if (($key eq 'wind')||($key eq 'visibility')) { # ||($key eq 'clouds')) {
		$msg = "$key ";
		$msg .= '.' while (length($msg) < $minkey);
		$msg .= ': ';
		if (ref($hash) eq "HASH") {
			my $mg = '';
			foreach my $k (keys %{$hash}) {
				my $v = ${%{$hash}}{$k};
				$mg .= ', ' if length($mg);
				$mg .= "$k=$v";
			}
			$msg .= $mg;
		} else {
			$msg .= $metar{$key};
		}
		prt( "$msg\n" );
	}
}

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)';
	}
	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 prtw {
	my ($mg, $tf) = @_;
	if ($tf ne $acttaf) {
		$acttaf = $tf;
		push(@warnings,$tf);
	}
	prt($mg);
	chomp $mg;
	push(@warnings,$mg);
}

sub decode_metar {
	my ($tt) = shift;
	my ($part, $ppart, $had_dc, $msg);
	my ($v1,$v2,$v3,$v4,$v5,$v6, $tr, $tmpn, $tmptr);
	my @parts = split(/\s/, $tt);
	my $num_parts = scalar @parts;
	my %decoded_metar = ();
    $decoded_metar{'remarks'} = '';
	$part = '';
	$had_dc = 0;
	$msg = '';
	for (my $i = 0; $i < $num_parts; $i++) {
		$ppart = $part;
		$part = $parts[$i];
		$had_dc = 0;	# no DECODE yet
		# if (ereg('RMK|AFT|TEMPO|BECMG|INTER', $part))
		#if ($part =~ /(RMK|AFT|TEMPO|BECMG|INTER)/) {
		if (($part =~ /(RMK|AFT|TEMPO|BECMG|INTER)/)||($part eq 'BEC')||($part =~ /^BEC([0-9]{2,4})/) ) {
			$msg = "$part - The rest of the METAR is either a remark or temporary information. We keep the remark.";
		    $decoded_metar{'remarks'} = $part;
			$i++;
			for ( ; $i < $num_parts; $i++) {
				$part = $parts[$i];
			    $decoded_metar{'remarks'} .= ' '.$part;
			}
		    $decoded_metar{'remarks'} = trim_all($decoded_metar{'remarks'});
			$had_dc = 1;
			last;
		} elsif (($part eq 'METAR')||($part eq 'SPECI')||($part eq 'TAF')) {
			$msg = "$part - Type of Report: METAR, SPECI, TAF";
			$decoded_metar{'type'} = $part;
			$had_dc = 1;
		###} elsif ($part eq 'AMD') {
		} elsif ($part =~ /^AMD(.*)$/) {
			$msg = "$part - Qualifier of report.";
			$v1 = $1;
			$tr = 'qualifier';
			$tmptr = $tr;
			$tmpn = 0;
			while (defined $decoded_metar{$tmptr}) {
				$tmpn++;
				$tmptr = $tr.$tmpn;
			}
			if ($v1 && length($v1)) {
				$decoded_metar{$tmptr} = "Amendment $v1";	# $tr = 'qualifier';
			} else {
				$decoded_metar{$tmptr} = 'Amended';
			}
			$had_dc = 1;
		} elsif ($part =~ /^CAVOK(=)?/) {
			$msg = "$part - 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 
			$v1 = $1;
			if ($v1 && length($v1)) {
				$decoded_metar{'end-report'} = $part.' (Clear, no cloud 5000 feet, vis 6 miles+, no rain ...)';
				last;
			} else {
				$decoded_metar{'indication'} = $part.' (Clear, no cloud 5000 feet, vis 6 miles+, no rain ...)';
			}
			$had_dc = 1;
		} elsif ($part =~ /^[A-Z]{4,5}$/) {
			$msg = "$part - Station Identifier.";
			# $decoded_metar{'icao'}  = $part;
			$v1 = $part;
			$tr = 'icao';
			$tmptr = $tr;
			$tmpn = 0;
			while (defined $decoded_metar{$tmptr}) {
				$tmpn++;
				$tmptr = $tr.$tmpn;
			}
			$decoded_metar{$tmptr} = $v1;
			$had_dc = 1;
		} elsif ($part =~ /([0-9]{2})([0-9]{2})([0-9]{2})Z/) {
			$msg = "$part - DATE, HOUR, MINUTES Z.";
			$decoded_metar{'time'}{'day'} = $1;
			$decoded_metar{'time'}{'hour'} = $2;
			$decoded_metar{'time'}{'mins'} = $3;
			if (($i + 1) < $num_parts) {
				$part = $parts[$i+1];
				if ($part =~ /([0-9]{2})([0-9]{2})([0-9]{2})/) {
					$decoded_metar{'time2'}{'day'} = $1;
					##$decoded_metar{'time2'}{'hour'} = $2;
					##$decoded_metar{'time2'}{'mins'} = $3;
					$decoded_metar{'time2'}{'begin-hour'} = $2;
					$decoded_metar{'time2'}{'end-hour'} = $3;
					$i++;
				}
			}
			$had_dc = 1;
		} elsif ($part =~ /([0-9]{4})\/([0-9]{2})\/([0-9]{2})/) {
			$msg = "$part - got a DATE YYYY/MM/DD";
			$decoded_metar{'record_date'}{'year'} = $1;
			$decoded_metar{'record_date'}{'month'} = $2;
			$decoded_metar{'record_date'}{'day'} = $3;
			if (($i + 1) < $num_parts) {
				$part = $parts[$i+1];
				if ($part =~ /([0-9]{2}):([0-9]{2})/) {
					$decoded_metar{'record_date'}{'hour'} = $1;
					$decoded_metar{'record_date'}{'minutes'} = $2;
					$i++;
					$msg .= ", plus $part - HOUR, MINUTES";
				}
			}
			$had_dc = 1;
		} elsif ($part =~ /(AUTO|COR|RTD|CC[A-Z]|RR[A-Z])/ ) {
			$msg = "$part - Report Modifier: AUTO, COR, CCx or RRx";
			$v1 = $1;
			$decoded_metar{'report_mod'} = $v1;
			$had_dc = 1;
		} elsif ($part =~ /([0-9]{3}|VRB)([0-9]{2,3})G?([0-9]{2,3})?(KT|MPS|KMH)/) {
			$msg = "$part - Wind Group";
			# 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 = 'wind';
			$tmptr = $tr;
			$tmpn = 0;
			while (defined $decoded_metar{$tmptr}) {
				$tmpn++;
				$tmptr = $tr.$tmpn;
			}
			$decoded_metar{$tmptr}{'deg'} = $v1; # $tmptr = 'wind'.[nn]
			if ($v2 == 0) {
				$decoded_metar{$tmptr}{'knots'} = 0;
				$decoded_metar{$tmptr}{'meters_per_second'} = 0;
				$decoded_metar{$tmptr}{'miles_per_hour'} = 0;
			} else {
				if ($v4 eq 'KT') {
					$decoded_metar{$tmptr}{'knots'} = $v2;
					# The windspeed measured in meters per second, rounded to one decimal place
					# $meterspersec = number_format($value * 0.5144, 1);
					$decoded_metar{$tmptr}{'meters_per_second'} = number_format( ($v2 * 0.5144), 1 );
					# The windspeed measured in miles per hour, rounded to one decimal place
					$decoded_metar{$tmptr}{'miles_per_hour'} = number_format( ($v2 * 1.1508), 1 );
				} elsif ($v4 eq 'MPS') {
					# The windspeed measured in meters per second */
					$decoded_metar{$tmptr}{'meters_per_second'} = number_format( $v2, 1 );
					# The windspeed measured in knots, rounded to one decimal place
					$decoded_metar{$tmptr}{'knots'} = number_format($v2 / 0.5144, 1);
					# The windspeed measured in miles per hour, rounded to one decimal place
					$decoded_metar{$tmptr}{'miles_per_hour'} = number_format($v2 / 0.5144 * 1.1508, 1);
				} elsif ($v4 eq 'KMH') {
					# The windspeed measured in kilometers per hour
					$decoded_metar{$tmptr}{'meters_per_second'} = number_format($v2 * 1000 / 3600, 1);
					$decoded_metar{$tmptr}{'knots'} = number_format($v2 * 1000 / 3600 / 0.5144, 1);
					# The windspeed measured in miles per hour, rounded to one decimal place
					$decoded_metar{$tmptr}{'miles_per_hour'} = number_format($decoded_metar{'wind'}{'knots'} * 1.1508, 1);
				} else {
					$decoded_metar{$tmptr}{'knots'} = "$v2 (KTS assumed!)";
				}
			}
			$had_dc = 1;
		} elsif ($part =~ /^([0-9]{3})V([0-9]{3})$/) { # !empty($decoded_metar['wind'])) {
			$msg = "$part - Variable wind-direction";
			$decoded_metar{'wind'}{'var_beg'} = $1;
			$decoded_metar{'wind'}{'var_end'} = $2;
			$had_dc = 1;
		} elsif ($part =~ /^([0-9]{4})([NS]?[EW]?)$/) {
			$msg = "$part - Visibility in meters (4 digits only).";
			$v1 = $1;
			if ($v1 eq '0000') {
				$decoded_metar{'visibility'}{'prefix'} = -1;
				$decoded_metar{'visibility'}{'meter'}  = 50;
				$decoded_metar{'visibility'}{'km'}     = 0.05;
				$decoded_metar{'visibility'}{'ft'}     = 164;
				$decoded_metar{'visibility'}{'mile'}   = 0.031;
			} elsif ($v1 eq '9999') {
				# elseif ($regs[1] == '9999')
				# Special high value
				$decoded_metar{'visibility'}{'prefix'} = 1;
				$decoded_metar{'visibility'}{'meter'}  = 10000;
				$decoded_metar{'visibility'}{'km'}     = 10;
				$decoded_metar{'visibility'}{'ft'}     = 32800;
				$decoded_metar{'visibility'}{'mile'}   = 6.2;
			} else {
				# Normal visibility, returned in both small and large units.
				$decoded_metar{'visibility'}{'prefix'} = 0;
				$decoded_metar{'visibility'}{'km'}     = number_format($v1 / 1000, 1);
				$decoded_metar{'visibility'}{'meter'}  = number_format($v1, 1);
				$decoded_metar{'visibility'}{'ft'}     = number_format($v1 * 3.28084, 1);
				$decoded_metar{'visibility'}{'mile'}   = number_format($v1 / 1609.344, 1);
			}
			$had_dc = 1;
		} elsif ($part =~ /^(VC)?(-|\+)?(MI|PR|BC|DR|BL|SH|TS|FZ)?((DZ|RA|SN|SG|IC|PL|GR|GS|UP)+)?(BR|FG|FU|VA|DU|SA|HZ|PY)?(PO|SQ|FC|SS)?$/ ) {
			# } 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 */
			$msg = "$part - Weather.";
			$v1 = $1;
			$v2 = $2;
			$v3 = $3;
			$v4 = $4;
			$v5 = $5;
			$v6 = $6;
			$tr = 'weather';
			$tmptr = $tr;
			$tmpn = 0;
			while (defined $decoded_metar{$tmptr}) {
				$tmpn++;
				$tmptr = $tr.$tmpn;
			}
			# $decoded_metar{'weather'}{'proximity'}     = $v1 if ($v1 && length($v1));
			if ($v1 && length($v1)) {
				if ($v1 eq 'VC') {
					$decoded_metar{$tmptr}{'proximity'}     = 'outside aerodrome (5-10km)'; # 'weather'.[nn]
				} else {
					$decoded_metar{$tmptr}{'proximity'}     = 'outside aerodrome (5-10km) $v1 CHECKME';
				}
			} else {
				$decoded_metar{$tmptr}{'proximity'}     = 'at aerodrome';
			}
			###$decoded_metar{'weather'}{'intensity'}     = $v2 if ($v2 && length($v2));
			if ($v2 && length($v2)) {
				if ($v2 eq '-') {
					$decoded_metar{$tmptr}{'intensity'} = 'light (-)';
				} elsif ($v2 eq '+') {
					$decoded_metar{$tmptr}{'intensity'} = 'heavy (+)';
				} else {
					$decoded_metar{$tmptr}{'intensity'} = 'unknown ($v2) CHECKME';
				}
			} else {
				# no sign
				$decoded_metar{$tmptr}{'intensity'} = 'moderate (no sign)';
			}
			$decoded_metar{$tmptr}{'decriptor'}     = get_descriptor($v3) if ($v3 && length($v3));
			$decoded_metar{$tmptr}{'precipitation'} = get_precipitation($v4) if ($v4 && length($v4));
			$decoded_metar{$tmptr}{'obscuration'}   = get_obscuration($v5) if ($v5 && length($v5));
			$decoded_metar{$tmptr}{'other'}         = get_other($v6) if ($v6 && length($v6));
			$had_dc = 1;
			# ================================================================================
		} elsif (($part eq 'SKC') || ($part eq 'CLR') || ($part eq 'NSC')) {
			$msg = "$part - Cloud-group.";
			#$decoded_metar{'clouds'}{'condition'} = $part;
			$decoded_metar{'conditions'} = $part.' (Clear)';
			$had_dc = 1;
		###} elsif ($part =~ /^(VV|FEW|SCT|BKN|OVC)([0-9]{3}|\/\/\/)(CB|TCU)?$/ ) {
		} elsif ($part =~ /^(VV|FEW|SCT|BKN|OVC)([0-9]{2,3}|\/\/\/)(CB|TCU)?$/ ) {
			$msg = "$part - We have found (another) a cloud-layer-group.";
			$v1 = $1;
			$v2  = $2;
			$v3  = $3;
			$tr = 'clouds';
			$tmptr = $tr;
			$tmpn = 0;
			while (defined $decoded_metar{$tmptr}) {
				$tmpn++;
				$tmptr = $tr.$tmpn;
			}
			if ($v2 eq '000') {
				# if ($regs[2] == '000')
				# '000' is a special height.
				$decoded_metar{$tmptr}{'ft'}     = 100; # $tr = 'clouds'.[nn]
				$decoded_metar{$tmptr}{'meter'}  = 30;
				$decoded_metar{$tmptr}{'prefix'} = -1; #/* Less than */
			} elsif ($v2 eq '///') {
				# '///' means height nil
				$decoded_metar{$tmptr}{'ft'}     = 'nil';
				$decoded_metar{$tmptr}{'meter'}  = 'nil';
			} else {
				$decoded_metar{$tmptr}{'ft'}     = $v2 * 100;
				$decoded_metar{$tmptr}{'meter'}  = int($v2 * 30.48);
			}
			$decoded_metar{$tmptr}{'cond'}   = $v3 if ($v3 && length($v3));
			$had_dc = 1;
			################################################################
		} elsif ($part =~ /^(M?[0-9]{2})\/(M?[0-9]{2}|\/\/)?$/) {
			$msg = "$part - Temperature/Dew Point Group.";
			# 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. 
			$v1 = $1;
			$v2 = $2;
			$v3 = '';
			if ($v1 && length($v1) && (substr($v1,0,1) eq 'M')) {
				$v3 = '-';
			}
			$decoded_metar{'temperature'}{'temp_c'} = $v3.int($v1);
			$decoded_metar{'temperature'}{'temp_f'} = $v3.int( ($v1 * (9/5)) + 32 );
			# 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{'temperature'}{'dew_c'} = $v3.int($v2);
				$decoded_metar{'temperature'}{'dew_f'} = $v3.int(($v2 * (9/5)) + 32).'M-';
			}
			$had_dc = 1;
		} elsif ($part =~ /A([0-9]{4})/) {
			$msg = "$part - Altimeter.";
			# The pressure measured in inHg.
			$v1 = $1;
			$tr = 'altimeter';
			$tmptr = $tr;
			$tmpn = 0;
			while (defined $decoded_metar{$tmptr}) {
				$tmpn++;
				$tmptr = $tr.$tmpn;
			}
			$decoded_metar{$tmptr}{'inhg'} = number_format($v1/100, 2); # $tmptr = 'altimeter'.[nn]
			# The pressure measured in mmHg, hPa and atm */
			$decoded_metar{$tmptr}{'mmhg'} = number_format($v1 * 0.254, 1);
			$decoded_metar{$tmptr}{'hpa'}  = int($v1 * 0.33864);
			$decoded_metar{$tmptr}{'atm'}  = number_format(($v1 * 3.3421e-4), 3);
			$msg .= " ($tmptr)";
			$had_dc = 1;
		} elsif ( $part =~ /^Q([0-9]{4})/ ) {
			$msg = "$part - Q Altimeter.";
			$v1 = $1;
			$tr = 'altimeter';
			$tmptr = $tr;
			$tmpn = 0;
			while (defined $decoded_metar{$tmptr}) {
				$tmpn++;
				$tmptr = $tr.$tmpn;
			}
			# The specification doesn't say anything about
			# the Qxxxx-form, but it's in the METARs.
			# /* The pressure measured in hPa */
			$decoded_metar{$tmptr}{'hpa'}  = int($v1);	# $tmptr = 'altimeter'.[nn]
			# /* The pressure measured in mmHg, inHg and atm */
			$decoded_metar{$tmptr}{'mmhg'} = number_format($v1 * 0.75006, 1);
			$decoded_metar{$tmptr}{'inhg'} = number_format($v1 * 0.02953, 2);
			$decoded_metar{$tmptr}{'atm'}  = number_format($v1 * 9.8692e-4, 3);
			$msg .= " ($tmptr)";
			$had_dc = 1;
		} elsif ( $part =~ /^QNH([0-9]{4})INS/ ) {
			$msg = "$part - QNH Altimeter.";
			$v1 = $1;
			$tr = 'altimeter';
			$tmptr = $tr;
			$tmpn = 0;
			while (defined $decoded_metar{$tmptr}) {
				$tmpn++;
				$tmptr = $tr.$tmpn;
			}
			$decoded_metar{$tmptr}{'inhg'} = number_format($v1 / 100, 2);	# $tmptr = 'altimeter'.[nn]
			$decoded_metar{$tmptr}{'mmhg'} = number_format($v1 * 0.254, 2);	# 100 inch to mm
			$decoded_metar{$tmptr}{'hpa'}   = number_format($v1 * 0.338639, 2);
			$decoded_metar{$tmptr}{'atm'}  = number_format(($v1 * 3.3421e-4), 3);
			$msg .= " ($tmptr)";
			$had_dc = 1;
		} elsif ($part =~ /^T([0-9]{4})([0-9]{4})/ ) {
			prtw("WARNING:1: [$part] NOT YET HANDLED!\n", $tt );
#         * 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']);
		} elsif ($part =~ /^T([0-9]{4}$)/) {
			prtw("WARNING:2: [$part] NOT YET HANDLED!\n", $tt );
#	$this->store_temp($regs[1],
#			  $decoded_metar['temperature']['temp_c'],
#			  $decoded_metar['temperature']['temp_f']);
		} elsif ($part =~ /^1([0-9]{4}$)/) {
			prtw("WARNING:3: [$part] NOT YET HANDLED!\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}$)/) {
			prtw("WARNING:4: [$part] NOT YET HANDLED!\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})$/) {
			prtw("WARNING:5: [$part] NOT YET HANDLED!\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})/) {
			prtw("WARNING:6: [$part] NOT YET HANDLED!\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})/) {
			$msg = "$part - Precipitation during last 3 or 6 hours in hundredths of an inch.";
			$v1 = $1;
			$tr = 'precititation';
			$tmptr = $tr;
			$tmpn = 0;
			while (defined $decoded_metar{$tmptr}) {
				$tmpn++;
				$tmptr = $tr.$tmpn;
			}
			if ($v1 eq '0000') {
				$decoded_metar{$tmptr}{'in_6h'} = -1;
				$decoded_metar{$tmptr}{'mm_6h'} = -1;
			} else {
				$decoded_metar{$tmptr}{'in_6h'} = number_format($v1/100, 2);
				$decoded_metar{$tmptr}{'mm_6h'} = number_format($v1*0.254, 2);
			}
			$msg .= " ($tmptr)";
			$had_dc = 1;
		} elsif ($part =~ /^7([0-9]{4})/ ) {
			prtw("WARNING:8: [$part] NOT YET HANDLED!\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})/) {
			prtw("WARNING:9: [$part] NOT YET HANDLED!\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})/) {
			$msg = "$part - Probability.";
			# 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. 
			$v1 = $1;
			$decoded_metar{'probability'}{'percent'} = $v1;
			if (($i + 1) < $num_parts) {
				$part = $parts[$i+1];
				if ($part =~ /^([0-9]{2})([0-9]{2})$/) {
					$v1 = $1;
					$v2 = $2;
					$decoded_metar{'probability'}{'start'} = $v1;
					$decoded_metar{'probability'}{'end'} = $v2;
					$msg .= " (Bgn=$v1, End=$v2)";
					$i++;
				}
			}
			$had_dc = 1;
		} elsif ($part =~ /^M?(([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;
			prtw("WARNING:10: [$part] NOT YET HANDLED! v1=[$v1] v2=[$v2] v3=[$v3] v4=[$v4] \n", $tt );
		} elsif ($part =~ /([0-9]{2})([0-9]{2})([0-9]{2})/) {
			$v1 = $1;
			$v2 = $2;
			$v3 = $3;
			$tr = 'time-range';
			$tmptr = $tr;
			$tmpn = 0;
			while (defined $decoded_metar{$tmptr}) {
				$tmpn++;
				$tmptr = $tr.$tmpn;
			}
			$decoded_metar{$tmptr}{'day'} = $v1;
			$decoded_metar{$tmptr}{'begin-hour'} = $v2;
			$decoded_metar{$tmptr}{'end-hour'} = $v3;
		} elsif ($part =~ /^FM([0-9]{2})([0-9]{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.
			$v1 = $1;
			$v2 = $2;
			$tr = 'time-range';
			$tmptr = $tr;
			$tmpn = 0;
			while (defined $decoded_metar{$tmptr}) {
				$tmpn++;
				$tmptr = $tr.$tmpn;
			}
			$decoded_metar{$tmptr}{'begin-hour'} = $v1;
			$decoded_metar{$tmptr}{'begin-mins'} = $v2;
		} 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 = 'time-range';
			$tmptr = $tr;
			$tmpn = 0;
			while (defined $decoded_metar{$tmptr}) {
				$tmpn++;
				$tmptr = $tr.$tmpn;
			}
			$decoded_metar{$tmptr}{'begin-hour'} = $v1;
			$decoded_metar{$tmptr}{'begin-mins'} = $v2;
		} else {
#      } else {
#         * If we couldn't match the group, we assume that it was a remark.
#	$decoded_metar['remarks'] .= ' ' . $part;
#      }
#    }
			prtw("WARNING:ELSE: [$part] NO CASE FOR THIS!\n", $tt );
			if (defined $decoded_metar{'UNDECODED'}) {
				$decoded_metar{'UNDECODED'} .= ' '.$part;
			} else {
				$decoded_metar{'UNDECODED'} = $part;
			}
		}
	}

	return %decoded_metar;
}

#  function decode_metar($index=false) {
#    /* initialization */
#    $temp_visibility_miles = '';
#    $decoded_metar['remarks'] = '';
#    $decoded_metar['location'] = $this->get_location();
#
#    /* Make sure we got the metar */
#    $tmp_metar = $this->get_metar();
#
#    /* Setup other variables */
#    if ($index===false) { /* We are getting the current METAR */
#      $decoded_metar['metar'] = $tmp_metar;
#      $decoded_metar['time'] = $this->metar_time;
#    }
#    else {
#      if ($this->metar_arch===false) { /* error */
#	return false;
#      } 
#      $tmp_metar = $this->metar_arch[$index]['metar'];
#      $decoded_metar['metar'] = $tmp_metar;
#      $decoded_metar['time'] = $this->metar_arch[$index]['time'];
#    }
#
#    /* We parse the METAR */
#    $parts = explode(' ', $tmp_metar);
#    $num_parts = count($parts);
#    
#    for ($i = 0; $i < $num_parts; $i++) {
#      $part = $parts[$i];
#      
#      if (ereg('RMK|AFT|TEMPO|BECMG|INTER', $part)) {
#        /* The rest of the METAR is either a remark or temporary
#         * information. We keep the remark.
#         */
#	for($j=$i;$j<$num_parts; $j++)
#	  $decoded_metar['remarks'] .= ' ' . $parts[$j];
#	$decoded_metar['remarks'] = trim($decoded_metar['remarks']);
# 	break;
#      } elseif ($part == 'METAR') {
#        /*
#         * Type of Report: METAR
#         */
#	$decoded_metar['type'] = 'METAR';
#      } elseif ($part == 'SPECI') {
#        /*
#         * Type of Report: SPECI
#         */
#	$decoded_metar['type'] = 'SPECI';
#      } elseif (ereg('^[A-Z]{4}$', $part) &&
#                empty($decoded_metar['icao']))  {
#        /*
#         * Station Identifier
#         */
#	$decoded_metar['icao']  = $part;
#//       } elseif (ereg('([0-9]{2})([0-9]{2})([0-9]{2})Z', $part, $regs)) {
#//         /*
#//          * Date and Time of Report.
#//          *
#//          * We return a standard Unix UTC/GMT timestamp suitable for
#//          * gmdate().
#//          *
#//          * If you experience incorrect timestamps but cannot set the
#//          * clock, then you can set $this->properties['offset'] to be
#//          * the offset in hours to add. For example, if your times
#//          * generated are 1 hour too early (so METARs appear an hour
#//          * older than they are), set $this->properties['offset'] to be
#//          * +1 in your defaults.php file.
#//          */
#
#//  	if ($regs[1] > gmdate('j')) {
#//            /* The day is greather that the current day of month => the
#//             * report is from last month. 
#//             */
#//  	  $month = gmdate('n') - 1;
#//  	} else {
#//  	  $month = gmdate('n');
#//  	}
#
#//  	$decoded_metar['time'] =
#//           gmmktime($regs[2] + $this->properties['offset'],
#//                    $regs[3], 0, $month, $regs[1], gmdate('Y'));
#
#
#      } elseif (ereg('(AUTO|COR|RTD|CC[A-Z]|RR[A-Z])', $part, $regs)) {
#        /*
#         * Report Modifier: AUTO, COR, CCx or RRx
#         */
#        $decoded_metar['report_mod'] = $regs[1];
#      } elseif (ereg('([0-9]{3}|VRB)([0-9]{2,3})G?([0-9]{2,3})?(KT|MPS|KMH)', $part, $regs)) {
#        
#        /* Wind Group */
#	
#	$decoded_metar['wind']['deg'] = $regs[1];
#	
#	$this->store_speed($regs[2],
#			   $regs[4],
#			   $decoded_metar['wind']['knots'],
#			   $decoded_metar['wind']['meters_per_second'],
#			   $decoded_metar['wind']['miles_per_hour']);
#	
#	if (!empty($regs[3])) {
#          
#          /* We have a report with information about the gust.
#           * First we have the gust measured in knots.
#           */
#	  $this->store_speed($regs[3],
#			     $regs[4],
#			     $decoded_metar['wind']['gust_knots'],
#			     $decoded_metar['wind']['gust_meters_per_second'],
#			     $decoded_metar['wind']['gust_miles_per_hour']);
#	}
#      } elseif (ereg('^([0-9]{3})V([0-9]{3})$', $part, $regs) &&
#                !empty($decoded_metar['wind'])) {
#        
#        /*
#         * Variable wind-direction
#         */
#        $decoded_metar['wind']['var_beg'] = $regs[1];
#	$decoded_metar['wind']['var_end'] = $regs[2];
#      } elseif (ereg('^([0-9]{4})([NS]?[EW]?)$', $part, $regs)) {
#        /* 
#         * Visibility in meters (4 digits only)
#         */
#        unset($group);
#
#        if ($regs[1] == '0000') {
#          /* Special low value */
#          
#	  $group['prefix'] = -1; /* Less than */
#          $group['meter']  = 50;
#          $group['km']     = 0.05;
#          $group['ft']     = 164;
#          $group['miles']  = 0.031;
#	} elseif ($regs[1] == '9999') {
#          /* Special high value */
#          $group['prefix'] = 1; 
#          $group['meter']  = 10000;
#          $group['km']     = 10;
#          $group['ft']     = 32800;
#          $group['miles']  = 6.2;
#	} else {
#          /* Normal visibility, returned in both small and large units. */
#          $group['prefix'] = 0; 
#          $group['km']     = number_format($regs[1]/1000, 1);
#          $group['miles']  = number_format($regs[1]/1609.344, 1);
#          $group['meter']  = $regs[1] * 1;
#          $group['ft']     = round($regs[1] * 3.28084);
#	}
#	if (!empty($regs[2])) {
#	  $group['deg'] = $regs[2];
#	}
#        $decoded_metar['visibility'][] = $group;
#
#      } elseif (ereg('^[0-9]$', $part)) {
#        /*
#         * Temp Visibility Group, single digit followed by space.
#         */
#        $temp_visibility_miles = $part;
#      } elseif (ereg('^M?(([0-9]?)[ ]?([0-9])(/?)([0-9]*))SM$',
#                     $temp_visibility_miles . ' ' . $part, $regs)) {
#        /*
#         * Visibility Group
#         */
#        unset($group);
#
#	if ($regs[4] == '/') {
#	  $vis_miles = $regs[2] + $regs[3]/$regs[5];
#        } else {
#          $vis_miles = $regs[1];
#        }
#        if ($regs[0][0] == 'M') {
#          /* Prefix - less than */
#          $group['prefix'] = -1;
#        } else {
#          $group['prefix'] = 0;
#        }
#        
#        /* The visibility measured in miles */
#        $group['miles']  = number_format($vis_miles, 1);
#        
#        /* The visibility measured in feet */
#        $group['ft']     = round($vis_miles * 5280, 1);
#        
#        /* The visibility measured in kilometers */
#        $group['km']     = number_format($vis_miles * 1.6093, 1);
#        
#        /* The visibility measured in meters */
#        $group['meter']  = round($vis_miles * 1609.3);
#
#        $decoded_metar['visibility'][] = $group;
#      } elseif ($part == 'CAVOK') {
#        /* CAVOK is used when the visibility is greater than 10
#         * kilometers, the lowest cloud-base is at 5000 feet or more
#         * and there is no significant weather.
#         */
#        unset($group);
#        $group['prefix'] = 1; 
#        $group['km']     = 10;
#        $group['meter']  = 10000;
#        $group['miles']  = 6.2;
#        $group['ft']     = 32800;
#        $decoded_metar['visibility'][] = $group;
#        $decoded_metar['clouds'][]['condition'] = 'CAVOK';
#
#      } elseif (ereg('^R([0-9]{2})([RLC]?)/([MP]?)([0-9]{4})' .
#                     '([DNU]?)V?(P?)([0-9]{4})?([DNU]?)$', $part, $regs)) {
#        /* Runway-group */
#        unset($group);
#        $group['nr'] = $regs[1];
#	if (!empty($regs[2])) {
#	  $group['approach'] = $regs[2];
#	}
#	
#	if (!empty($regs[7])) {
#          /* We have both min and max visibility since $regs[7] holds
#           * the max visibility.
#           */
#          if (!empty($regs[5])) { 
#            /* $regs[5] is tendency for min visibility. */
#            $group['min_tendency'] = $regs[5];
#	  }
#	  
#          if (!empty($regs[8])) { 
#            /* $regs[8] is tendency for max visibility. */
#            $group['max_tendency'] = $regs[8];
#	  }
#	  
#	  if ($regs[3] == 'M') {
#            /* Less than. */
#            $group['min_prefix'] = -1;
#	  }
#          $group['min_meter'] = $regs[4] * 1;
#          $group['min_ft']    = round($regs[4] * 3.2808);
#	  
#	  if ($regs[6] == 'P') {
#            /* Greater than. */
#            $group['max_prefix'] = 1;
#	  }
#          $group['max_meter'] = $regs[7] * 1;
#          $group['max_ft']    = round($regs[7] * 3.2808);
#	  
#	} else {
#          /* We only have a single visibility. */
#          
#          if (!empty($regs[5])) { 
#            /* $regs[5] holds the tendency for visibility. */
#            $group['tendency'] = $regs[5];
#	  }
#	  
#	  if ($regs[3] == 'M') {
#            /* Less than. */
#            $group['prefix'] = -1;
#	  } elseif ($regs[3] == 'P') {
#            /* Greater than. */
#            $group['prefix'] = 1;
#	  }
#          $group['meter'] = $regs[4] * 1;
#          $group['ft']    = round($regs[4] * 3.2808);
#	}
#        $decoded_metar['runway'][] = $group;
#        
#      } 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 */
#		     $part, $regs)) {
#        /*
#         * Current weather-group.
#         */
#        $decoded_metar['weather'][] =
#          array('proximity'     => $regs[1],
#                'intensity'     => $regs[2],
#                'descriptor'    => $regs[3],
#                'precipitation' => $regs[4],
#                'obscuration'   => $regs[6],
#                'other'         => $regs[7]);
#        
#      } elseif ($part == 'SKC' || $part == 'CLR') {
#        /* Cloud-group */
#        $decoded_metar['clouds'][]['condition'] = $part;
#
#      } elseif (ereg('^(VV|FEW|SCT|BKN|OVC)([0-9]{3}|///)' .
#                     '(CB|TCU)?$', $part, $regs)) {
#        /* We have found (another) a cloud-layer-group. */
#        unset($group);
#
#	$group['condition'] = $regs[1];
#	if (!empty($regs[3])) {
#	  $group['cumulus'] = $regs[3];
#	}
#	if ($regs[2] == '000') {
#          /* '000' is a special height. */
#          $group['ft']     = 100;
#          $group['meter']  = 30;
#          $group['prefix'] = -1; /* Less than */
#	} elseif ($regs[2] == '///') {
#          /* '///' means height nil */
#          $group['ft']     = 'nil';
#          $group['meter']  = 'nil';
#	} else {
#          $group['ft']     = $regs[2] *100;
#          $group['meter']  = round($regs[2] * 30.48);
#	}
#        $decoded_metar['clouds'][] = $group;
#
#      } elseif (ereg('^(M?[0-9]{2})/(M?[0-9]{2}|//)?$', $part, $regs)) {
#        /*
#         * Temperature/Dew Point Group.
#         */
#        $decoded_metar['temperature']['temp_c'] =
#          round(strtr($regs[1], 'M', '-'));
#        $decoded_metar['temperature']['temp_f'] =
#          round(strtr($regs[1], 'M', '-') * (9/5) + 32);
#        
#        /* 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... */
#	if (!empty($regs[2]) && $regs[2] != '//') {
#          $decoded_metar['temperature']['dew_c'] =
#            round(strtr($regs[2], 'M', '-'));
#          $decoded_metar['temperature']['dew_f'] =
#            round(strtr($regs[2], 'M', '-') * (9/5) + 32);
#	}
#      } elseif (ereg('A([0-9]{4})', $part, $regs)) {
#        /*
#         * Altimeter.
#         * The pressure measured in inHg.
#         */
#        $decoded_metar['altimeter']['inhg'] =
#          number_format($regs[1]/100, 2);
#        
#        /* The pressure measured in mmHg, hPa and atm */
#        $decoded_metar['altimeter']['mmhg'] =
#          number_format($regs[1] * 0.254, 1, '.', '');
#        $decoded_metar['altimeter']['hpa']  =
#          round($regs[1] * 0.33864);
#	$decoded_metar['altimeter']['atm']  =
#          number_format($regs[1] * 3.3421e-4, 3, '.', '');
#      } elseif (ereg('Q([0-9]{4})', $part, $regs)) {
#        /*
#         * Altimeter.
#         * The specification doesn't say anything about
#         * the Qxxxx-form, but it's in the METARs.
#         */
#        
#        /* The pressure measured in hPa */
#        $decoded_metar['altimeter']['hpa']  = round($regs[1]);
#        
#        /* The pressure measured in mmHg, inHg and atm */
#        $decoded_metar['altimeter']['mmhg'] =
#          number_format($regs[1] * 0.75006, 1, '.', '');
#        $decoded_metar['altimeter']['inhg'] =
#          number_format($regs[1] * 0.02953, 2);
#        $decoded_metar['altimeter']['atm']  =
#          number_format($regs[1] * 9.8692e-4, 3, '.', '');
#      } elseif (ereg('^T([0-9]{4})([0-9]{4})', $part, $regs)) {
#        
#        /*
#         * 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']);
#      } elseif (ereg('^T([0-9]{4}$)', $part, $regs)) {
#	$this->store_temp($regs[1],
#			  $decoded_metar['temperature']['temp_c'],
#			  $decoded_metar['temperature']['temp_f']);
#      } elseif (ereg('^1([0-9]{4}$)', $part, $regs)) {
#        
#        /*
#         * 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']);
#      } elseif (ereg('^2([0-9]{4}$)', $part, $regs)) {
#        
#        /*
#         * 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']);
#      } elseif (ereg('^4([0-9]{4})([0-9]{4})$', $part, $regs)) {
#        
#        /*
#         * 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']);
#      } elseif (ereg('^P([0-9]{4})', $part, $regs)) {
#        
#        /*
#         * 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);
#	}
#      } elseif (ereg('^6([0-9]{4})', $part, $regs)) {
#        
#        /*
#         * Precipitation during last 3 or 6 hours in hundredths of an
#         * inch.
#         */
#	if ($regs[1] == '0000') {
#	  $decoded_metar['precipitation']['in_6h'] = -1;
#	  $decoded_metar['precipitation']['mm_6h'] = -1;
#	} else {
#          $decoded_metar['precipitation']['in_6h'] =
#            number_format($regs[1]/100, 2);
#          $decoded_metar['precipitation']['mm_6h'] =
#            number_format($regs[1]*0.254, 2);
#	}
#      } elseif (ereg('^7([0-9]{4})', $part, $regs)) {
#        
#        /*
#         * 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, '.', '');
#	}
#      } elseif (ereg('^4/([0-9]{3})', $part, $regs)) {
#        
#        /*
#         * 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);
#	}
#      } else {
#        
#        /*
#         * If we couldn't match the group, we assume that it was a
#         * remark.
#         */
#	$decoded_metar['remarks'] .= ' ' . $part;
#      }
#    }
#    
#    /*
#     * Relative humidity
#     */
#    if (!empty($decoded_metar['temperature']['temp_c']) &&
#	!empty($decoded_metar['temperature']['dew_c'])) {
#
#      $decoded_metar['rel_humidity'] =
#        number_format(pow(10, (1779.75 * ($decoded_metar['temperature']['dew_c'] -
#                                          $decoded_metar['temperature']['temp_c'])
#                               / ((237.3 + $decoded_metar['temperature']['dew_c']) *
#                                  (237.3 + $decoded_metar['temperature']['temp_c']))
#                               + 2)), 1);
#    } 
#    
#    
#    /*
#     *  Compute windchill if temp < 40f and windspeed > 3 mph
#     */
#    if (!empty($decoded_metar['temperature']['temp_f']) && 
#        $decoded_metar['temperature']['temp_f'] <= 40 &&
#        !empty($decoded_metar['wind']['miles_per_hour']) &&
#        $decoded_metar['wind']['miles_per_hour'] > 3) {
#      $decoded_metar['windchill']['windchill_f'] = 
#        number_format(35.74 + 0.6215*$decoded_metar['temperature']['temp_f'] 
#                      - 35.75*pow((float)$decoded_metar['wind']['miles_per_hour'], 0.16) 
#                      + 0.4275*$decoded_metar['temperature']['temp_f'] * 
#                      pow((float)$decoded_metar['wind']['miles_per_hour'], 0.16));
#      $decoded_metar['windchill']['windchill_c'] = 
#        number_format(13.112 + 0.6215*$decoded_metar['temperature']['temp_c']
#                      - 13.37*pow(($decoded_metar['wind']['miles_per_hour']/1.609), 0.16)
#                      + 0.3965*$decoded_metar['temperature']['temp_c'] *
#                      pow(($decoded_metar['wind']['miles_per_hour']/1.609), 0.16));
#    }
#	
#    /*
#     * Compute heat index if temp > 70F
#     */
#    if (!empty($decoded_metar['temperature']['temp_f']) &&
#        $decoded_metar['temperature']['temp_f'] > 70 &&
#        !empty($decoded_metar['rel_humidity'])) {
#      $decoded_metar['heatindex']['heatindex_f'] =
#        number_format(-42.379
#                      + 2.04901523 * $decoded_metar['temperature']['temp_f']
#                      + 10.1433312 * $decoded_metar['rel_humidity']
#                      - 0.22475541 * $decoded_metar['temperature']['temp_f']
#                                   * $decoded_metar['rel_humidity']
#                      - 0.00683783 * $decoded_metar['temperature']['temp_f']
#                                   * $decoded_metar['temperature']['temp_f']
#                      - 0.05481717 * $decoded_metar['rel_humidity']
#                                   * $decoded_metar['rel_humidity']
#                      + 0.00122874 * $decoded_metar['temperature']['temp_f']
#                                   * $decoded_metar['temperature']['temp_f']
#                                   * $decoded_metar['rel_humidity']
#                      + 0.00085282 * $decoded_metar['temperature']['temp_f']
#                                   * $decoded_metar['rel_humidity']
#                                   * $decoded_metar['rel_humidity']
#                      - 0.00000199 * $decoded_metar['temperature']['temp_f']
#                                   * $decoded_metar['temperature']['temp_f']
#                                   * $decoded_metar['rel_humidity']
#                                   * $decoded_metar['rel_humidity']);
#     $decoded_metar['heatindex']['heatindex_c'] =
#       number_format(($decoded_metar['heatindex']['heatindex_f'] - 32) / 1.8);
#    }
#
#    /*
#     * Compute the humidity index
#     */
#    if (!empty($decoded_metar['rel_humidity'])) {
#      $e = (6.112 * pow(10, 7.5 * $decoded_metar['temperature']['temp_c']
#                        / (237.7 + $decoded_metar['temperature']['temp_c']))
#            * $decoded_metar['rel_humidity'] / 100) - 10;
#      $decoded_metar['humidex']['humidex_c'] =
#        number_format($decoded_metar['temperature']['temp_c'] + 5/9 * $e, 1);
#      $decoded_metar['humidex']['humidex_f'] =
#        number_format($decoded_metar['humidex']['humidex_c'] * 9/5 + 32, 1);
#    }
#
#
#    /* Finally we store our decoded METAR in $this->decoded_metar so
#     * that other methods can use it.
#     */
#    if ($index===false) $this->decoded_metar = $decoded_metar;
#    else $this->decoded_metar_arch[$index] = $decoded_metar;
#
#    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" );
	}
}

# eof
