#!/usr/bin/perl -w
# NAME: findaps.pl
# AIM: There is a BIG findap[nn].pl - This is a SIMPLER version
# 17/10/2014 - Change -i -c = no case change on name
# 16/10/2014 - Use later fgdata 3.3, thus add typ=100
# 14/04/2013 - Use later fgdata 2.10
# 19/05/2012 - Add more output according to verbosity
# 20/03/2012 - On help output the apt.dat file name
# 20/01/2012 - Output airport names correctly 'cased', and FIX sub not_on_track($)
#              and add nav, fixes and airways loads
# 2011-12-12 - Also compiled and run in Ubuntu, and added -m num to change output count
use strict;
use warnings;
use File::Basename;  # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] )
use Cwd;
my $os = $^O;
my $perl_dir = 'C:\GTools\perl';
my $out_dir = $perl_dir;
my $dir_sep = "\\";
my $CDATROOT="C:/FG/fgdata";
my $XDROOT="X:\\fgdata";
#my $CDATROOT="C:/FGCVS/FlightGear/data";
if ( !($os =~ /Win/i) ) {
    $perl_dir = "/home/geoff/bin";
    $out_dir = "/tmp";
    $dir_sep = '/';
    $CDATROOT="/home/geoff/fg/fg16/fgfs/data";
}
unshift(@INC, $perl_dir);
require 'lib_utils.pl' or die "Unable to load 'lib_utils.pl' Check paths in \@INC...\n";
require 'fg_wsg84.pl' or die "Unable to load fg_wsg84.pl ...\n";
require "Bucket2.pm" or die "Unable to load Bucket2.pm ...\n";
# my $CDATROOT="C:/FGCVS/FlightGear/data";
if (-d $XDROOT) {
    $CDATROOT=$XDROOT;
}
# =============================================================================
# This NEEDS to be adjusted to YOUR particular default location of these files.
my $FGROOT = (exists $ENV{'FG_ROOT'})? $ENV{'FG_ROOT'} : $CDATROOT;
#my $FGROOT = (exists $ENV{'FG_ROOT'})? $ENV{'FG_ROOT'} : "C:/FG/27/data";
# file spec : http://data.x-plane.com/file_specs/Apt810.htm
my $APTFILE 	  = "$FGROOT/Airports/apt.dat.gz";	# the airports data file
my $NAVFILE 	  = "$FGROOT/Navaids/nav.dat.gz";	# the NAV, NDB, etc. data file
# add these files
my $FIXFILE 	  = "$FGROOT/Navaids/fix.dat.gz";	# the FIX data file
my $AWYFILE       = "$FGROOT/Navaids/awy.dat.gz";   # Airways data
# =============================================================================

my $MY_F2M = 0.3048;
my $MY_M2F = 3.28083989501312335958;
my $SG_METER_TO_NM = 0.0005399568034557235;

# log file stuff
our ($LF);
my $pgmname = $0;
if ($pgmname =~ /(\\|\/)/) {
    my @tmpsp = split(/(\\|\/)/,$pgmname);
    $pgmname = $tmpsp[-1];
}
my $outfile = $out_dir.$dir_sep."temp.$pgmname.txt";
open_log($outfile);

# user variables
my $VERS = "0.0.4 2014-10-16";  # output nicely cased airport names, and add nav, fixes and airways
# $VERS = "0.0.3 2012-01-20";  # output nicely cased airport names, and add nav, fixes and airways
# $VERS = "0.0.2 2011-12-12";
my $load_log = 0;
my $in_icao = '';
my $verbosity = 0;
#my $out_xml = '';
my $g_max_out = 20;
# EXCLUDED from list
my $g_xhele = 0;
my $g_xsea  = 0;
my $g_xold  = 0;
my $g_track = -1;   # no track
my $g_spread = 30;  # +/- this spread
my $name_as_is = 0;
my $show_navaids = 0;
my $show_fixes = 0;
my $show_airways = 0;
my $show_bounds = 0;
my $only_with_ils = 0;

my $aptdat = $APTFILE;
my $navdat = $NAVFILE;
my $g_fixfile = $FIXFILE;
my $g_awyfile = $AWYFILE;

# format constants
my $g_distmin = 7;
my $g_altmin = 7;
my $g_frqmin = 5;
my $g_rngmin = 5;

### Debug
my $debug_on = 0;
my $del_icao = 'KTEX';

### program variables
my @warnings = ();
my $cwd = cwd();

my $rnavaids;

my $g_clat = 400;
my $g_clon = 400;

my ($g_minlat,$g_minlon,$g_maxlat,$g_maxlon,$g_minalt,$g_maxalt);

# 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

# Radio Frequencies # AWOS (Automatic Weather Observation System), ASOS (Automatic Surface Observation System)
my $minatc = '50'; # ATIS (Automated Terminal Information System). AWIS (Automatic Weather Information Service)
my $unicom = '51'; # Unicom or CTAF (USA), radio (UK) - open channel for pilot position reporting at uncontrolled airports.
my $cleara = '52'; # Clearance delivery.
my $goundf = '53'; # ground
my $twrfrq = '54';	# like 12210 TWR
my $appfrq = '55';  # like 11970 ROTTERDAM APP
my $maxatc = '56'; # Departure.
my %off2name = (
    0 => 'ATIS',
    1 => 'Unicom',
    2 => 'Clearance',
    3 => 'Ground',
    4 => 'Tower',
    5 => 'Approach',
    6 => 'Departure'
);

# offset 10 in runway array
my %runway_surface = (
    1  => 'Asphalt',
    2  => 'Concrete',
    3  => 'Turf/grass',
    4  => 'Dirt',
    5  => 'Gravel',
    6  => 'H-Asphalt', # helepad (big 'H' in the middle).
    7  => 'H-Concrete', # helepad (big 'H' in the middle).
    8  => 'H-Turf', # helepad (big 'H' in the middle).
    9  => 'H-Dirt', # helepad (big 'H' in the middle). 
    10 => 'T-Asphalt', # taxiway - with yellow hold line across long axis (not available from WorldMaker).
    11 => 'T-Concrete', # taxiway - with yellow hold line across long axis (not available from WorldMaker).
    12 => 'Dry Lakebed', # (eg. at KEDW Edwards AFB).
    13 => 'Water' # runways (marked with bobbing buoys) for seaplane/floatplane bases (available in X-Plane 7.0 and later). 
);

# =====================================================================================================
my $lastln = '99'; # end of file

# =============================
# NAV FILE INFO
# nav.dat.gz CODES
my $navNDB = '2';
my $navVOR = '3';
my $navILS = '4';
my $navLOC = '5';
my $navGS  = '6';
my $navOM  = '7';
my $navMM  = '8';
my $navIM  = '9';
my $navVDME = '12';
my $navNDME = '13';
# my @navset = ($navNDB, $navVOR, $navILS, $navLOC, $navGS, $navOM, $navMM, $navIM, $navVDME, $navNDME);
my %nav2type = (
    $navNDB => 'NDB',
    $navVOR => 'VOR',
    $navILS => 'ILS',
    $navLOC => 'LOC',
    $navGS  => 'GS',
    $navOM  => 'OM',
    $navMM  => 'MM',
    $navIM  => 'IM',
    $navVDME => 'VDME',
    $navNDME => 'NDME'
    );

sub get_nav_type_stg($) {
    my $typ = shift;
    if (defined $nav2type{$typ}) {
        return $nav2type{$typ};
    }
    return "Type $typ unknown";
}
sub is_defined_nav_type($) {
    my $typ = shift;
    return 1 if (defined $nav2type{$typ});
    return 0;
}

# =============================

# program variables
my @g_aptlist = ();
my $totaptcnt = 0;
my $totrwycnt = 0;

sub VERB1() { return $verbosity >= 1; }
sub VERB2() { return $verbosity >= 2; }
sub VERB5() { return $verbosity >= 5; }
sub VERB9() { return $verbosity >= 9; }

sub show_warnings($) {
    my ($val) = @_;
    if (@warnings) {
        prt( "\nGot ".scalar @warnings." WARNINGS...\n" );
        foreach my $itm (@warnings) {
           prt("$itm\n");
        }
        prt("\n");
    } else {
        prt( "\nNo warnings issued.\n\n" ) if (VERB9());
    }
}

sub pgm_exit($$) {
    my ($val,$msg) = @_;
    if (length($msg)) {
        $msg .= "\n" if (!($msg =~ /\n$/));
        prt($msg);
    }
    show_warnings($val);
    close_log($outfile,$load_log);
    exit($val);
}


sub prtw($) {
   my ($tx) = shift;
   $tx =~ s/\n$//;
   prt("$tx\n");
   push(@warnings,$tx);
}

sub process_in_file($) {
    my ($inf) = @_;
    if (! open INF, "<$inf") {
        pgm_exit(1,"ERROR: Unable to open file [$inf]\n"); 
    }
    my @lines = <INF>;
    close INF;
    my $lncnt = scalar @lines;
    prt("Processing $lncnt lines, from [$inf]...\n");
    my ($line,$inc,$lnn);
    $lnn = 0;
    foreach $line (@lines) {
        chomp $line;
        $lnn++;
        if ($line =~ /\s*#\s*include\s+(.+)$/) {
            $inc = $1;
            prt("$lnn: $inc\n");
        }
    }
}

sub in_world_range($$) {
    my ($lat,$lon) = @_;
    return 0 if (($lat > 90)||($lat < -90)||($lon > 180)||($lon < -180));
    return 1;
}

#//////////////////////////////////////////////////////////////////////
#//
#// Convert a cartexian XYZ coordinate to a geodetic lat/lon/alt.
#// This function is a copy of what's in SimGear,
#//  simgear/math/SGGeodesy.cxx.
#//
#////////////////////////////////////////////////////////////////////////
#// High-precision versions of the above produced with an arbitrary
#// precision calculator (the compiler might lose a few bits in the FPU
#// operations).  These are specified to 81 bits of mantissa, which is
#// higher than any FPU known to me:
my $SGD_PI = 3.1415926535;

my $SQUASH  = 0.9966471893352525192801545;
my $STRETCH = 1.0033640898209764189003079;
my $POLRAD  = 6356752.3142451794975639668;

my $SG_RAD_TO_NM  = 3437.7467707849392526;
my $SG_NM_TO_METER  = 1852.0000;
my $SG_METER_TO_FEET  = 3.28083989501312335958;
my $SGD_PI_2    = 1.57079632679489661923;
my $SG_RADIANS_TO_DEGREES = 180.0 / $SGD_PI;

my $EQURAD = 6378137.0;

my $E2 = abs(1 - ($SQUASH*$SQUASH));
my $ra2 = 1/($EQURAD*$EQURAD);
my $e2 = $E2;
my $e4 = $E2*$E2;

#//////////////////////////////////////////////////////////////////////
#//  This is the inverse of the algorithm in localLat().  It
#//  returns the (cylindrical) coordinates of a surface latitude
#//  expressed as an "up" unit vector.
#//////////////////////////////////////////////////////////////////////
#static void surfRZ (double upr, double upz, double* r, double* z)
sub surfRZ($$$$) {
    my ($upr,$upz,$rr,$rz) = @_;
    # // We are
    #// converting a (2D, cylindrical) "up" vector defined by the
    #// geodetic latitude into unitless R and Z coordinates in
    #// cartesian space.
    my $R = $upr * $STRETCH;
    my $Z = $upz * $SQUASH;
    #// Now we need to turn R and Z into a surface point.  That is,
    #// pick a coefficient C for them such that the point is on the
    #// surface when converted to "squashed" space:
    #//  (C*R*SQUASH)^2 + (C*Z)^2 = POLRAD^2
    #//   C^2 = POLRAD^2 / ((R*SQUASH)^2 + Z^2)
    my $sr = $R * $SQUASH;
    my $c = $POLRAD / sqrt($sr*$sr + $Z*$Z);
    $R *= $c;
    $Z *= $c;
    ${$rr} = $R;
    ${$rz} = $Z;
}   #// surfRZ()
#//////////////////////////////////////////////////////////////////////

# void sgCartToGeod ( const Point3D& CartPoint , Point3D& GeodPoint )
sub sgCartToGeod($$) {
    my ($rCartPoint,$rGeodPoint) = @_;
    #// according to
    #// H. Vermeille,
    #// Direct transformation from geocentric to geodetic ccordinates,
    #// Journal of Geodesy (2002) 76:451-454
    my $x = ${$rCartPoint}[0];
    my $y = ${$rCartPoint}[1];
    my $z = ${$rCartPoint}[2];
    my $XXpYY = $x*$x+$y*$y;
    my $sqrtXXpYY = sqrt($XXpYY);
    my $p = $XXpYY*$ra2;
    my $q = $z*$z*(1-$e2)*$ra2;
    my $r = 1/6.0*($p+$q-$e4);
    my $s = $e4*$p*$q/(4*$r*$r*$r);
    my $t = pow(1+$s+sqrt($s*(2+$s)), 1/3.0);
    my $u = $r*(1+$t+1/$t);
    my $v = sqrt($u*$u+$e4*$q);
    my $w = $e2*($u+$v-$q)/(2*$v);
    my $k = sqrt($u+$v+$w*$w)-$w;
    my $D = $k*$sqrtXXpYY/($k+$e2);
    ${$rGeodPoint}[0] = (2*atan2($y, $x+$sqrtXXpYY)) * $SG_RADIANS_TO_DEGREES; # lon
    my $sqrtDDpZZ = sqrt($D*$D+$z*$z);
    ${$rGeodPoint}[1] = (2*atan2($z, $D+$sqrtDDpZZ)) * $SG_RADIANS_TO_DEGREES; # lat
    ${$rGeodPoint}[2] = (($k+$e2-1)*$sqrtDDpZZ/$k) * $SG_METER_TO_FEET;        # alt
}   #// sgCartToGeod()

#//////////////////////////////////////////////////////////////////////
#// opposite of sgCartToGeod
#//////////////////////////////////////////////////////////////////////
#void sgGeodToCart ( double lat, double lon, double alt, double* xyz )
sub sgGeodToCart($$$$) {
    my ($lat, $lon, $alt, $rxyz) = @_;
    #// This is the inverse of the algorithm in localLat().  We are
    #// converting a (2D, cylindrical) "up" vector defined by the
    #// geodetic latitude into unitless R and Z coordinates in
    #// cartesian space.
    my $upr = cos($lat);
    my $upz = sin($lat);
    my ($r, $z);
    surfRZ($upr, $upz, \$r, \$z);
    #// Add the altitude using the "up" unit vector we calculated
    #// initially.
    $r += $upr * $alt;
    $z += $upz * $alt;
    #// Finally, convert from cylindrical to cartesian
    ${$rxyz}[0] = $r * cos($lon);
    ${$rxyz}[1] = $r * sin($lon);
    ${$rxyz}[2] = $z;
}   #// sgGeodToCart()
#//////////////////////////////////////////////////////////////////////

# LOAD apt.dat.gz
# details see : http://data.x-plane.com/file_specs/Apt810.htm
# Line codes used in apt.dat (810 version and 1000 version) 
# Airport Line - eg 
# 0  1    2   3   4    5++
# 1  1050 0   0   YGIL Gilgandra
# ID AMSL Twr Bld ICAO Name
# Code (apt.dat) Used for 
# 1 Airport header data. 
# 16 Seaplane base header data. No airport buildings or boundary fences will be rendered in X-Plane. 
# 17 Heliport header data.  No airport buildings or boundary fences will be rendered in X-Plane. 
# 10 Runway or taxiway at an airport. 
# 14 Tower view location. 
# 15 Ramp startup position(s) 
# 18 Airport light beacons (usually "rotating beacons" in the USA).  Different colours may be defined. 
# 19 Airport windsocks. 
# 50 to 56 Airport ATC (Air Traffic Control) frequencies. 
# runway
# 0  1           2          3   4       5    6         7             8 9      10  11   12    13    14     15
# 10 -31.696928  148.636404 15x 162.00  4204 0000.0000 0000.0000    98 121121  5   0    2    0.25   0     0000.0000
# rwy lat        lon        num true    feet displament/extension  wid lights surf shld mark smooth signs VASI
sub load_apt_data {
    my ($i,$max,$msg);
    prt("[v1] Loading $aptdat file ...\n") if (VERB1());
    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;
    $max = scalar @lines;
    prt("[v9] Got $max lines to scan...\n") if (VERB9());
    my ($add,$alat,$alon);
    $add = 0;
    my ($off,$atyp,$az,@arr,@arr2,$rwyt,$glat,$glon,$rlat,$rlon);
    my ($line,$apt,$diff,$rwycnt,$icao,$name,@runways,$version);
    my ($aalt,$actl,$abld,$ftyp,$cfrq,$frqn,@freqs);
    my ($len,$type);
    my ($rwid,$surf,$rwy1,$rwy2,$elat1,$elon1,$elat2,$elon2,$az1,$az2,$s,$res);
    $off = 0;
    $az = 0;
    $glat = 0;
    $glon = 0;
    $apt = '';
    $rwycnt = 0;
    @runways = ();
    @freqs = ();
    $msg = '[v1] ';
    #$msg .= "Search ICAO [$apticao]...";
    $msg .= " got $max lines, FOR airports,rwys,txwys... ";
    for ($i = 0; $i < $max; $i++) {
        $line = $lines[$i];
        $line = trim_all($line);
        if ($line =~ /\s+Version\s+/i) {
            @arr2 = split(/\s+/,$line);
            $version = $arr2[0];
            $msg .= "Version $version";
            $i++;
            last;
        }
    }
    prt("$msg\n") if (VERB1());
    for ( ; $i < $max; $i++) {
        $line = $lines[$i];
        $line = trim_all($line);
        $len = length($line);
        next if ($len == 0);
        ###prt("$line\n");
        my @arr = split(/\s+/,$line);
        $type = $arr[0];
        if (($line =~ /^$aln\s+/)||	    # start with '1'
            ($line =~ /^$sealn\s+/)||   # =  '16'; # Seaplane base header data.
            ($line =~ /^$heliln\s+/)) { # = '17'; # Heliport header data.  
            # 0  1   2 3 4     
            # 17 126 0 0 EH0001 [H] VU medisch centrum
            # ID ALT C B NAME++
            if (length($apt)) {
                if ($rwycnt > 0) {
                    # average position
                    $alat = $glat / $rwycnt;
                    $alon = $glon / $rwycnt;
                    $off = -1;
                    $az = 400;
                    @arr2 = split(/ /,$apt);
                    $atyp = $arr2[0]; # airport, heleiport, or seaport
                    $aalt = $arr2[1]; # Airport (general) ALTITUDE AMSL
                    $actl = $arr2[2]; # control tower
                    $abld = $arr2[3]; # buildings
                    $icao = $arr2[4]; # ICAO
                    $name = join(' ', splice(@arr2,5)); # Name
                    ##prt("$diff [$apt] (with $rwycnt runways at [$alat, $alon]) ...\n");
                    ##prt("$diff [$icao] [$name] ...\n");
                    #push(@g_aptlist, [$diff, $icao, $name, $alat, $alon, -1, 0, 0, 0, $icao, $name, $off, $dist, $az]);
                    my @f = @freqs;
                    my @r = @runways;
                    #                 0      1      2      3      4      5      6    7
                    push(@g_aptlist, [$atyp, $icao, $name, $alat, $alon, $aalt, \@f, \@r]);
                    ### prt("[v9] $icao, $name, $alat, $alon, $aalt, $rwycnt runways\n") if (VERB9());
                } else {
                    prtw("WARNING: Airport with NO runways! $icao, $name, $alat, $alon, $aalt\n");
                }
            }
            $apt = $line;
            $rwycnt = 0;
            @runways = ();  # clear RUNWAY list
            @freqs = (); # clear frequencies
            $glat = 0;
            $glon = 0;
            $totaptcnt++;	# count another AIRPORT
        } elsif ($line =~ /^$rln\s+/) {
            # 10  36.962213  127.031071 14x 131.52  8208 1595.0620 0000.0000   150 321321  1 0 3 0.25 0 0300.0300
            # 10  36.969145  127.020106 xxx 221.51   329 0.0 0.0    75 161161  1 0 0 0.25 0 
            $rlat = $arr[1];
            $rlon = $arr[2];
            $rwyt = $arr[3]; # text 'xxx'=taxiway, 'H1x'=heleport, else a runway
            ###prt( "$line [$rlat, $rlon]\n" );
            if ( $rwyt ne "xxx" ) {
                $glat += $rlat;
                $glon += $rlon;
                $rwycnt++;
                my @ar = @arr;
                push(@runways, \@ar);
                $totrwycnt++;
            }
        } elsif ($line =~ /^5(\d+)\s+/) {
            # frequencies
            $ftyp = $1;
            $cfrq = $arr[1];
            $frqn = $arr[2];
            $add = 0;
            if ($ftyp == 0) {
                $add = 1; # ATIS
            } elsif ($ftyp == 1) {
                $add = 1; # Unicom
            } elsif ($ftyp == 2) {
                $add = 1; # clearance
            } elsif ($ftyp == 3) {
                $add = 1; # ground
            } elsif ($ftyp == 4) {
                $add = 1; # tower
            } elsif ($ftyp == 5) {
                $add = 1; # approach
            } elsif ($ftyp == 6) {
                $add = 1; # departure
            }
            if ($add) {
                my @af = @arr;
                push(@freqs, \@af); # save the freq array
            } else {
                pgm_exit(1, "WHAT IS THIS [5$ftyp $cfrq $frqn] [$line]\n FIX ME!!!");
            }
        } elsif ($line =~ /^$lastln\s?/) {	# 99, followed by space, count 0 or more ...
            prt( "Reached END OF FILE ... \n" ) if (VERB9());
            last;
        } elsif ($type == 14) {
            # Tower view location(s).
        } elsif ($type == 15) {
            # parking Ramp startup position(s) 
        } elsif ($type == 18) {
            # 18 Airport light beacons (usually "rotating beacons" in the USA).  Different colours may be defined. 
        } elsif ($type == 19) {
            # 19 Airport windsocks.
        # ===============================================================================
        } elsif ($type == 20) {
            # 20 22.32152700 114.19750500 224.10 0 3 {@Y,^l}31-13{^r}
        } elsif ($type == 21) {
            # 21 22.31928000 114.19800800 3 134.09 3.10 13 PAPI-4R
        } elsif ($type == 100) {
            # 0   1          2          3   4       5      6         7         8   9      10 11 12 13   14 15
            # typ lat        lon        mrk bearing alt-ft
            # 10  36.962213  127.031071 14x 131.52  8208   1595.0620 0000.0000 150 321321  1  0  3 0.25 0  0300.0300
            # version 1000 runway
            # 0   1     2 3 4    5 6 7 8  9           10           11   12   13 14 15 16 17 18          19           20   21   22 23 24 25
            # 100 29.87 3 0 0.00 1 2 1 16 43.91080605 004.90321905 0.00 0.00 2  0  0  0  34 43.90662331 004.90428974 0.00 0.00 2  0  0  0
            $rwid  = $arr[1];  # WIDTH in meters? NOT SHOWN
            $surf  = $arr[2];  # add surface type
            $rwy1  = $arr[8];
            $elat1 = $arr[9];
            $elon1 = $arr[10];

            $rwy2 = $arr[17];
            $elat2 = $arr[18];
            $elon2 = $arr[19];
            $res = fg_geo_inverse_wgs_84 ($elat1,$elon1,$elat2,$elon2,\$az1,\$az2,\$s);
            $s = int($s * $MY_M2F);
            $rlat = ($elat1 + $elat2) / 2;
            $rlon = ($elon1 + $elon2) / 2;
            $glat += $rlat;
            $glon += $rlon;
            $rwycnt++;
            # 0   1=lat      2=lon      3=s 4=hdg  5=len 6=offsets 7=stopway 8=wid 9=lights 10=surf 11 12 13   14 15
            # 10  36.962213  127.031071 14x 131.52  8208 1595.0620 0000.0000 150   321321   1       0  3  0.25 0  0300.0300
            # 11=shoulder 12=marks 13=smooth 14=signs 15=GS angles
            # 0           3        0.25      0        0300.0300
            #        0  1     2     3     4    5
            $rwy2 = [10,$rlat,$rlon,$rwy2,$az1,$s,6,7,8,9,$surf,11,12,13,14,15];
            #  push(@runways, \@arr);
            push(@runways,$rwy2);
        } elsif ($type == 101) {	# Water runways
            # Water runways
            # 0   1      2 3  4           5             6  7           8
            # 101 243.84 0 16 29.27763293 -089.35826258 34 29.26458929 -089.35340410
            # 101 22.86  0 07 29.12988952 -089.39561501 25 29.13389936 -089.38060001
            $elat1 = $arr[4];
            $elon1 = $arr[5];
            $elat2 = $arr[7];
            $elon2 = $arr[8];
            $surf  = 13;
            $res = fg_geo_inverse_wgs_84 ($elat1,$elon1,$elat2,$elon2,\$az1,\$az2,\$s);
            $s = int($s * $MY_M2F);
            $rwy1 = int(($az1 / 10) + 0.5);
            $rlat = sprintf("%.8f",(($elat1 + $elat2) / 2));
            $rlon = sprintf("%.8f",(($elon1 + $elon2) / 2));
            $glat += $rlat;
            $glon += $rlon;
            $rwycnt++;
            $rwy2 = [10,$rlat,$rlon,$rwy2,$az1,$s,6,7,8,9,$surf,11,12,13,14,15];
            # push(@waterways, \@a2);
            push(@runways,$rwy2);
        } elsif ($type == 102) {	# Heliport
            # 0   1  2           3            4      5     6     7 8 9 10   11
            # 102 H2 52.48160046 013.39580674 355.00 18.90 18.90 2 0 0 0.00 0
            # 102 H3 52.48071507 013.39937648 2.64   13.11 13.11 1 0 0 0.00 0
            $rwy1  = $arr[1];
            $elat1 = $arr[2];
            $elon1 = $arr[3];
            $az1   = $arr[4];
            $s     = int($arr[5] * $MY_M2F);
            $surf  = 6;
            $rlat = sprintf("%.8f",$elat1);
            $rlon = sprintf("%.8f",$elon1);
            $glat += $rlat;
            $glon += $rlon;
            $rwycnt++;
            $rwy2 = [10,$rlat,$rlon,$rwy2,$az1,$s,6,7,8,9,$surf,11,12,13,14,15];
            push(@runways,$rwy2);
        } elsif ($type == 110) {
            # 110 2 0.00 134.10 runway sholder
        } elsif ($type == 111) {
            # 111 22.30419700 114.21613100
        } elsif ($type == 112) {
            # 112 22.30449500 114.21644400 22.30480900 114.21677000 51 102
        } elsif ($type == 113) {
            # 113 22.30370300 114.21561700
        } elsif ($type == 114) {
            # 114 43.29914799 -008.38013558 43.29965322 -008.37970933
        } elsif ($type == 115) {
            # 115 22.31009400 114.21038500
        } elsif ($type == 116) {
            # 116 43.30240028 -008.37799316 43.30271076 -008.37878407
        } elsif ($type == 120) {
            # 120 hold lines W A13
        } elsif ($type == 130) {
            # 130 Airport Boundary
        } elsif ($type == 1000) {
            # 1000 Northerly flow
        } elsif ($type == 1001) {
            # 1001 KGRB 270 020 999
        } elsif ($type == 1002) {
            # 1002 KGRB 0
        } elsif ($type == 1003) {
            # 1003 KGRB 0
        } elsif ($type == 1004) {
            # 1004 0000 2400
        } elsif ($type == 1100) {
            # 1100 36 12654 all heavy|jets|turboprops|props 000360 000360 Northerly
        } elsif ($type == 1101) {
            # 1101 36 left
        } elsif ($type == 1200) {
            # ????
        } elsif ($type == 1201) {
            # 1201 42.75457409 -073.80880021 both 2110 _start
        } elsif ($type == 1202) {
            # 1202 2110 2112 twoway taxiway
        } elsif ($type == 1204) {
            # 1204 arrival 01,19
        } elsif ($type == 1300) {
            # 1300 30.32875704 -009.41140596 323.85 misc jets|props Ramp
        # ===============================================================================
        } else {
            pgm_exit(1,"Line type $type NOT USED [$line]\n*** FIX ME ***");
        }
    }

    # do any LAST entry
    $add = 0;
    $off = -1;
    $az = 0;
    if (length($apt) && ($rwycnt > 0)) {
        $alat = $glat / $rwycnt;
        $alon = $glon / $rwycnt;
        $off = -1;
        $az = 400;
        #$off = near_given_point( $alat, $alon, \$dist, \$az );
        #$dlat = abs( $c_lat - $alat );
        #$dlon = abs( $c_lon - $alon );
        #$diff = int( ($dlat * 10) + ($dlon * 10) );
        @arr2 = split(/ /,$apt);
        $atyp = $arr2[0];
        $aalt = $arr2[1];
        $actl = $arr2[2]; # control tower
        $abld = $arr2[3]; # buildings
        $icao = $arr2[4];
        $name = join(' ', splice(@arr2,5));
        ###prt("$diff [$apt] (with $rwycnt runways at [$alat, $alon]) ...\n");
        ###prt("$diff [$icao] [$name] ...\n");
        ###push(@g_aptlist, [$diff, $icao, $name, $alat, $alon]);
        ##push(@g_aptlist, [$diff, $icao, $name, $alat, $alon, -1, 0, 0, 0, $icao, $name, $off, $dist, $az]);
        my @f = @freqs;
        my @r = @runways;
        #                 0      1      2      3      4      5      6    7
        push(@g_aptlist, [$atyp, $icao, $name, $alat, $alon, $aalt, \@f, \@r]);
        $totaptcnt++;	# count another AIRPORT
    }
    my $cnt = scalar @g_aptlist;
    prt("[v1] Done scan of $max lines for $cnt airports, $totrwycnt runways...\n") if (VERB1());
}

sub mycmp_decend_dist {
   return -1 if (${$a}[8] < ${$b}[8]);
   return 1 if (${$a}[8] > ${$b}[8]);
   return 0;
}

# 12345678901
# -18.0748140
sub set_lat_stg($) {
    my ($rl) = @_;
    ${$rl} = sprintf("%2.7f",${$rl});
    ${$rl} = ' '.${$rl} while (length(${$rl}) < 11);
}

# 123456789012
# -140.9458860
sub set_lon_stg($) {
    my ($rl) = @_;
    ${$rl} = sprintf("%3.7f",${$rl});
    ${$rl} = ' '.${$rl} while (length(${$rl}) < 12);
}

sub set_azimuth_stg($) {
    my ($rl) = @_;
    ${$rl} = sprintf("%03.1f",${$rl});
    ${$rl} = ' '.${$rl} while (length(${$rl}) < 5);
}

sub not_on_track($) {
    my ($trk) = shift; # = $az1
    my $diff = abs($trk - $g_track);    # get absolute difference current and desired
    # but then the case - azimuth is 345, g_track is zero(0), spread is 30
    $diff = 360 - $diff if ($diff > 180);
    return 0 if ($diff <= $g_spread);   # if abs difference less than or equal to desired spread = ON TRACK
    return 1;   # is OFF TRACK
}

sub cased_name($) { # if (!$name_as_is);
    my $name = shift;
    my @arr = split(/\s+/,$name);
    my $nname = "";
    my ($part,$nm,$len);
    foreach $part (@arr) {
        $len = length($part);
        next if ($len == 0);
        $nname .= ' ' if (length($nname));
        if ($part =~ /^\[.+\]/) {
            $nname .= $part;
        } else {
            $nm = uc(substr($part,0,1));
            $nm .= lc(substr($part,1)) if ($len > 1);
            $nname .= $nm;
        }
    }
    return $nname;
}

# @runways reference
# 0   1=lat      2=lon      3=s 4=hdg  5=len 6=offsets 7=stopway 8=wid 9=lights 10=surf 11 12 13   14 15
# 10  36.962213  127.031071 14x 131.52  8208 1595.0620 0000.0000 150   321321   1       0  3  0.25 0  0300.0300
# 11=shoulder 12=marks 13=smooth 14=signs 15=GS angles
# 0           3        0.25      0        0300.0300

sub get_runways_stg($) {
    my $rrwys = shift;
    my $cnt = scalar @{$rrwys};
    my ($i,$ra,$max,$rlen,$hdg);
    $max = 0;
    $hdg = 0;
    for ($i = 0; $i < $cnt; $i++) {
        $ra = ${$rrwys}[$i];
        $rlen = ${$ra}[5];
        if ($rlen > $max) {
            $max = $rlen;
            $hdg  = ${$ra}[4]
            ##$hdg  = ${$ra}[3];
            ##$hdg =~ s/x$//;
        }
    }
    $hdg = int($hdg);
    my $txt = "rw:$cnt:$max:$hdg";
    return $txt;
}

# 0   1 (lat)   2 (lon)        3     4   5           6   7  8++
# 2   38.087769 -077.324919  284   396  25       0.000 APH  A P Hill NDB
# 3   57.103719  009.995578   57 11670 100       1.000 AAL  Aalborg VORTAC
# 4   39.980911 -075.877814  660 10850  18     281.662 IMQS 40N 29 ILS-cat-I
# 4  -09.458922  147.231225  128 11010  18     148.650 IWG  AYPY 14L ILS-cat-I
# 5   40.034606 -079.023281 2272 10870  18     236.086 ISOZ 2G9 24 LOC
# parsed and put in an array
##              0    1     2     3     4     5     6      7     8  9    10
#push(@navlist,[$typ,$nlat,$nlon,$nalt,$nfrq,$nrng,$nid  ,$name,$s,$az1,$az2]);

sub get_ils_cnt($) {
    my ($icao) = shift;
    my $icnt = 0;
    my $max = scalar @{$rnavaids};
    my ($i,$typ,$ra,$name,$tmp,@arr);
    # prt("Finding ILS for [$icao] in $max navaids...\n");
    for ($i = 0; $i < $max; $i++) {
        $ra = ${$rnavaids}[$i];
        $typ = ${$ra}[0];
        next if ($typ != 4);
        $name = ${$ra}[7]; 
        @arr = split(/\s+/,$name);
        $tmp = $arr[0];
        next if ($icao ne $tmp);
        $icnt++;
    }
    return $icnt;
}

sub show_distance_list($) {
    my ($find_icao) = @_;
    my $rapts = \@g_aptlist;
    my $cnt = scalar @{$rapts};
    my ($i,$atyp,$icao,$name,$alat,$alon,$aalt,$rfreq,$rrwys,$rwycnt,$len);
    my ($fatyp,$ficao,$fname,$falat,$falon,$faalt,$frfreq,$frrwys,$frwycnt);
    my ($s,$az1,$az2,$distnm,$arwys);
    my $minn = 0;
    # SORT list in decending DISTANCE order
    prt("Show list of $g_max_out nearest airports to $find_icao... ");
    if ($g_track != -1) {
        prt("On track $g_track, +/-$g_spread degrees... ");
    }
    if ($only_with_ils) {
        prt("with ILS ");
    }
    prt("\n");
    # ============================================
    my @newarr = sort mycmp_decend_dist @{$rapts};
    # ============================================
    $rapts = \@newarr;
    my $max = $g_max_out;
    my $xhele = $g_xhele;
    my $xsea = $g_xsea;
    my $xold = $g_xold;
    my $dn_hdr = 0;
    my $x_hel = 0;
    my $x_sea = 0;
    my $x_old = 0;
    my $x_trk = 0;
    my $done = 0;
    my $ilscnt = 0;
    $minn = 0;
    $done = 0;
    # run only to get minimum NAME length
    for ($i = 0; $i < $cnt; $i++) {
        $name = ${$rapts}[$i][2];
        $az1   = ${$rapts}[$i][9];
        if ($done) {
            if ($g_track != -1) {
                if (not_on_track($az1)) {
                    $x_trk++;
                    next;
                }
            }
            if (($name =~ /\[H\]/) && $xhele) {
                $x_hel++;
                next;
            }
            if (($name =~ /\[S\]/) && $xsea) {
                $x_sea++;
                next;
            }
            if (($name =~ /\[X\]/) && $xold) {
                $x_old++;
                next;
            }
            if ($only_with_ils) {
                $icao = ${$rapts}[$i][1];
                $ilscnt = get_ils_cnt($icao);
                next if ($ilscnt == 0);
            }

        }
        $len = length($name);
        $minn = $len if ($len > $minn);
        $done++;
        last if ($done == $max);
    }

    # display run
    # ==========================================
    $done = 0; # restart done counter
    for ($i = 0; $i < $cnt; $i++) {
        $name = ${$rapts}[$i][2];
        $az1   = ${$rapts}[$i][9];
        if ($done) {
            next if (($g_track != -1) && not_on_track($az1));
            next if (($name =~ /\[H\]/) && $xhele);
            next if (($name =~ /\[S\]/) && $xsea);
            next if (($name =~ /\[X\]/) && $xold);
            if ($only_with_ils) {
                $icao = ${$rapts}[$i][1];
                $ilscnt = get_ils_cnt($icao);
                next if ($ilscnt == 0);
            }
        }
        $atyp = ${$rapts}[$i][0];
        $icao = ${$rapts}[$i][1];
        $alat = ${$rapts}[$i][3];
        $alon = ${$rapts}[$i][4];
        $aalt = ${$rapts}[$i][5];
        $rfreq = ${$rapts}[$i][6];  # ATC frequ
        $rrwys = ${$rapts}[$i][7];  # Runways
        $s     = ${$rapts}[$i][8];
        $az2   = ${$rapts}[$i][10];

        # set BOUNDS
        $g_minlat = $alat if ($alat < $g_minlat);
        $g_minlon = $alon if ($alon < $g_minlon);
        $g_maxlat = $alat if ($alat > $g_maxlat);
        $g_maxlon = $alon if ($alon > $g_maxlon);
        $g_minalt = $aalt if ($aalt < $g_minalt);
        $g_maxalt = $aalt if ($aalt > $g_maxalt);

        # FORMAT the display
        ##############################################
        $arwys = get_runways_stg($rrwys);
        $ilscnt = get_ils_cnt($icao);
        if ($ilscnt) {
            $arwys .= ":ils:".$ilscnt;
        } else {
            ### $arwys .= ":ni";
        }
        $name = cased_name($name) if (!$name_as_is);
        $distnm = $s * $SG_METER_TO_NM;
        $distnm = (int($distnm * 10) / 10);
        if ($distnm == int($distnm)) {
            $distnm .= ".0";
        }
        $distnm = ' '.$distnm while (length($distnm) < $g_distmin);
        $name .= ' ' while (length($name) < $minn);
        $icao .= ' ' while (length($icao) < 4);
        set_lat_stg(\$alat);
        set_lon_stg(\$alon);
        $aalt = ' '.$aalt while (length($aalt) < $g_altmin);
        set_azimuth_stg(\$az1);
        if (!$dn_hdr) {
            prt("ICAO, ");
            my $tmp = "Name";
            $tmp .= ' ' while (length($tmp) < $minn);
            prt("$tmp, ");
            $tmp = "Latitude";
            $tmp = ' '.$tmp while (length($tmp) < length($alat));
            prt("$tmp, ");
            $tmp = "Longitude";
            $tmp = ' '.$tmp while (length($tmp) < length($alon));
            prt("$tmp, ");
            $tmp = "Alt(ft)";
            $tmp = ' '.$tmp while (length($tmp) < length($aalt));
            prt("$tmp, ");
            $tmp = "D(nm)";
            $tmp = ' '.$tmp while (length($tmp) < $g_distmin);
            prt("$tmp, ");
            $tmp = "Degs";
            $tmp = ' '.$tmp while (length($tmp) < length($az1));
            prt("$tmp");
            prt("\n");
            $dn_hdr = 1;
        }
        # skip the first, which is the user target airport
        prt("$icao, $name, $alat, $alon, $aalt, $distnm, $az1, $arwys\n") if ($done);
        $done++;
        last if ($done == $max);
    }
    if ($x_hel || $x_sea || $x_old || $x_trk) {
        prt("NOTE: Excluded: ");
        prt("$x_trk not on track $g_track, +/-$g_spread degs. ") if ($x_trk);
        prt("$x_hel heleports. ") if ($x_hel);
        prt("$x_sea seaports. ") if ($x_sea);
        prt("$x_old closed. ") if ($x_old);
        prt("\n");
    }

}


sub process_in_icao($) {
    my ($find_icao) = @_;   # user ICAO
    my $rapts = \@g_aptlist;    # find AIRPORT
    my @found = ();
    my $cnt = scalar @{$rapts};
    ##                 0      1      2      3      4      5      6    7
    #push(@g_aptlist, [$diff, $icao, $name, $alat, $alon, $aalt, \@f, \@r]);
    my ($i,$atyp,$icao,$name,$alat,$alon,$aalt,$rfreq,$rrwys,$rwycnt,$len);
    prt("[v1] Searching $cnt airports for ICAO [$find_icao]...\n") if (VERB1());
    my $minn = 0;
    my $fndcnt = 0;
    $g_minlat =  200;
    $g_minlon =  200;
    $g_maxlat = -200;
    $g_maxlon = -200;
    $g_minalt =  9999999;
    $g_maxalt = -9999999;
    for ($i = 0; $i < $cnt; $i++) {
        $icao = ${$rapts}[$i][1];
        $name = ${$rapts}[$i][2];
        if ($icao eq $find_icao) {
            $atyp = ${$rapts}[$i][0];
            $alat = ${$rapts}[$i][3];
            $alon = ${$rapts}[$i][4];
            $aalt = ${$rapts}[$i][5];
            $rfreq = ${$rapts}[$i][6];
            $rrwys = ${$rapts}[$i][7];
            $rwycnt = scalar @{$rrwys};
            # set BOUNDS
            $g_minlat = $alat if ($alat < $g_minlat);
            $g_minlon = $alon if ($alon < $g_minlon);
            $g_maxlat = $alat if ($alat > $g_maxlat);
            $g_maxlon = $alon if ($alon > $g_maxlon);
            $g_minalt = $aalt if ($aalt < $g_minalt);
            $g_maxalt = $aalt if ($aalt > $g_maxalt);

            $name = cased_name($name) if (!$name_as_is);
            prt("Found $icao, $name, $alat, $alon, $aalt ft, $rwycnt runways\n"); # if (VERB1());
            push(@found, $i);
            $fndcnt++;
        }
        $len = length($name);
        $minn = $len if ($len > $minn);
    }
    if (!$fndcnt) {
        prt("No airport found with ICAO of $find_icao!\n");
        return 0;
    }
    if ($fndcnt > 1) {
        prtw("WARNING: Found $fndcnt matching ICAO! Only nearest first shown\n");
    }
    my $fnd = $found[0];
    $i = $fnd;
    my ($fatyp,$ficao,$fname,$falat,$falon,$faalt,$frfreq,$frrwys,$frwycnt);
    $fatyp = ${$rapts}[$i][0];
    $ficao = ${$rapts}[$i][1];
    $fname = ${$rapts}[$i][2];
    $falat = ${$rapts}[$i][3];
    $falon = ${$rapts}[$i][4];
    $faalt = ${$rapts}[$i][5];
    $frfreq = ${$rapts}[$i][6];
    $frrwys = ${$rapts}[$i][7];
    $g_clat = $falat;
    $g_clon = $falon;
    ${$rapts}[$i][8] = 0;
    ${$rapts}[$i][9] = 0;
    ${$rapts}[$i][10] = 0;
    my ($s,$az1,$az2,$distnm);
    ##                 0      1      2      3      4      5      6    7
    #push(@g_aptlist, [$diff, $icao, $name, $alat, $alon, $aalt, \@f, \@r]);
    for ($i = 0; $i < $cnt; $i++) {
        next if ($i == $fnd);
        $alat = ${$rapts}[$i][3];
        $alon = ${$rapts}[$i][4];
        #sub fg_geo_inverse_wgs_84 {
        #my ($lat1, $lon1, $lat2, $lon2, $az1, $az2, $s) = @_;
        fg_geo_inverse_wgs_84($falat,$falon,$alat,$alon,\$az1,\$az2,\$s);
        ${$rapts}[$i][8] = $s;      # distance from FOUND
        ${$rapts}[$i][9] = $az1;    # direction from found
        ${$rapts}[$i][10] = $az1;   # direction to found
    }

    return $fndcnt;
}

# **************************
sub load_gzip_lines($) {
    my $file = shift;
	prt("\n[v9] Loading $file file ...\n") if (VERB9());
	mydie("ERROR: Can NOT locate [$file]!\n") if ( !( -f $file) );
	open NIF, "gzip -d -c $file|" or mydie( "ERROR: CAN NOT OPEN $file...$!...\n" );
	my @nav_lines = <NIF>;
	close NIF;
    prt("[v9] Got ".scalar @nav_lines." lines to scan...\n") if (VERB9());
    return \@nav_lines;
}

# NAV.DAT FIX.DAT AWY.DAT
sub load_nav_lines() { return load_gzip_lines($navdat); }
sub load_fix_lines() { return load_gzip_lines($g_fixfile); }
sub load_awy_lines() { return load_gzip_lines($g_awyfile); }

####################################################################
### load navaids, and keep DISTANCE from the airport given
sub load_nav_file() {
    my $rnav = load_nav_lines();
    my $cnt = scalar @{$rnav};
    prt("[v1] Loaded $cnt lines, from [$navdat]...\n") if (VERB1());
    my ($i,$line,$len,$lnn,@arr,$nc);
    my ($typ,$nlat,$nlon,$nalt,$nfrq,$nrng,$nfrq2,$nid,$name,$navcnt);
    my ($s,$az1,$az2);
    $nlat = $g_clat;
    $nlon = $g_clon;
    set_lat_stg(\$nlat);
    set_lon_stg(\$nlon);
    #prt("Show  $g_max_out closest navaids to $nlat,$nlon\n");
    $lnn = 0;
    $navcnt = 0;
    my @navlist = ();
    for ($i = 0; $i < $cnt; $i++) {
        $line = trim_all(${$rnav}[$i]);
        $len = length($line);
        next if ($len == 0);
        next if ($line =~ /\s+Version\s+/i);
		@arr = split(/\s+/,$line);
		$nc = scalar @arr;
		$typ = $arr[0];
        next if ($typ eq 'I');
        last if ($typ eq '99');
        if (!is_defined_nav_type($typ)) {
            prt("$lnn: Undefined [$line]\n");
            next;
        }
        $navcnt++;
		# 0   1 (lat)   2 (lon)        3     4   5           6   7  8++
		# 2   38.087769 -077.324919  284   396  25       0.000 APH  A P Hill NDB
		# 3   57.103719  009.995578   57 11670 100       1.000 AAL  Aalborg VORTAC
		# 4   39.980911 -075.877814  660 10850  18     281.662 IMQS 40N 29 ILS-cat-I
		# 4  -09.458922  147.231225  128 11010  18     148.650 IWG  AYPY 14L ILS-cat-I
		# 5   40.034606 -079.023281 2272 10870  18     236.086 ISOZ 2G9 24 LOC
		# 5   67.018506 -050.682072  165 10955  18      61.600 ISF  BGSF 10 LOC
		# 6   39.977294 -075.860275  655 10850  10  300281.205 ---  40N 29 GS
		# 6  -09.432703  147.216444  128 11010  10  302148.785 ---  AYPY 14L GS
		# 7   39.960719 -075.750778  660     0   0     281.205 ---  40N 29 OM
		# 7  -09.376150  147.176867  146     0   0     148.785 JSN  AYPY 14L OM
		# 8  -09.421875  147.208331   91     0   0     148.785 MM   AYPY 14L MM
		# 8  -09.461050  147.232544  146     0   0     328.777 PY   AYPY 32R MM
		# 9   65.609444 -018.052222   32     0   0      22.093 ---  BIAR 01 IM
		# 9   08.425319  004.475597 1126     0   0      49.252 IL   DNIL 05 IM
		# 12 -09.432703  147.216444   11 11010  18       0.000 IWG  AYPY 14L DME-ILS
		# 12 -09.449222  147.226589   11 10950  18       0.000 IBB  AYPY 32R DME-ILS
        $nlat = $arr[1];
        $nlon = $arr[2];
        $nalt = $arr[3];
        $nfrq = $arr[4];
        $nrng = $arr[5];
        $nfrq2 = $arr[6];
        $nid = $arr[7];     # this is an ICAO if it is an ILS
        $name = '';
        for (my $i = 8; $i < $nc; $i++) {
            $name .= ' ' if length($name);
            $name .= $arr[$i];
        }
        fg_geo_inverse_wgs_84($g_clat,$g_clon,$nlat,$nlon,\$az1,\$az2,\$s);
        #push(@navlist,[$typ,$nlat,$nlon,$nalt,$nfrq,$nrng,$nfrq2,$name,$s,$az1,$az2]);
        #              0    1     2     3     4     5     6      7     8  9    10
        push(@navlist,[$typ,$nlat,$nlon,$nalt,$nfrq,$nrng,$nid  ,$name,$s,$az1,$az2]);
    }
    prt("Loaded $navcnt navigation aids...\n") if (VERB5());
    @navlist = sort mycmp_decend_dist @navlist;
    prt("[v1] $navcnt navaids sorted per distance from $g_clat,$g_clon...\n") if (VERB1());
    return \@navlist;
}

sub show_nav_list($) {
    my ($rnavs) = @_;
    my $cnt = scalar @{$rnavs};
    my ($ctyp,$typ,$nlat,$nlon,$nalt,$nfrq,$nrng,$nfrq2,$nid,$name,$navcnt);
    my ($i,$s,$az1,$az2,$distnm,$minnm,$len,$msg);
    my $max = $g_max_out;
    my $done = 0;
    $minnm = 0;
    for ($i = 0; $i < $cnt; $i++) {
        last if ($done == $max);
        #               0    1     2     3     4     5     6      7     8  9    10
        #push(@navlist,[$typ,$nlat,$nlon,$nalt,$nfrq,$nrng,$nid  ,$name,$s,$az1,$az2]);
        $name = ${$rnavs}[$i][7];
        $len = length($name);
        $minnm = $len if ($len > $minnm);
        $done++;
    }
    $done = 0;
    #           VDME -32.2196810, 148.5776610     935 11440    50    31.4 185.5 DUBBO VOR-DME DME 
    my $head = "Type Latitude    Longitude     Alt.   Freq  Range Dist.NM Hdg   Name";
    prt("$head\n");
    for ($i = 0; $i < $cnt; $i++) {
        last if ($done == $max);
        #               0    1     2     3     4     5     6      7     8  9    10
        #push(@navlist,[$typ,$nlat,$nlon,$nalt,$nfrq,$nrng,$nfrq2,$name,$s,$az1,$az2]);
        $typ = ${$rnavs}[$i][0];
        $nlat = ${$rnavs}[$i][1];
        $nlon = ${$rnavs}[$i][2];
        $nalt = ${$rnavs}[$i][3];
        $nfrq = ${$rnavs}[$i][4];
        $nrng = ${$rnavs}[$i][5];
        $nfrq2 = ${$rnavs}[$i][6];
        $name = ${$rnavs}[$i][7];
        $s = ${$rnavs}[$i][8];
        $az1 = ${$rnavs}[$i][9];
        $az2 = ${$rnavs}[$i][10];
        $distnm = $s * $SG_METER_TO_NM;
        $msg = '';
        # No range for 
        # my $navOM  = '7';
        # my $navMM  = '8';
        # my $navIM  = '9';
        if (("$typ" eq $navOM)|| ("$typ" eq $navMM) || ("$typ" eq $navIM)) {
            # these have NO range - very short anyway
        } else {
            $msg = '(Rng!)' if ($distnm > $nrng);
        }
        # get display FORMAT
        $ctyp = get_nav_type_stg($typ);
        $ctyp .= ' ' while (length($ctyp) < 4);
        $distnm = (int($distnm * 10) / 10);
        if ($distnm == int($distnm)) {
            $distnm .= ".0";
        }
        $distnm = ' '.$distnm while (length($distnm) < $g_distmin);
        set_lat_stg(\$nlat);
        set_lon_stg(\$nlon);
        $nalt = ' '.$nalt while (length($nalt) < $g_altmin);
        $nfrq .= ' ' while (length($nfrq) < $g_frqmin);
        $nrng = ' '.$nrng while (length($nrng) < $g_rngmin);
        set_azimuth_stg(\$az1);
        $name .= ' ' while (length($name) < $minnm);
        #prt("$ctyp,$nlat,$nlon,$nalt,$nfrq,$nrng,$nfrq2,$name,$s,$az1,$az2\n");
        prt("$ctyp $nlat,$nlon $nalt $nfrq $nrng $distnm $az1 $name $msg\n");
        $done++;
    }
    $nlat = $g_clat;
    $nlon = $g_clon;
    set_lat_stg(\$nlat);
    set_lon_stg(\$nlon);
    prt("Shown $done closest navaids to $nlat,$nlon\n");
}

sub load_fix_hash($) {
    my ($rfa) = @_;
    my $max = scalar @{$rfa};
    my ($line,$len,@arr,$cnt,$typ,$flat,$flon,$fname,$name,$key);
    my %h;
    foreach $line (@{$rfa}) {
        chomp $line;
        $line = trim_all($line);
        $len = length($line);
        next if ($len == 0);
        next if ($line =~ /^I/);
        next if ($line =~ /Version/i);
        @arr = split(/\s+/,$line);
        $cnt = scalar @arr;
        $typ = $arr[0];
        next if ($typ == 600);
        last if ($typ == 99);
        if ($cnt >= 3) {
            $flat = $arr[0];
            $flon = $arr[1];
            $name = trim_all($arr[2]);
            #  $name      0      1
            $h{$name} = [ $flat, $flon ];
        }
    }
    return \%h;
}

sub load_fix_file() {
    my $rfixs = load_fix_lines();
    my $cnt = scalar @{$rfixs};
    prt("Loaded $cnt lines of fixes...\n") if (VERB9());
    my $rfh = load_fix_hash($rfixs);
    my @fixarr = ();
    my ($name,$val,$flat,$flon,$fcnt);
    my ($s,$az1,$az2);
    $fcnt = 0;
    foreach $name (keys %{$rfh}) {
        $val = ${$rfh}{$name};
        $flat = ${$val}[0];
        $flon = ${$val}[1];
        if (in_world_range($flat,$flon)) {
            fg_geo_inverse_wgs_84($g_clat,$g_clon,$flat,$flon,\$az1,\$az2,\$s);
            #              0     1     2    3 4 5 6 7 8  9    10
            push(@fixarr,[$name,$flat,$flon,0,0,0,0,0,$s,$az1,$az2]);
            $fcnt++;
        } else {
            prt("$name $flat $flon - OUT OF WORLD RANGE!\n");
        }
    }
    @fixarr = sort mycmp_decend_dist @fixarr;
    prt("Loaded $fcnt fixes... sorted by distance...\n") if (VERB9());
    return \@fixarr;
}

sub show_fix_list($) {
    my ($rfixs) = @_;
    my $cnt = scalar @{$rfixs};
    my ($i,$name,$flat,$flon,$s,$az1,$done,$minnm,$len,$distnm);
    my $max = $g_max_out;
    $done = 0;
    $minnm = 0;
    for ($i = 0; $i < $cnt; $i++) {
        last if ($done == $max);
        #               0     1     2    3 4 5 6 7 8  9    10
        #push(@fixarr,[$name,$flat,$flon,0,0,0,0,0,$s,$az1,$az2]);
        $name = ${$rfixs}[$i][0];
        $len = length($name);
        $minnm = $len if ($len > $minnm);
        $done++;
    }
    $done = 0;
    #           HILAR -31.6452780, 148.4861110     8.3 291.9
    my $head = "Name  Latitude    Longitude    Dist.NM Hdg";
    prt("$head\n");
    for ($i = 0; $i < $cnt; $i++) {
        last if ($done == $max);
        #               0     1     2    3 4 5 6 7 8  9    10
        #push(@fixarr,[$name,$flat,$flon,0,0,0,0,0,$s,$az1,$az2]);
        $name = ${$rfixs}[$i][0];
        $flat = ${$rfixs}[$i][1];
        $flon = ${$rfixs}[$i][2];
        $s    = ${$rfixs}[$i][8];
        $az1  = ${$rfixs}[$i][9];
        #prt("$name,$flat,$flon,$s,$az1\n");

        # get display FORMAT
        $name .= ' ' while (length($name) < $minnm);
        set_lat_stg(\$flat);
        set_lon_stg(\$flon);
        $distnm = $s * $SG_METER_TO_NM;
        $distnm = (int($distnm * 10) / 10);
        if ($distnm == int($distnm)) {
            $distnm .= ".0";
        }
        $distnm = ' '.$distnm while (length($distnm) < $g_distmin);
        set_azimuth_stg(\$az1);
        prt("$name $flat,$flon $distnm $az1\n");
        $done++;
    }
}

# FG airways file
# 0      1          2          3      4          5          6  7   8   9
# ASKIK  50.052778  008.533611 RUDUS  50.047500  008.078333 1  050 240 L984
# from   lat        lon        to     lat        lon       cat bfl efl name
#
# Basic to air traffic control are special air routes called airways.
# Airways are defined on charts and are provided with radio ranges, 
# devices that allow the pilot whose craft has a suitable receiver 
# to determine the plane's bearing and distance from a fixed location. 
# The most common beacon is a very high frequency omnidirectional 
# radio beacon, which emits a signal that varies according to the 
# direction in which it is transmitted. Using a special receiver, 
# an air navigator can obtain an accurate bearing on the transmitter 
# and, using distance-measuring equipment (DME), distance from it as well.
#
# The system of radio ranges around the United States is often called 
# the VORTAC system. For long distances other electronic navigation 
# systems have been developed: 
# Omega, accurate to about two miles (3 km); 
# Loran-C, accurate to within .25 mi (.4 km) but available only in the United States; 
# and the Global Positioning System (GPS), a network of 24 satellites that 
# is accurate to within a few yards and is making radio ranging obsolete.

sub load_awy_file() {
    my $raa = load_awy_lines();
    my $max = scalar @{$raa};
    my ($line,$len,@arr,$cnt,$typ,$flat,$flon,$fname,$name,$key);
    my ($tlat,$tlon,$from,$to,$hadver);
    my ($cat,$bfl,$efl,$ra,$lnn,$num);
    my ($s,$az1,$az2);
    my @airways = ();
    prt("Loaded $max lines of airways...\n") if (VERB9());
    $lnn = 0;
    $hadver = 0;
    $num = 0;
    foreach $line (@{$raa}) {
        $lnn++;
        chomp $line;
        $line = trim_all($line);
        $len = length($line);
        next if ($len == 0);
        next if (($line =~ /^I/)&&($hadver == 0));
        if ($line =~ /\s+Version\s+/) {
            $hadver = 1;
            #next if ($typ == 640);
            next;
        }
        @arr = split(/\s+/,$line);
        $cnt = scalar @arr;
        $typ = $arr[0];
        last if ($typ =~ /^99/);
        if ($cnt >= 10) {
            # 0      1          2          3      4          5          6  7   8   9
            # ASKIK  50.052778  008.533611 RUDUS  50.047500  008.078333 1  050 240 L984
            # from   lat        lon        to     lat        lon       cat bfl efl name
            $from = $arr[0];
            $flat = $arr[1];
            $flon = $arr[2];
            $to   = $arr[3];
            $tlat = $arr[4];
            $tlon = $arr[5];
            # 1 115 285 W73
            $cat  = $arr[6]; # category 1 == low altitude, 2 == high altitude
            $bfl  = $arr[7]; # begin flight level
            $efl  = $arr[8]; # end flight level
            $name = trim_all($arr[9]);
            #$ids{$name} = [ ] if (!defined $ids{$name});
            #$ra = $ids{$name};
            #push(@{$ra}, [ $from, $flat, $flon, $to, $tlat, $tlon, $cat, $bfl, $efl ]);
            $num++;
            fg_geo_inverse_wgs_84($g_clat,$g_clon,$flat,$flon,\$az1,\$az2,\$s);
            #                0      1      2      3      4     5     6     7  8   9     10    11
            push(@airways, [ $name, $from, $flat, $flon, $cat, $bfl, $efl, 0, $s, $az1, $az2, $num ]);
            fg_geo_inverse_wgs_84($g_clat,$g_clon,$tlat,$tlon,\$az1,\$az2,\$s);
            #                0      1      2      3      4     5     6     7  8   9     10    11
            push(@airways, [ $name, $to,   $tlat, $tlon, $cat, $bfl, $efl, 1, $s, $az1, $az2, $num ]);
        }
    }

    @airways = sort mycmp_decend_dist @airways;
    prt("Loaded $num airways... sorted by distance...\n") if (VERB9());
    return \@airways;
}

sub show_awy_list($) {
    my $rawys = shift;
    my $cnt = scalar @{$rawys};
    #                 0      1      2      3      4     5     6     7  8   9     10    11
    #push(@airways, [ $name, $from, $flat, $flon, $cat, $bfl, $efl, 0, $s, $az1, $az2, $num ]);
    #push(@airways, [ $name, $to,   $tlat, $tlon, $cat, $bfl, $efl, 1, $s, $az1, $az2, $num ]);
    my $max = $g_max_out;
    my ($i,$done,$minnm,$name,$from,$flat,$flon,$cat,$bfl,$efl,$tf,$distnm);
    my ($s,$az1,$minnm2,$tfm1,$tfm2,$len);
    my ($j,$to,$tlat,$tlon,$num,$minnm3,$minnm4, $nxt);
    $done = 0;
    $minnm = 0;
    $minnm2 = 0;
    $minnm3 = 0;
    $minnm4 = 0;
    my @arr = ();
    for ($i = 0; $i < $cnt; $i++) {
        last if ($done == $max);
        $name = ${$rawys}[$i][0];
        $len = length($name);
        $minnm = $len if ($len > $minnm);
        $from = ${$rawys}[$i][1];
        $len = length($from);
        $minnm2 = $len if ($len > $minnm2);
        $done++;
        $num  = ${$rawys}[$i][11];
        ### ${$rawys}[$i][12] = 0;
        $nxt = $i;
        for ($j = 0; $j < $cnt; $j++) {
            next if ($i == $j);
            if ($num == ${$rawys}[$j][11]) {
                $len = length(${$rawys}[$j][1]);
                $minnm3 = $len if ($len > $minnm3);
                $nxt = $j;
                last;
            }
        }
        $s    = ${$rawys}[$i][8];
        $distnm = $s * $SG_METER_TO_NM;
        $distnm = (int($distnm * 10) / 10);
        $distnm .= ".0" if ($distnm == int($distnm));
        $len = length($distnm);
        $minnm4 = $len if ($len > $minnm4);
        push(@arr,[$i,$nxt]);
    }
    $done = 0;
    #           Y23   fr OKAPI -31.8644440, 148.6538890 10.0 175.1 to TW    -31.0662030, 150.8300220 1 180 600
    my $head = "Name  tf Code  Latitude    Longigude    NM   Hdg   tf Code  Latitude    Longitude    C BFL EFL";
    prt("$head\n");
    for ($i = 0; $i < $cnt; $i++) {
        last if ($done == $max);
        $name = ${$rawys}[$i][0];
        $from = ${$rawys}[$i][1];
        $flat = ${$rawys}[$i][2];
        $flon = ${$rawys}[$i][3];
        $cat  = ${$rawys}[$i][4];
        $bfl  = ${$rawys}[$i][5];
        $efl  = ${$rawys}[$i][6];
        $tf   = ${$rawys}[$i][7];
        $s    = ${$rawys}[$i][8];
        $az1  = ${$rawys}[$i][9];
        $num  = ${$rawys}[$i][11];
        $to = "NOTFND";
        $tlat = 0;
        $tlon = 0;
        for ($j = 0; $j < $cnt; $j++) {
            next if ($i == $j);
            if ($num == ${$rawys}[$j][11]) {
                $to   = ${$rawys}[$j][1];
                $tlat = ${$rawys}[$j][2];
                $tlon = ${$rawys}[$j][3];
                last;
            }
        }
        $done++;

        # display format
        if ($tf) {
            $tfm1 = "to";
            $tfm2 = "fr";
        } else {
            $tfm2 = "to";
            $tfm1 = "fr";
        }
        $name .= ' ' while (length($name) < $minnm);
        $from .= ' ' while (length($from) < $minnm2);
        $to   .= ' ' while (length($to) < $minnm3);
        set_lat_stg(\$flat);
        set_lon_stg(\$flon);
        $distnm = $s * $SG_METER_TO_NM;
        $distnm = (int($distnm * 10) / 10);
        $distnm .= ".0" if ($distnm == int($distnm));
        #$distnm = ' '.$distnm while (length($distnm) < $g_distmin);
        $distnm = ' '.$distnm while (length($distnm) < $minnm4);
        set_lat_stg(\$tlat);
        set_lon_stg(\$tlon);
        set_azimuth_stg(\$az1);
        prt("$name $tfm1 $from $flat,$flon $distnm $az1 $tfm2 $to $tlat,$tlon $cat $bfl $efl\n");
    }
}

sub get_bucket_info {
   my ($lon,$lat) = @_;
   my $b = Bucket2->new();
   $b->set_bucket($lon,$lat);
   return $b->bucket_info();
}

# 14/12/2010 - Switch to using the Bucket2.pm
# 02/05/2011 - add the tile INDEX
sub get_tile { # $alon, $alat
	my ($lon, $lat) = @_;
    my $b = Bucket2->new();
    $b->set_bucket($lon,$lat);
    return $b->gen_base_path()."/".$b->gen_index();
}

sub get_bucket_wid($$) {
	my ($lon, $lat) = @_;
    my $b = Bucket2->new();
    my $wid = $b->get_width();
    return $wid;
}

sub get_bucket_hgt($$) {
	my ($lon, $lat) = @_;
    my $b = Bucket2->new();
    my $hgt = $b->get_height();
    return $hgt;
}

sub get_bucket_index($$) { # $alon, $alat
	my ($lon, $lat) = @_;
    my $b = Bucket2->new();
    $b->set_bucket($lon,$lat);
    return $b->gen_index();
}

sub show_touching_buckets($$) {
    my ($lon,$lat) = @_;
    my $b = Bucket2->new();
    $b->set_bucket($lon,$lat);
    my ($i,$i2,$nb,$line,$nbt,$bpos);
    $nbt = $b->gen_base_path()."/".$b->gen_index();
    prt("[v2] Set of 8 touching bucket to $nbt\n");
    my %pos = (
        0 => 'BL',
        1 => 'BC',
        2 => 'BR',
        3 => 'CR',
        4 => 'TR',
        5 => 'TC',
        6 => 'TL',
        7 => 'CL' );
    for ($i = 0; $i <= 7; $i++) {
        $i2 = $i + 1;
        $nb = $b->get_next_bucket($i);
        $nbt = $nb->gen_base_path()."/".$nb->gen_index();
        $bpos = $pos{$i};
        $line = "$i2: $bpos: $nbt";
        prt("$line\n");
    }
}

#########################################
### MAIN ###
parse_args(@ARGV);
load_apt_data();
if (process_in_icao($in_icao)) {
    #               0    1     2     3     4     5     6      7     8  9    10
    #push(@navlist,[$typ,$nlat,$nlon,$nalt,$nfrq,$nrng,$nid  ,$name,$s,$az1,$az2]);
    $rnavaids = load_nav_file();
    show_distance_list($in_icao);
    ##prt("Bounds:lat,lon: -minmax=$g_minlat,$g_minlon,$g_maxlat,$g_maxlon alt-minmax=$g_minalt,$g_maxalt\n");
    prt("Bounds minlat=$g_minlat minlon=$g_minlon maxlat=$g_maxlat maxlon=$g_maxlon\n");
    prt("Altitude range min=$g_minalt max=$g_maxalt\n");
    my ($lat,$lon);
    if (VERB1()) {
        my $clat = ($g_minlat + $g_maxlat) / 2;
        my $clon = ($g_minlon + $g_maxlon) / 2;
        my $alat = sprintf("%2.9f",$clat);
        my $alon = sprintf("%3.9f",$clon);
        my $line = "Center: lat=$alat lon=$alon ";
        $line .= " fg=".get_tile($clon,$clat);
		prt("$line\n"); # print
        $line = "Bucket: ".get_bucket_info($clon,$clat);
		prt("$line\n"); # print
        show_touching_buckets($clon,$clat) if (VERB2());
        if (VERB5()) {
            my $b = Bucket2->new();
            $b->set_bucket($clon,$clat);
            my ($i,$i2,$nb,$line,$nbt,$bpos,$ind);
            $ind = $b->gen_index();

            my $wid = get_bucket_wid($clon,$clat) / 5;
            my $hgt = get_bucket_hgt($clon,$clat) / 5;
            my $cnt = 0;
            my %indexes =();
            for ($lon = $g_minlon; $lon <= $g_maxlon; $lon += $wid) {
                for ($lat = $g_minlat; $lat < $g_maxlat; $lat += $hgt) {
                    $b->set_bucket($lon,$lat);
                    $ind = $b->gen_index();
                    if (!defined $indexes{$ind}) {
                        $indexes{$ind} = 1;
                        $cnt++;
                    }
                }
            }
            prt("[v5] Area spans $cnt SG buckets...\n");
            my $len = length($cnt);
            my $form = '%'.sprintf("%d",$len)."d";
            my $acnt = sprintf($form,$cnt);
            $cnt = 0;
            %indexes = ();
            for ($lon = $g_minlon; $lon <= $g_maxlon; $lon += $wid) {
                for ($lat = $g_minlat; $lat <= $g_maxlat; $lat += $hgt) {
                    $b->set_bucket($lon,$lat);
                    $ind = $b->gen_index();
                    if (!defined $indexes{$ind}) {
                        $indexes{$ind} = 1;
                        $cnt++;
                        $acnt = sprintf($form,$cnt);
                        #prt(" $acnt: Bucket: ".get_bucket_info($lon,$lat)."\n");
                        prt(" $acnt: Tile: ".get_tile($lon,$lat)."\n");
                    }
                }
            }
        }
    }
    if ($show_navaids && in_world_range($g_clat,$g_clon) && (-f $navdat)) {
        $lat = $g_clat;
        $lon = $g_clon;
        set_lat_stg(\$lat);
        set_lon_stg(\$lon);
        prt("Show  $g_max_out closest NAVAIDS to $lat,$lon...\n");
        ###my $rnavs = load_nav_file();
        show_nav_list($rnavaids);
    }
    if ($show_fixes && (-f $g_fixfile) && in_world_range($g_clat,$g_clon)) {
        $lat = $g_clat;
        $lon = $g_clon;
        set_lat_stg(\$lat);
        set_lon_stg(\$lon);
        prt("Show   $g_max_out closest FIXES to $lat,$lon...\n");
        my $rfixes = load_fix_file();
        show_fix_list($rfixes);
    }
    if ($show_airways && (-f $g_awyfile) && in_world_range($g_clat,$g_clon)) {
        $lat = $g_clat;
        $lon = $g_clon;
        set_lat_stg(\$lat);
        set_lon_stg(\$lon);
        prt("Show   $g_max_out closest AIRWAYS to $lat,$lon...\n");
        my $rawys = load_awy_file();
        show_awy_list($rawys);
    }
}
pgm_exit(0,"");
########################################

sub out_xclude_list() {
    # show the set
    if ($g_xhele && $g_xsea && $g_xold) {
        prt("all of hele:sea:closed");
    } elsif ($g_xhele || $g_xsea || $g_xold) {
        my $tmp = '';
        if ($g_xhele) {
            $tmp .= ':' if (length($tmp));
            $tmp .= "hele";
        }
        if ($g_xsea) {
            $tmp .= ':' if (length($tmp));
            $tmp .= "sea";
        }
        if ($g_xold) {
            $tmp .= ':' if (length($tmp));
            $tmp .= "closed";
        }
        prt($tmp);
    } else {
        prt("none of hele:sea:closed");
    }
}

sub give_help {
    prt("$pgmname: version $VERS\n");
    prt("Usage: $pgmname [options] icao\n");
    prt("Options:\n");
    prt(" --help   (-h or -?) = This help, and exit 0.\n");
    prt(" --airways      (-a) = Load and show closest $g_max_out airways ends.\n");
    prt(" --bounds       (-b) = Show bounding box. (def=$show_bounds)\n");
    prt(" --case         (-c) = No case change. Show airport names as they appear in file.\n");
    prt(" --fix          (-f) = Load and show closest $g_max_out fixes.\n");
    prt(" --ils          (-i) = Show only airports with an ILS facility.\n");
    prt(" --load         (-l) = Load LOG at end. ($outfile)\n");
    prt(" --max <num>    (-m) = Maximum number of closest output. (def=$g_max_out)\n");
    prt(" --nav          (-n) = Load and show closest $g_max_out navaids.\n");
    prt(" --spread <deg> (-s) = Spread +/- degrees if track given. (def=$g_spread)\n");
    prt(" --track <deg>  (-t) = Find nearest on this heading in degs.");
    prt(" (def=".(($g_track == -1) ? "Off" : "$g_track, +/- spread below").")\n");
    prt(" --verb[n]      (-v) = Bump [or set] verbosity to 0,1,2,5,9. (def=$verbosity)\n");
    prt(" --xclude <typ> (-x) = eXclude type hele, sea, closed, all or none.");
    prt(" (def=");
    out_xclude_list();
    prt(")\n");
    prt(" Sources:\n");
    prt("  airports: [$aptdat] ".((-f $aptdat) ? "ok" : "NOT FOUND!")."\n");
    prt("  navaids:  [$navdat] ".((-f $navdat) ? "ok" : "NOT FOUND!")."\n");
    prt("  fixes:    [$g_fixfile] ".((-f $g_fixfile) ? "ok" : "NOT FOUND!")."\n");
    prt("  airways:  [$g_awyfile] ".((-f $g_awyfile) ? "ok" : "NOT FOUND!")."\n");
    prt(" Given an airport ICAO, find all others within $g_spread degrees, or\n");
    prt(" or/near the heading is one is given.\n");
}

sub need_arg {
    my ($arg,@av) = @_;
    pgm_exit(1,"ERROR: [$arg] must have a following argument!\n") if (!@av);
}

sub parse_args {
    my (@av) = @_;
    my ($arg,$sarg,@arr);
    while (@av) {
        $arg = $av[0];
        if ($arg =~ /^-/) {
            $sarg = substr($arg,1);
            $sarg = substr($sarg,1) while ($sarg =~ /^-/);
            if (($sarg =~ /^h/i)||($sarg eq '?')) {
                give_help();
                pgm_exit(0,"Help exit(0)");
            } elsif ($sarg =~ /^a/) {
                $show_airways = 1;
                prt("Set to load and display closest airways.\n") if (VERB1());
            } elsif ($sarg =~ /^b/) {
                $show_bounds = 1;
                prt("Show max/min bounds.\n") if (VERB1());
            } elsif ($sarg =~ /^c/) {
                $name_as_is = 1;
                prt("Set to display airport name as is.\n") if (VERB1());
            } elsif ($sarg =~ /^f/) {
                $show_fixes = 1;
                prt("Set to load and display closest fixes.\n") if (VERB1());
            } elsif ($sarg =~ /^i/) {
                $only_with_ils = 1;
                prt("Set to load and display closest fixes.\n") if (VERB1());
            } elsif ($sarg =~ /^v/) {
                if ($sarg =~ /^v.*(\d+)$/) {
                    $verbosity = $1;
                } else {
                    while ($sarg =~ /^v/) {
                        $verbosity++;
                        $sarg = substr($sarg,1);
                    }
                }
                prt("Verbosity = $verbosity\n") if (VERB1());
            } elsif ($sarg =~ /^l/) {
                $load_log = 1;
                prt("Set to load log at end.\n") if (VERB1());
            } elsif ($sarg =~ /^m/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                if ($sarg =~ /^\d+$/) {
                    $g_max_out = $sarg;
                    prt("Set maximum output to $g_max_out\n") if (VERB1());
                } else {
                    pgm_exit(1,"ERROR: Argument $arg must be followed by integer only! Not $sarg\n");
                }
            } elsif ($sarg =~ /^n/) {
                $show_navaids = 1;
                prt("Show nearest navaids after airport list.\n") if (VERB1());
            } elsif ($sarg =~ /^t/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                if (($sarg =~ /^\d+$/)&&($sarg >= 0)&&($sarg <= 360)) {
                    $g_track = $sarg;
                    prt("Set track to [$g_track] +/-$g_spread degs.\n") if (VERB1());
                } else {
                    pgm_exit(1,"ERROR: Argument $arg must be followed by integer 0-360 only! Not $sarg\n");
                }
            } elsif ($sarg =~ /^s/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                if (($sarg =~ /^\d+$/)&&($sarg >= 0.0001)&&($sarg < 90)) {
                    $g_spread = $sarg;
                    prt("Set spread to +/-$g_spread, to track $g_track degs.\n") if (VERB1());
                } else {
                    pgm_exit(1,"ERROR: Argument $arg must be followed by integer >0 and <90 only! Not $sarg\n");
                }
            } elsif ($sarg =~ /^x/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                @arr = split(':',$sarg);
                foreach $sarg (@arr) {
                    if ($sarg eq 'hele') {
                        $g_xhele = 1;
                    } elsif ($sarg eq 'sea') {
                        $g_xsea = 1;
                    } elsif ($sarg eq 'closed') {
                        $g_xold = 1;
                    } elsif ($sarg eq 'all') {
                        $g_xhele = 1;
                        $g_xsea = 1;
                        $g_xold = 1;
                    } elsif ($sarg eq 'none') {
                        $g_xhele = 0;
                        $g_xsea = 0;
                        $g_xold = 0;
                    } else {
                        pgm_exit(1,"ERROR: Argument $arg must be followed by one of hele, sea, closed, all or none! NOT $sarg.\n");
                    }
                }
                if (VERB1()) {
                    prt("Set exclude to ");
                    out_xclude_list();
                    prt("\n");
                }
            } else {
                pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n");
            }
        } else {
            $in_icao = $arg;
            prt("Set input to [$in_icao]\n") if (VERB1());
        }
        shift @av;
    }
    if ($debug_on) {
        prtw("DEBUG is ON\n");
        if (length($in_icao) ==  0) {
            $in_icao = $del_icao;
            $verbosity = 9;
        }
    }
    if (length($in_icao) ==  0) {
        pgm_exit(1,"ERROR: No input ICAO found in command!\n");
    }
}

sub get_810_spec() {
    my $txt = <<EOF;
from : http://data.x-plane.com/file_specs/Apt810.htm
Code (apt.dat) Used for 
1  Airport header data. 
16 Seaplane base header data. No airport buildings or boundary fences will be rendered in X-Plane. 
17 Heliport header data.  No airport buildings or boundary fences will be rendered in X-Plane. 
10 Runway or taxiway at an airport. 
14 Tower view location. 
15 Ramp startup position(s) 
18 Airport light beacons (usually "rotating beacons" in the USA).  Different colours may be defined. 
19 Airport windsocks. 
50 to 56 Airport ATC (Air Traffic Control) frequencies. 
EOF
    return $txt;
}
# eof - findaps.pl
