#!/usr/bin/perl -w
# NAME: flightplan.pl
# AIM: Given a starting and ending airport, try to develop a flight plan
# Method: Construct a bounding box
# have a bgn_lat,bgn_lon,bgn_alt, and an end_lat,end_lon,end_alt
# Have a FULL array of ALL airports loaded
# Have a FULL array of ALL navaids loaded
# need to construct a quadrilateral, from bgn towards end,
#         min        max
#     -----|----------|
#    /     | need     |  
#   /      | navaids  |
#  B -> E  | in this  |
#   \      | box      |
#    \     |          |
#     -----|----------|
#
# 29/09/2014 geoff mclane http://geoffair.net/mperl
use strict;
use warnings;
use File::Basename;  # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] )
use Time::HiRes qw( gettimeofday tv_interval );
use Cwd;
my $os = $^O;
my $perl_dir = '/home/geoff/bin';
my $PATH_SEP = '/';
my $temp_dir = '/tmp';
if ($os =~ /win/i) {
    $perl_dir = 'C:\GTools\perl';
    $temp_dir = $perl_dir;
    $PATH_SEP = "\\";
}
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";
# log file stuff
our ($LF);
my $pgmname = $0;
if ($pgmname =~ /(\\|\/)/) {
    my @tmpsp = split(/(\\|\/)/,$pgmname);
    $pgmname = $tmpsp[-1];
}
my $outfile = $temp_dir.$PATH_SEP."temp.$pgmname.txt";
my $out_xg = $temp_dir.$PATH_SEP."tempout.xg";
my $out_xml = $temp_dir.$PATH_SEP."tempout.xml";
open_log($outfile);
my $t0 = [gettimeofday];

# user variables
my $VERS = "0.0.3 2014-11-09";
###my $VERS = "0.0.2 2014-09-26";
my $load_log = 0;
my $in_file = '';
my $verbosity = 0;
my $out_file = '';
my $track_color = 'green';
my $runway_color = 'red';
my $bbox_color = 'gray';
my $stdalt = 5000;  # feet

my $bgn_icao = '';
my $end_icao = '';
my ($bgn_lat,$bgn_lon,$bgn_alt);
my ($end_lat,$end_lon,$end_alt);
my ($bgn_az,$tot_dist);
# offset into @g_aptlist
my $bgn_off = -1;
my $end_off = -1;

my ($actnav);

###########################################################################
### This should depends of the aircraft type AND VFR or IFR
###########################################################################
my $fp_type = 'VFR';
my $min_dist = 40;  # nm
my $max_dist = 100; # nm
my $max_max_dist = 400; # 350; # 300; # 250; # 200; # nm
my $deg_spread = 10;
my $max_deg_spread = 46; # 40; # 30;
my $extension_nm = 40; #nm
###########################################################################

sub set_defs() {
    # YGIL  1050 ft -31.69684576,148.63677076 Gilgandra, 345 km on 137, to
    # YSSY    21 ft -33.94927300,151.18134683 Sydney Intl
    $bgn_lat = -31.69684576;
    $bgn_lon = 148.63677076;
    $bgn_alt = 1050;
    $end_lat = -33.94927300;
    $end_lon = 151.18134683;
    $end_alt = 21;
    $bgn_az = 137;
    $tot_dist = 345000;
    prt("YGIL  1050 ft -31.69684576,148.63677076 Gilgandra, 345 km on 137, to\n");
    prt("YSSY    21 ft -33.94927300,151.18134683 Sydney Intl\n");
}

# =============================================================================
# This NEEDS to be adjusted to YOUR particular default location of these files.
my $CDATROOT="F:/fgdata"; # 20140127 - 3.1
if (-d "X:/fgdata") {
    $CDATROOT = "X:/fgdata";
}
my $FGROOT = (exists $ENV{'FG_ROOT'})? $ENV{'FG_ROOT'} : $CDATROOT;
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 $g_aptdat = $APTFILE;
my $g_navdat  = $NAVFILE;
my $g_fixfile = $FIXFILE;
my $g_awyfile = $AWYFILE;

# ### DEBUG ###
my $debug_on = 0;
my $def_bgn = 'YPPH';   # Perth
###my $def_bgn = 'YGIL';
my $def_end = 'YSSY';   # Sydney
#my $def_end = 'YSDU';

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

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

my @apt_lines = ();
my @g_naptlist = ();
my @g_aptlist = ();

my @g_nav_lines = ();
my @g_navlist = ();
# 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 @navtypes = qw( NDB      VOR      ILS      LOC      GS       OM      MM     IM      VDME      NDME     );

# VDME - VOR (VHF (Very High Frequency) Omni-directional Radio-range) with Distance Measuring Equipment - usually belongs to Government & Military.
# NDB - European from 280 kHz to 530 kHz 
# with a gap between 495 and 505 kHz because 500 kHz was the international maritime distress (emergency) frequency.

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

sub load_nav_data() {
    my $t1 = [gettimeofday];
	prt("\n[v9] Loading $g_navdat file ...\n") if (VERB9());
	mydie("ERROR: Can NOT locate [$g_navdat]!\n") if ( !( -f $g_navdat) );
	open NIF, "gzip -d -c $g_navdat|" or mydie( "ERROR: CAN NOT OPEN $g_navdat...$!...\n" );
	@g_nav_lines = <NIF>;
	close NIF;
    my $elap = secs_HHMMSS( tv_interval( $t1, [gettimeofday] ) );
    prt("[v9] Got ".scalar @g_nav_lines." lines in $elap...\n") if (VERB9());
}

sub load_apt_data() {
    my ($cnt,$msg);
    prt("[v9] Loading $g_aptdat file... moment... 15-20 secs...\n") if (VERB9());
    pgm_exit(1,"ERROR: Can NOT locate $g_aptdat ...$!...\n") if ( !( -f $g_aptdat) );
    open IF, "gzip -d -c $g_aptdat|" or mydie( "ERROR: CAN NOT OPEN $g_aptdat...$!...\n" );
    @apt_lines = <IF>;
    close IF;
    $cnt = scalar @apt_lines;
    my $elapsed = tv_interval ( $t0, [gettimeofday]);
    $elapsed = secs_HHMMSS($elapsed);
    prt("[v9] Done in $elapsed, got $cnt lines to scan... 20-30 secs\n") if (VERB9());
}

sub is_valid_nav($) {
	my ($t) = shift;
    if ($t && length($t)) {
        my $txt = "$t";
        my $cnt = 0;
        foreach my $n (@navset) {
            if ($n eq $txt) {
                $actnav = $navtypes[$cnt];
                return 1;
            }
            $cnt++;
        }
    }
	return 0;
}

sub get_nav_type($) {
	my ($t) = shift;
    my $txt = "$t";
    my $cnt = 0;
    foreach my $n (@navset) {
        if ($n eq $txt) {
            $txt = $navtypes[$cnt];
            last;
        }
        $cnt++;
    }
    $txt .= ' ' while (length($txt) < 4);
    return $txt;
}

sub search_nav_lines() {
    my $rnls = \@g_nav_lines;
    my $nav_cnt = scalar @{$rnls};
    prt("Processing $nav_cnt navaid records, getting distances to $bgn_lat,$bgn_lon and $end_lat,$end_lon...\n");
    my ($ln,$line,$len,$lnn,@arr,$typ,$nc,$i,$res);
    my ($nlat,$nlon,$nalt,$nfrq,$nrng,$nfrq2,$nid,$name,$km,$az);
    my ($dist1,$az11,$az12);
    my ($dist2,$az21,$az22);

    for ($ln = 0; $ln < $nav_cnt; $ln++) {
        $lnn = $ln + 1;
        $line = ${$rnls}[$ln];
		$line = trim_all($line);
        $len = length($line);
        next if ($line =~ /\s+Version\s+/i);
        next if ($line =~ /^I/);
        next if ($len == 0);
		# 0   1 (lat)   2 (lon)     3(alt) 4(feq)  5(rng)     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
		@arr = split(/\s+/,$line);
		$nc = scalar @arr;
		$typ = $arr[0];
        last if ($typ == 99);
        if ($nc < 8) {
            prt("Type: [$typ] - Handle this line [$line] - count = $nc...\n");
            pgm_exit(1,"ERROR: FIX ME FIRST!\n");
        }
		if ( is_valid_nav($typ) ) {
			$nlat  = $arr[1];
			$nlon  = $arr[2];
			$nalt  = $arr[3];
			$nfrq  = $arr[4];
			$nrng  = $arr[5];
			$nfrq2 = $arr[6];
			$nid   = $arr[7];
			$name  = '';
			for ($i = 8; $i < $nc; $i++) {
				$name .= ' ' if length($name);
				$name .= $arr[$i];
			}
            $res = fg_geo_inverse_wgs_84 ($bgn_lat,$bgn_lon,$nlat,$nlon,\$az11,\$az12,\$dist1);
            $res = fg_geo_inverse_wgs_84 ($end_lat,$end_lon,$nlat,$nlon,\$az21,\$az22,\$dist2);
            #$km = $dist / 1000;
            #$km = (int(($km + 0.05) * 10) / 10);
            #$az = (int(($az1 + 0.05) * 10) / 10);
            #prt( "[v5] $actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $km, $az\n") if (VERB5());
            #               0=typ,1=lat, 2=lon, 3=alt, 4=frq, 5-rng, 6-frq2, 7=nid,8=name,9=dist,10=az1,11=az2
            push(@g_navlist, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $dist1, $az11, $dist2, $az21] );
        } else {
            pgm_exit(1,"ERROR: FIX ME! Unknown type [$line]\n");
        }
    }
    $nc = scalar @g_navlist;
    prt("Loaded $nc navaids... from $g_navdat\n");
}

sub load_all_navaids() {
    my $t1 = [gettimeofday];
    load_nav_data();
    search_nav_lines();
    my $elap = secs_HHMMSS( tv_interval( $t1, [gettimeofday] ) );
    prt("Loaded full navaid array in $elap\n");
}

sub find_airports() {
    my ($line,$len,$type,@arr,@arr2);
    my ($trwycnt,$version);
    my ($alat,$alon,$aalt,$actl,$abld,$icao,$name);
    my ($rlat,$rlon,$rwyt,$add);
    my ($rlat1,$rlon1,$rlat2,$rlon2);
    my ($ftyp,$cfrq,$frqn);
    my $rwycnt = 0;
    my $wwcnt = 0;
    my $helicnt = 0;
    my $lncnt = 0;
    my @line_array = ();
    my $glat = 0;
    my $glon = 0;
    my $apt = '';
    my $totaptcnt = 0;
    my @runways = ();  # clear RUNWAY list
    my @waterways = ();  # clear RUNWAY list
    my @heliways = ();  # clear RUNWAY list
    my @freqs = (); # clear frequencies
    my $fndcnt = 0;
    foreach $line (@apt_lines) {
        $lncnt++;
        $line = trim_all($line);
        $len = length($line);
        next if ($len == 0);
        next if ($line =~ /^I/);
        @arr = split(/\s+/,$line);
        if ($line =~ /^\d+\s+Version\s+/) {
            #    my $ind = index($line,',');
            $version = $arr[0];
            $len = index($line,',');
            $len = 80 if ($len <= 0);
            prt(substr($line,0,$len)." ($version) file: $g_aptdat\n");
            next;
        }
        ###prt("$line\n");
        push(@line_array,$line);
        $type = $arr[0];
        # if 1=Airport, 16=SeaPlane, 17=Heliport
        if (($type == 1)||($type == 16)||($type == 17)) {	# start with 1, 16, 17
            # 0  1   2 3 4     
            # 17 126 0 0 EH0001 [H] VU medisch centrum
            # ID ALT C B NAME++
            $trwycnt = $rwycnt;
            $trwycnt += $wwcnt;
            $trwycnt += $helicnt;
            if (length($apt) && ($trwycnt > 0)) {
                # average position
                $alat = $glat / $trwycnt;
                $alon = $glon / $trwycnt;
                @arr2 = split(/\s+/,$apt);  # split airport line
                $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
                my @ra = @runways;
                my @wa = @waterways;
                my @ha = @heliways;
                my @fa = @freqs;
                if (($icao eq $bgn_icao)||($icao eq $end_icao)) {
                    #                 0      1      2      3      4      5     6     7     8
                    push(@g_aptlist, [$icao, $name, $alat, $alon, $aalt, \@ra, \@wa, \@ha, \@fa ]);
                    $fndcnt++;
                    # show_frequencies(\@fa);
                } else {
                    #                  0      1      2      3      4      5     6     7     8
                    push(@g_naptlist, [$icao, $name, $alat, $alon, $aalt, \@ra, \@wa, \@ha, \@fa ]);
                }
            }
            @line_array = ();   # clear ALL lines of this AIRPORT
            push(@line_array,$line);
            $apt = $line;
            $rwycnt = 0;
            $wwcnt = 0;
            $helicnt = 0;
            @runways = ();  # clear RUNWAY list
            @waterways = ();  # clear RUNWAY list
            @heliways = ();  # clear RUNWAY list
            @freqs = (); # clear frequencies
            $glat = 0;
            $glon = 0;
            $totaptcnt++;	# count another AIRPORT
        ###} elsif ($line =~ /^$rln\s+/) {
        } elsif ($type == 10) {
            # 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" ) {
                # $rwyt =~ s/x//g;    # remove trailing 'x'
                $glat += $rlat;
                $glon += $rlon;
                $rwycnt++;
                push(@runways, \@arr);
            }
        ###} elsif ($line =~ /^5(\d+)\s+/) {
        } elsif ( ($type >= 50) && ($type <= 56) ) {
            # frequencies
            # 50 12775 ATIS
            $ftyp = $type - 50;
            $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
            } else {
                pgm_exit(1,"Unknown [$line]\n");
            }
            if ($add) {
                my @fa3 = @arr;
                push(@freqs, \@fa3); # save the freq array
            } else {
                pgm_exit(1, "WHAT IS THIS [5$ftyp $cfrq $frqn] [$line]\n FIX ME!!!");
            }
        } elsif ($type == 14) {
            # tower location
        } elsif ($type == 15) {
            # ramp startup
        } elsif ($type == 18) {
            # Airport light beacon
        } elsif ($type == 19) {
            # Airport windsock
        # =============================================================================
        # 20140110 - Switch to LATEST git fgdata - IE 1000 Version - data cycle 2013.10
        # So must ADD all the NEW 'types', just like x-plane
        } 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) {
            # See full version 1000 specs below
            # 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
            $rlat1 = $arr[9];  # $of_lat1
            $rlon1 = $arr[10]; # $of_lon1
            $rlat2 = $arr[18]; # $of_lat2
            $rlon2 = $arr[19]; # $of_lon2
            $rlat = ($rlat1 + $rlat2) / 2;
            $rlon = ($rlon1 + $rlon2) / 2;
            ###prt( "$line [$rlat, $rlon]\n" );
            $glat += $rlat;
            $glon += $rlon;
            my @a2 = @arr;
            push(@runways, \@a2);
            $rwycnt++;
        } elsif ($type == 101) {	# 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
            # prt("$.: $line\n");
            $rlat1 = $arr[4];
            $rlon1 = $arr[5];
            $rlat2 = $arr[7];
            $rlon2 = $arr[8];
            $rlat = sprintf("%.8f",(($rlat1 + $rlat2) / 2));
            $rlon = sprintf("%.8f",(($rlon1 + $rlon2) / 2));
            if (!in_world_range($rlat,$rlon)) {
                prtw( "WARNING: $.: $line [$rlat, $rlon] NOT IN WORLD\n" );
                next;
            }
            $glat += $rlat;
            $glon += $rlon;
            my @a2 = @arr;
            push(@waterways, \@a2);
            $wwcnt++;
        } elsif ($type == 102) {	# Heliport
            # my $heli =   '102'; # Helipad
            # 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
            # prt("$.: $line\n");
            $rlat = sprintf("%.8f",$arr[2]);
            $rlon = sprintf("%.8f",$arr[3]);
            if (!in_world_range($rlat,$rlon)) {
                prtw( "WARNING: $.: $line [$rlat, $rlon] NOT IN WORLD\n" );
                next;
            }
            $glat += $rlat;
            $glon += $rlon;
            my @a2 = @arr;
            push(@heliways, \@a2);
            $helicnt++;
        } 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
        # ===============================================================================
        } elsif ($type == 99) {
        ### } elsif ($line =~ /^$lastln\s?/) {	# 99, followed by space, count 0 or more ...
            prt( "Reached END OF FILE ... \n" ) if (VERB9());
            last;
        } else {
            my $cnt = scalar @apt_lines;
            my $elapsed = tv_interval ( $t0, [gettimeofday]);
            $elapsed = secs_HHMMSS($elapsed);
            prt("FIX ME - LINE UNCASED $type - Line ".get_nn($lncnt)." of ".get_nn($cnt)." - $elapsed\n");
            prt("$line\n");
            pgm_exit(1,"");
        }
    }
}

sub process_inputs() {
    load_apt_data();
    my $t1 = [gettimeofday];
    find_airports();
    my $max  = scalar @g_aptlist;
    my $acnt = scalar @g_naptlist;
    my $elap = secs_HHMMSS( tv_interval( $t1, [gettimeofday] ) );
    prt("From $acnt airports, found $max in $elap...\n");
    my ($ra);
    #                   0      1      2      3      4      5     6     7     8
    #push(@g_naptlist, [$icao, $name, $alat, $alon, $aalt, \@ra, \@wa, \@ha, \@fa ]);
    my ($i,$rlat,$rlon,$calt);
    my ($icao1,$name1,$alat1,$alon1,$aalt1,$rra1,$rwa1,$rha1,$rfa1);
    my ($icao2,$name2,$alat2,$alon2,$aalt2,$rra2,$rwa2,$rha2,$rfa2);
    my ($dist,$az1,$az2,$s,$ret);
    for ($i = 0; $i < $max; $i++) {
        $ra = $g_aptlist[$i];
        $icao1 = ${$ra}[0];
        if ($icao1 eq $bgn_icao) {
            $bgn_off = $i;
        } elsif ($icao1 eq $end_icao) {
            $end_off = $i;
        }
    }
    $ret = 0;
    if ($bgn_off == -1) {
        prt("icao $bgn_icao NOT FOUND! ");
        $ret++;
    }
    if ($end_off == -1) {
        prt("icao $end_icao NOT FOUND! ");
        $ret++;
    }
    if ($ret > 0) {
        prt("FAILED!\n");
        return;
    }

    $ra = $g_aptlist[$bgn_off];
    $icao1 = ${$ra}[0];
    $name1 = ${$ra}[1];
    $alat1 = ${$ra}[2];
    $alon1 = ${$ra}[3];
    $aalt1 = ${$ra}[4];
    $rra1  = ${$ra}[5];
    $rwa1  = ${$ra}[6];
    $rha1  = ${$ra}[7];
    $rfa1  = ${$ra}[8];
    $ra = $g_aptlist[$end_off];
    $icao2 = ${$ra}[0];
    $name2 = ${$ra}[1];
    $alat2 = ${$ra}[2];
    $alon2 = ${$ra}[3];
    $aalt2 = ${$ra}[4];
    $rra2  = ${$ra}[5];
    $rwa2  = ${$ra}[6];
    $rha2  = ${$ra}[7];
    $rfa2  = ${$ra}[8];

    $bgn_lat = $alat1;
    $bgn_lon = $alon1;
    $bgn_alt = $aalt1;
    $end_lat = $alat2;
    $end_lon = $alon2;
    $end_alt = $aalt2;
    $ret = fg_geo_inverse_wgs_84($bgn_lat, $bgn_lon, $end_lat, $end_lon, \$az1, \$az2, \$s);
    $bgn_az = $az1;
    $tot_dist = $s;

    # just for display
    $rlat = sprintf("%.8f",$alat1);
    $rlon = sprintf("%.8f",$alon1);
    $calt = sprintf("%5d",$aalt1). " ft";
    #$ret = fg_geo_inverse_wgs_84($alat1, $alon1, $alat2, $alon2, \$az1, \$az2, \$s);
    $az2 = int( $az1 + 0.5 );
    $dist = int( ($s + 0.5) / 1000 )." km";
    prt("$icao1 $calt $rlat,$rlon $name1, $dist on $az2, to\n");
    $rlat = sprintf("%.8f",$alat2);
    $rlon = sprintf("%.8f",$alon2);
    $calt = sprintf("%5d",$aalt2). " ft";
    prt("$icao2 $calt $rlat,$rlon $name2\n");
}

# FLight Plan
#
# US FCC Type
# 1. Type - VFR (I), IFR (V), DVFR - mixed Y - init IFR, Z - init VFR
#    Type of flight - S=Sceduled, N=Non-sceduled, G=General Aviation, M=Militry, X=Other?
# 2. Aircraft - IDENT
# 3. Aircraft Type / Special Equipment: H=Heavy (136K kg or more), M=Medium (7K kg +), L-Light (<7K kg)
#    Radio communication, navigation and approach aid equipment and capabilities
#    N=No COM/NAV/approach, S=standard COM/NAV/Approach + A=GBAS landing system, B=LPV (APV with SBAS)
#    C=Loran, D=DME, E1=FMC WPR ACARS, E2=D-FIS ACARS, E3=PDC ACARS, F=ADF, G=GNSS,
#    H=HF RTF, I=Intertial Navigation, J1=CPDLC ATN VDL Mode 2, J2=CPDLC FANS 1/A HFDL,
#    J3=CPDLC FANS 1/A VDL Mode A, J4=CPDLC FANS 1/A VDL Mode 2, J5=CPDLC FANS 1/A SATCOM(INMARSAT),
#    J6=CPDLC FANS 1/A SATCOM (MTSAT), J7=CPDLC FANS 1/A SATCOM (Iridium), K=MLS, L=ILS,
#    M1=ATC RTF SATCOM (INMARSAT), M2=ATC RTF (MTSAT), M3=ATC RTF (Iridium), O=VOR P1�P9 Reserved for RCP
#    R=PBN approved, S=VHF RTF, VOR, and ILS, T=TACAN, U=UHF RTF, V=VHF RTF, W=RVSM approved, X=MNPS approved,
#    Y=VHF with 8.33 kHz channel spacing capability, Z=Other equipment with COM/, NAV/ and/or DAT/, as appropriate.
# 4. True Airspeed - KTS
# 5. Departure Point - SID, ICAO, ALT
# 6. Departure Time - Zulu - Proposed/Actual
# 7. Crusing Altitude - feet or FL - Flight level, expressed as F followed by 3 figures (for example, F085; F330), or
#    Standard Metric Level in tens of meters, expressed as S followed by 4 figures (for example, S1130), or
#    Altitude in hundreds of feet, expressed as A followed by 3 figures (for example, A045; A100), or
#    Altitude in tens of meters, expressed as M followed by 4 figures (for example, M0840), or
#    for uncontrolled VFR flights, the letters VFR.
# 8. ROUTE OF FLIGHT - Each waypoint
#    The identification of the significant point, followed by the bearing from the point in the form 
#    of 3 figures giving degrees magnetic, followed by the distance from the point in the form of 3 
#    figures expressing nautical miles. For example, a point 180 magnetic at a distance of 40 nautical 
#    miles from VOR DUB should be expressed as DUB180040.
# 9. Destination - STAR, ICAO, ALT
#10. Est. Time Enroute - HH:MM
#11. Remarks
#12. Fuel on Board - in HH:MM
#13. Alternate Airports
#14. Pilot - Name, address, telephone
#15. Number on board:
#16. Color of aircraft
#

sub show_airport($) {
    my $ra = shift;
    my ($icao, $name, $alat, $alon, $aalt, $rrwa, $wa, $ha, $fa);

    $icao = ${$ra}[0];
    $name = ${$ra}[1];
    $alat = ${$ra}[2];
    $alon = ${$ra}[3];
    $aalt = ${$ra}[4];
    $rrwa = ${$ra}[5];
    $wa   = ${$ra}[6];
    $ha   = ${$ra}[7];
    $fa   = ${$ra}[0];

    # for display
    $alon = sprintf("%.8f",$alon);
    $alat = sprintf("%.8f",$alat);
    $aalt = sprintf("%5d",$aalt)." ft";
    prt("$icao $alat,$alon,$aalt $name\n");
}

#/** Feet to Meters */
my $FEET_TO_METER = 0.3048;

# 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). 
);

sub get_ll_stg($$) {
    my ($lat,$lon) = @_;
    my $stg = sprintf("%.8f,%.8f",$lat,$lon);
    $stg .= ' ' while (length($stg) < 23);
    return $stg;
}

# radio frequency listing
# 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'
);

my %off2name2 = (
    0 => 'ATIS',
    1 => 'UNICOM',
    2 => 'CLR',
    3 => 'GRD',
    4 => 'TWR',
    5 => 'APP',
    6 => 'DEP'
);

my $use_full_list = 0; # seems better to GROUP frequencies
my $add_name_show = 0;  # does NOT seem helpful
my $use_short_names = 1; # Use APP instead of Approach

sub show_frequencies($) {
    my $rfa = shift;
    my $rfc = scalar @{$rfa};
    my ($tmp,$rtlen);
    my ($rfna,$line,$block,$len,$ra);
    my ($ev,$fr,$fn,$evnm,$ftyp);
    my %names = ();
    my $max_line = 100;
    my $info = '';
    if ($rfc) {
        $tmp = "radio:".$rfc." [";
        $rtlen = length($tmp);
        $info .= $tmp;
        foreach $ra (@{$rfa}) {
            # 50 12775 ATIS
            $ev = ${$ra}[0]; # number in file 50, 51, ...., 56
            $fr = ${$ra}[1]; # frequency x 100
            $fn = ${$ra}[2];   # type AWIS, CTAF, ...
            if ($ev && $fr && $fn) {
                #### prt("$ev $fr $fn\n");
                $fr /= 100;
            } else {
                pgm_exit(1,"ERROR: Frequency array FAILED\n");
            }

            # prepare information
            $evnm = 'UNK'.$ev.'?';
            $ftyp = $ev - 50;
            if ( ($ftyp >= 0) && ($ftyp <= 6) ) {
                if ($use_short_names) {
                    $evnm = $off2name2{$ftyp};
                } else {
                    $evnm = $off2name{$ftyp};
                }
            }
            #$info .= " $ev $fr $fn";
            #$info .= " $evnm $fn $fr";
            if ($use_full_list) {
                $info .= " $evnm $fr ($fn)";
            } else {
                $names{$evnm} = [] if (!defined $names{$evnm});
                $rfna = $names{$evnm};
                if ($add_name_show) {
                    push(@{$rfna}, "$fr ($fn)");
                } else {
                    push(@{$rfna},$fr);
                }
            }
        }
        if (!$use_full_list) {
            my ($key,$val,$wrap);
            if ($add_name_show) {
                $wrap = 0;
                foreach $key (sort keys %names) {
                    $rfna = $names{$key};
                    $info .= " $key:";
                    foreach $val (@{$rfna}) {
                        $info .= " $val";
                    }
                    $wrap++;
                    if ($wrap == 3) {
                        $wrap = 0;
                        $info .= "\n";
                    }
                }
            } else {
                $line = $info;  # start the line
                $info = '';
                foreach $key (sort keys %names) {
                    $rfna = $names{$key};
                    $block = '';
                    foreach $val (@{$rfna}) {
                        $block .= ' ' if (length($block));
                        $block .= $val;
                    }
                    $block = "$key: $block";
                    $len = length($line) + length($block); 
                    #prt("got len $len\n");
                    if ($len > $max_line) {
                        $info .= "$line\n" if (length($line));
                        $line = ' ' x $rtlen;
                        #prt("wrapped line\n");
                    }
                    $line .= "$block ";
                }
                $info .= $line if (length($line));
                $info =~ s/\s+$//;
            }
        }
        $info .= ']';
    } else {
        $info .= " [No freq. info]";
    }
    prt("$info\n");
}

sub show_runways($$$) {
    my ($rrwys,$rwater,$rheli) = @_;
    my $rwycnt = scalar @{$rrwys};
    my $info = "runway:$rwycnt: ";
    my ($ra,$type,$tmp,$rtyp,$hdg,$rlen,$disp1,$disp2,$rwid,$rlit,$surf,$rhdg);
    my ($displ1,$displ2,$stopw1,$stopw2,$rwy1,$rwy2);
    my ($elon1,$elon2,$elat1,$elat2,$s,$az1,$az2);
    my ($rwlen,$rwlen2,$hdgr,$eaz1,$eaz2,$hdg1,$hdg2);
    my $annoxg = '';
    foreach $ra (@{$rrwys}) {
        $tmp = scalar @{$ra};
        $type = ${$ra}[0];  # get first 'type' entry
        ###prt(join(" ",@{$ra})." t=$type c=$tmp\n");
        ###next;
        if ($type == 10) {
            # 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 
            if ($tmp < 15) {
                foreach $hdg (@{$ra}) {
                    $info .= "[$hdg] ";
                }
                pgm_exit(1,"ERROR: Invalid runway array cnt $tmp! $info\n");
            }
            $rtyp = ${$ra}[3];
            $hdg  = ${$ra}[4];
            $rlen = ${$ra}[5];  # length, in feet
            # For example, for displaced threshold lengths of 543 feet and 1234 feet, 
            # the code would be 543.1234.
            $tmp    = ${$ra}[6];  # get displacements - feet - threshold
            $displ1 = int($tmp);
            $displ2 = ($tmp - $displ1) * 10000;
            $tmp    = ${$ra}[7];  # get stopway - feet
            $stopw1 = int($tmp);
            $stopw2 = ($tmp - $stopw1) * 10000;
            $rwid   = ${$ra}[8];  # WIDTH in feet
            $rlit   = ${$ra}[9];  # LIGHTS
            $surf   = ${$ra}[10]; # add surface type

            $rtyp =~ s/x+$//;   # REMOVE any TRAILIN 'x', but may have 'L', 'R', 'C', 'S' appended
            if ($rtyp =~ /^\d+$/) {
                $rhdg = $rtyp * 10; # 2010-12-15 - get opposite end numbers
            } else {
                $rhdg = $hdg; # get opp heading, but may NOT be per numbers
            }
            $rhdg += 180; # reverse it
            $rhdg -= 360 if ($rhdg >= 360); # drop wrap
            $rwy1 = $rtyp;
            $rwy2 = int($rhdg / 10);
            $rhdg = int($rhdg / 10);
            $rhdg = "0$rhdg" if ($rhdg < 10);
            # display it
            #######################################################
            $info .= "\n"; #  if (VERB1()); # new line
            $info .= " $rtyp/$rhdg ($hdg) ";
            $info .= $rlen." ft.";  # length in FEET
            if (defined $runway_surface{$surf}) {
                $info .= " (s=".$runway_surface{$surf}.")";
            }
            $rwlen2 = (${$ra}[5] * $FEET_TO_METER) / 2;
            $hdgr = $hdg + 180;
            $hdgr -= 360 if ($hdgr >= 360);
            fg_geo_direct_wgs_84( ${$ra}[1], ${$ra}[2], $hdg , $rwlen2, \$elat1, \$elon1, \$eaz1 );
            fg_geo_direct_wgs_84( ${$ra}[1], ${$ra}[2], $hdgr, $rwlen2, \$elat2, \$elon2, \$eaz2 );
            $hdg1 = $hdg;
            $hdg2 = $hdgr;
            $az1 = $hdg1;
            $az2 = $hdg2;
            if (VERB1()) {
                $info .= " ".${$ra}[1].",".${$ra}[2];
                if (VERB2()) {
                    # show ENDS of runway
                    $info .= "\n $rtyp: $elat1,$elon1 $rhdg: $elat2,$elon2";
                    $info .= " th=$displ1/$displ2 sp=$stopw1/$stopw2";
                }
            }
        } elsif ($type == 100) {
            # 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  = ${$ra}[1];  # WIDTH in meters? NOT SHOWN
            $surf  = ${$ra}[2];  # add surface type

            $rwy1  = ${$ra}[8];
            $elat1 = ${$ra}[9];
            $elon1 = ${$ra}[10];

            $rwy2 = ${$ra}[17];
            $elat2 = ${$ra}[18];
            $elon2 = ${$ra}[19];
            my $res = fg_geo_inverse_wgs_84 ($elat1,$elon1,$elat2,$elon2,\$az1,\$az2,\$s);
            # display it
            # ==========================================================================
            $info .= "\n"; #  if (VERB1()); # new line
            $s = (int(($s + 0.05) * 10) / 10);
            $az1 = (int(($az1 + 0.05) * 10) / 10);
            $rwy1 .= ' ' while (length($rwy1) < 3);
            $rwy2 .= ' ' while (length($rwy2) < 3);
            $info .= " $rwy1: ".get_ll_stg($elat1,$elon1)." $rwy2: ".get_ll_stg($elat2,$elon2)." b=$az1 l=$s m.";
            if (defined $runway_surface{$surf}) {
                $info .= " (s=".$runway_surface{$surf}.")";
            }
        } else {
            pgm_exit(1,"Uncoded RUNWAY type $type - FIX ME!\n".join(" ",@{$ra})."\n");
        }
        $annoxg .= "$elon1 $elat1\n";
        $annoxg .= "$elon2 $elat2\n";
        $annoxg .= "NEXT\n";
    }
    $info .= "\n";
    # 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
    # from : http://data.x-plane.com/file_specs/XP%20APT1000%20Spec.pdf
    # 0 101     - Raw code for waterway
    # 1 22.86   - width in meters
    # 2 0       - perimeter buoys
    # 3 16      - runway number
    # 4 29.129  - lat
    # 5 -089.39 - lon
    # 6 25      - runway number
    # 7 29.133  - lat
    # 8 -089.38 - lon
    $rwycnt = scalar @{$rwater};
    if ($rwycnt) {
        $info .= "waterway:$rwycnt:\n";
        foreach $ra (@{$rwater}) {
            $rwid  = ${$ra}[1];  # WIDTH in meters? NOT SHOWN
            $surf  = ${$ra}[2];  # buoys
            $rwy1  = ${$ra}[3];
            $elat1 = ${$ra}[4];
            $elon1 = ${$ra}[5];
            $rwy2  = ${$ra}[6];
            $elat2 = ${$ra}[7];
            $elon2 = ${$ra}[8];
            my $res = fg_geo_inverse_wgs_84 ($elat1,$elon1,$elat2,$elon2,\$az1,\$az2,\$s);
            # display it
            # ==========================================================================
            $s = (int(($s + 0.05) * 10) / 10);
            $az1 = (int(($az1 + 0.05) * 10) / 10);
            $rwy1 .= ' ' while (length($rwy1) < 3);
            $rwy2 .= ' ' while (length($rwy2) < 3);
            $info .= " $rwy1: ".get_ll_stg($elat1,$elon1)." $rwy2: ".get_ll_stg($elat2,$elon2)." b=$az1 l=$s m.";
            $info .= "\n"; #  if (VERB1()); # new line
        }
    }
    $rwycnt = scalar @{$rheli};
    if ($rwycnt) {
        # 0   1  2           3             4    5     6     7 8 9 10   11 
        # 102 H1 47.53918248 -122.30722302 2.00 10.06 10.06 1 0 0 0.25 0
        # 102 Helipad
        # 0 102             Row code for a helipad
        # 1 H1              Designator for a helipad. Must be unique at an airport. Usually “H” suffixed by an integer (eg. “H1”, “H3”)
        # 2 47.53918248     Latitude of helipad centre
        # 3 -122.30722302   Longitude of helipad centre in decimal degrees
        # 4 2.00            Orientation (true heading) of helipad in degrees
        # 5 10.06           Helipad length in metres Two decimal places recommended (metres), must be >=1.00
        # 6 10.06           Helipad width in metres Two decimal places recommended (metres), must be >= 1.00
        # 7 1               Helipad surface code Integer value for a Surface Type Code (see below)
        # 8 0               Helipad markings 0 (other values not yet supported)
        # 9 0               Code defining a helipad shoulder surface type 0=no shoulder, 1=asphalt shoulder, 2=concrete shoulder
        #10 0.25            Helipad smoothness (not used by X-Plane yet) 0.00 (smooth) to 1.00 (very rough). Default is 0.25
        #11 0               Helipad edge lighting 0=no edge lights, 1=yellow edge lights
        $info .= "helipad:$rwycnt:\n";
        $rwy1  = ${$ra}[1];
        $elat1 = ${$ra}[2];
        $elon1 = ${$ra}[3];
        $rwlen = ${$ra}[5];
        $rwlen2= ${$ra}[6];
        $info .= " $rwy1: ".get_ll_stg($elat1,$elon1)." $rwlen x $rwlen2 m.";
        $info .= "\n"; #  if (VERB1()); # new line
    }
    prt($info);
}

sub get_runways_xg($) {
    my $ra = shift;
    my ($rrwa, $wa, $ha, $fa);
    my ($tmp,$type,$rlat1,$rlat2,$rlon1,$rlon2,$rlat3,$rlon3,$rlat4,$rlon4);
    my ($elat1,$elon1,$elat2,$elon2,$rwid2,$res,$rcnt);
    my ($az1,$az2,$dist,$az);
    show_airport($ra);
    $rrwa = ${$ra}[5];
    $wa   = ${$ra}[6];
    $ha   = ${$ra}[7];
    $rcnt = scalar @{$rrwa};
    my $xg = '';
    if ($rcnt > 0) {
        $xg .= "color $runway_color\n";
        foreach $ra (@{$rrwa}) {
            $tmp = scalar @{$ra};
            $type = ${$ra}[0];  # get first 'type' entry
            if ($type == 10) {
                #  0  1          2          3
                # 10  36.962213  127.031071 14x 131.52  8208 1595.0620 0000.0000   150 321321  1 0 3 0.25 0 0300.0300
            } elsif ($type == 100) {
                # 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
                $rwid2 = ${$ra}[1] / 2;
                $elat1 = ${$ra}[9];
                $elon1 = ${$ra}[10];
                $elat2 = ${$ra}[18];
                $elon2 = ${$ra}[19];
                $res = fg_geo_inverse_wgs_84 ($elat1,$elon1,$elat2,$elon2,\$az1,\$az2,\$dist);
                $az2 = $az1 + 90;
                $az2 -= 360 if ($az2 >= 360);
                $res = fg_geo_direct_wgs_84($elat1,$elon1,$az2,$rwid2,\$rlat1, \$rlon1,\$az);
                $res = fg_geo_direct_wgs_84($elat2,$elon2,$az2,$rwid2,\$rlat3, \$rlon3,\$az);
                $az2 = $az1 - 90;
                $az2 += 360 if ($az2 < 0);
                $res = fg_geo_direct_wgs_84($elat1,$elon1,$az2,$rwid2,\$rlat2, \$rlon2,\$az);
                $res = fg_geo_direct_wgs_84($elat2,$elon2,$az2,$rwid2,\$rlat4, \$rlon4,\$az);
                # got the 4 corners
                $xg .= "$rlon1 $rlat1\n";
                $xg .= "$rlon2 $rlat2\n";
                $xg .= "$rlon3 $rlat3\n";
                $xg .= "$rlon4 $rlat4\n";
                $xg .= "NEXT\n";
            }
        }
    }
    return $xg;
}


sub show_airport_all($) {
    my $ra = shift;
    my ($rrwa, $wa, $ha, $fa);
    show_airport($ra);
    $rrwa = ${$ra}[5];
    $wa   = ${$ra}[6];
    $ha   = ${$ra}[7];
    $fa   = ${$ra}[8];
    show_runways($rrwa,$wa,$ha);
    show_frequencies($fa);
}


# BBOX lef_lon, bot_lat, rit_lon, top_lat 
sub find_apts($$) {
    my ($bbox,$rapts) = @_;
    my ($lef_lon, $bot_lat, $rit_lon, $top_lat);
    my @arr = split(",",$bbox);
    my $cnt = scalar @arr;
    return 0 if ($cnt != 4);
    $lef_lon = $arr[0];
    $bot_lat = $arr[1];
    $rit_lon = $arr[2];
    $top_lat = $arr[3];
    @arr = ();
    ##                  0      1      2      3      4      5     6     7     8
    #push(@g_naptlist, [$icao, $name, $alat, $alon, $aalt, \@ra, \@wa, \@ha, \@fa ]);
    my ($ra, $icao, $name, $alat, $alon, $aalt, $rrwa, $wa, $ha, $fa);
    foreach $ra (@g_naptlist) {
        $icao = ${$ra}[0];
        $name = ${$ra}[1];
        $alat = ${$ra}[2];
        $alon = ${$ra}[3];
        $aalt = ${$ra}[4];
        $rrwa = ${$ra}[5];
        $wa   = ${$ra}[6];
        $ha   = ${$ra}[7];
        $fa   = ${$ra}[0];
        if (($alon >= $lef_lon)&&
            ($alon <= $rit_lon)&&
            ($alat >= $bot_lat)&&
            ($alat <= $top_lat)) {
            push(@arr,$ra);
        }
    }
    $cnt = scalar @arr;
    if ($cnt > 0) {
        @{$rapts} = @arr;
    }
    return $cnt;
}

sub show_navaid($) {
    my $ra = shift;
    #                   0     1      2      3      4      5      6       7     8      9       10     11      12
    # push(@g_navlist, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $dist1, $az11, $dist2, $az21] );
    my ($typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $dist1, $az11, $dist2, $az21);
    $typ = ${$ra}[0];
    $nlat = ${$ra}[1];
    $nlon = ${$ra}[2];
    $nalt = ${$ra}[3];
    $nfrq = ${$ra}[4];
    $nrng = ${$ra}[5];
    $nfrq2 = ${$ra}[6];
    $nid = ${$ra}[7];
    $name = ${$ra}[8];
    $dist1 = ${$ra}[9];
    $az11 = ${$ra}[10];
    $dist2 = ${$ra}[11];
    $az21 = ${$ra}[12];

    # for display
    $nlon = sprintf("%.8f",$nlon);
    $nlat = sprintf("%.8f",$nlat);
    $nalt = sprintf("%5d",$nalt)." ft";
    $dist1 = int(($dist1 + 0.5) / 1000). " km";
    $dist2 = int(($dist2 + 0.5) / 1000). " km";
    $az11 = int($az11 + 0.5);
    $az21 = int($az21 + 0.5);
    prt("$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $dist1, $az11, $dist2, $az21\n");
}

# BBOX lef_lon, bot_lat, rit_lon, top_lat 
# 20140930 - Find ONLY VOR and NDB unless $all flag is ON
sub find_wpts($$$) {
    my ($bbox,$rwpts,$all) = @_;
    #                   0     1      2      3      4      5      6       7     8      9       10     11      12
    # push(@g_navlist, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $dist1, $az11, $dist2, $az21] );
    my ($ra,$nlat,$nlon,$ntyp);
    my ($lef_lon, $bot_lat, $rit_lon, $top_lat);
    my @arr = split(",",$bbox);
    my $cnt = scalar @arr;
    return 0 if ($cnt != 4);
    $lef_lon = $arr[0];
    $bot_lat = $arr[1];
    $rit_lon = $arr[2];
    $top_lat = $arr[3];
    @arr = ();
    foreach $ra (@g_navlist) {
        $ntyp = ${$ra}[0];
        if ($all || (($ntyp == 2)||($ntyp == 3))) {
            $nlat = ${$ra}[1];
            $nlon = ${$ra}[2];
            if (($nlon >= $lef_lon)&&
                ($nlon <= $rit_lon)&&
                ($nlat >= $bot_lat)&&
                ($nlat <= $top_lat)) {
                push(@arr,$ra);
            }
        }
    }
    $cnt = @arr;
    if ($cnt > 0) {
        @{$rwpts} = @arr;
        return $cnt;
    }
    return 0;
}

# BBOX left lon, bottom lat, right lon, top lat 
sub latlon_2_bbox($$$$$$$$) {
    my ($min_lat1,$min_lon1,$max_lat1,$max_lon1,$min_lat2,$min_lon2,$max_lat2,$max_lon2) = @_;
    my $left_lon = $min_lon1;
    $left_lon = $max_lon1 if ($max_lon1 < $left_lon);
    $left_lon = $min_lon2 if ($min_lon2 < $left_lon);
    $left_lon = $max_lon2 if ($max_lon2 < $left_lon);
    my $bot_lat = $min_lat1;
    $bot_lat = $max_lat1 if ($max_lat1 < $bot_lat);
    $bot_lat = $min_lat2 if ($min_lat2 < $bot_lat);
    $bot_lat = $max_lat2 if ($max_lat2 < $bot_lat);
    my $rit_lon = $min_lon1;
    $rit_lon = $max_lon1 if ($max_lon1 > $rit_lon);
    $rit_lon = $min_lon2 if ($min_lon2 > $rit_lon);
    $rit_lon = $max_lon2 if ($max_lon2 > $rit_lon);
    my $top_lat = $min_lat1;
    $top_lat = $max_lat1 if ($max_lat1 > $top_lat);
    $top_lat = $min_lat2 if ($min_lat2 > $top_lat);
    $top_lat = $max_lat2 if ($max_lat2 > $top_lat);
    if (VERB9()) {
        prt("Polgon ($min_lat1,$min_lon1,$max_lat1,$max_lon1,$min_lat2,$min_lon2,$max_lat2,$max_lon2)\n".
            "BBOX = $left_lon,$bot_lat,$rit_lon,$top_lat\n");
    }
    return "$left_lon,$bot_lat,$rit_lon,$top_lat"; 
}

# BBOX left lon, bottom lat, right lon, top lat 
sub bbox_2_xg($) {
    my $bbox = shift;
    my @arr = split(",",$bbox);
    my $cnt = scalar @arr;
    return "# bbox $bbox FAILED ($cnt)\n" if ($cnt != 4);
    my $lef_lon = $arr[0];
    my $bot_lat = $arr[1];
    my $rit_lon = $arr[2];
    my $top_lat = $arr[3];
    my $xg = "# BBOX LBRT $lef_lon,$bot_lat,$rit_lon,$top_lat\n";
    $xg .= "$lef_lon  $bot_lat\n";
    $xg .= "$lef_lon  $top_lat\n";
    $xg .= "$rit_lon  $top_lat\n";
    $xg .= "$rit_lon  $bot_lat\n";
    $xg .= "$lef_lon  $bot_lat\n";
    $xg .= "NEXT\n";
    return $xg;
}

my ($g_min_lat,$g_min_lon,$g_max_lat,$g_max_lon);

sub set_extended_bbox($$) {
    my ($lat,$lon) = @_;
    $g_min_lon = $lon if ($lon < $g_min_lon);
    $g_max_lon = $lon if ($lon > $g_max_lon);
    $g_min_lat = $lat if ($lat < $g_min_lat);
    $g_max_lat = $lat if ($lat > $g_max_lat);
}

sub set_initial_bbox($$$$) {
    my ($bgn_lat,$bgn_lon,$end_lat,$end_lon) = @_;
    $g_min_lon = $bgn_lon;
    $g_min_lon = $end_lon if ($end_lon < $g_min_lon);
    $g_max_lon = $bgn_lon;
    $g_max_lon = $end_lon if ($end_lon > $g_max_lon);

    $g_min_lat = $bgn_lat;
    $g_min_lat = $end_lat if ($end_lat < $g_min_lat);
    $g_max_lat = $bgn_lat;
    $g_max_lat = $end_lat if ($end_lat > $g_max_lat);

    # but this is just the start point and end point
    # need to extend this by say 10 nm around each point
    # $extension_nm
    my ($res,$elat,$elon,$s,$az);
    $s = nm_2_meter($extension_nm);

    $res = fg_geo_direct_wgs_84( $bgn_lat, $bgn_lon,   0, $s, \$elat, \$elon, \$az );
    set_extended_bbox($elat,$elon);
    $res = fg_geo_direct_wgs_84( $bgn_lat, $bgn_lon,  90, $s, \$elat, \$elon, \$az );
    set_extended_bbox($elat,$elon);
    $res = fg_geo_direct_wgs_84( $bgn_lat, $bgn_lon, 180, $s, \$elat, \$elon, \$az );
    set_extended_bbox($elat,$elon);
    $res = fg_geo_direct_wgs_84( $bgn_lat, $bgn_lon, 270, $s, \$elat, \$elon, \$az );
    set_extended_bbox($elat,$elon);

    $res = fg_geo_direct_wgs_84( $end_lat, $end_lon,   0, $s, \$elat, \$elon, \$az );
    set_extended_bbox($elat,$elon);
    $res = fg_geo_direct_wgs_84( $end_lat, $end_lon,  90, $s, \$elat, \$elon, \$az );
    set_extended_bbox($elat,$elon);
    $res = fg_geo_direct_wgs_84( $end_lat, $end_lon, 180, $s, \$elat, \$elon, \$az );
    set_extended_bbox($elat,$elon);
    $res = fg_geo_direct_wgs_84( $end_lat, $end_lon, 270, $s, \$elat, \$elon, \$az );
    set_extended_bbox($elat,$elon);

}


sub get_flight_plan() {
    my $dist_rem = $tot_dist;
    my $dist_nm =  meter_2_nm($dist_rem);
    my $nxt_az = $bgn_az;

    # ok, try to get NEXT waypoint
    # $min_dist = 40;  # nm
    # $max_dist = 100; # nm
    my $min_m = nm_2_meter($min_dist);
    my $max_m = nm_2_meter($max_dist);
    #sub fg_geo_direct_wgs_84 {
    #my ( $lat1, $lon1, $az1, $s, $lat2, $lon2, $az2 ) = @_;
    my ($min_lat1,$min_lon1,$max_lat1,$max_lon1);
    my ($min_lat2,$min_lon2,$max_lat2,$max_lon2);
    my ($res,$az,$ind,$bbox,$ra,$cnt,$nxtwpt,$acnt);
    my $nxt_lat = $bgn_lat;
    my $nxt_lon = $bgn_lon;
    my $home = 0;
    my $wpt_cnt = 0;
    my ($nlat2,$nlon2,$wpts_fnd,$apts_fnd);
    my ($finding,$nxt_max_m,$icao);
    my ($typ, $typ2, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $dist1, $az11, $az12, $dist2, $az21, $az22);
    my $xcnt = 0;
    my $total_dist = 0;
    my $tmpxg = '';
    my @track = ();
    my $flag = 0;
    # offset into @g_aptlist $bgn_off, $end_off = -1;
    #my ($icao,$alat,$alon,$aalt,$rra,$rwa,$rha,$rfa);
    $ra = $g_aptlist[$bgn_off];
    my $id = ${$ra}[0];
    my $alt = ${$ra}[4];

    show_airport_all($ra);

    set_initial_bbox($bgn_lat,$bgn_lon,$end_lat,$end_lon);
    # add BEGIN of track
    push(@track,[$bgn_lat,$bgn_lon,$id,$alt]);
    prt("BEGIN: $bgn_icao ($bgn_lat,$bgn_lon), dist ".int($dist_nm)." nm, bearing ".int($nxt_az)."\n");
    if ($dist_nm < $max_dist) {
        # too short - just a direct flight
        $ra = $g_aptlist[$end_off];
        $alt = ${$ra}[4];
        prt("On bearing ".int($bgn_az+0.5)." degrees, distance ".int($dist_nm + 0.5)." nm\n");
        prt("END: $end_icao ($end_lat,$end_lon,$alt)\n");
        # add END of track
        push(@track,[$end_lat,$end_lon,$end_icao,$alt]);
        $home = 1;
    }

    $tmpxg .= "color $bbox_color\n";
    while (!$home) {
        my @wpts = ();
        my @apts = ();
        # $deg_spread = 10;
        my $az1 = $nxt_az + $deg_spread;
        $az1 -= 360 if ($az1 >= 360);
        my $az2 = $nxt_az - $deg_spread;
        $az2 += 360 if ($az2 < 0);
        $res = fg_geo_direct_wgs_84( $nxt_lat, $nxt_lon, $az1, $min_m, \$min_lat1, \$min_lon1, \$az );
        $res = fg_geo_direct_wgs_84( $nxt_lat, $nxt_lon, $az2, $min_m, \$min_lat2, \$min_lon2, \$az );
        $finding = 1;
        $nxt_max_m = $max_m;    # start with configured value
        $xcnt = 0;
        ####################################################################
        while ($finding) {
            $res = fg_geo_direct_wgs_84( $nxt_lat, $nxt_lon, $az1, $nxt_max_m, \$max_lat1, \$max_lon1, \$az );
            $res = fg_geo_direct_wgs_84( $nxt_lat, $nxt_lon, $az2, $nxt_max_m, \$max_lat2, \$max_lon2, \$az );
            prt("BBOX: $min_lat1,$min_lon1 $max_lat1,$max_lon1 $min_lat2,$min_lon2 $max_lat2,$max_lon2\n") if (VERB9());
            $bbox = latlon_2_bbox($min_lat1,$min_lon1,$max_lat1,$max_lon1,$min_lat2,$min_lon2,$max_lat2,$max_lon2);
            prt("BBOX: $bbox\n") if (VERB5());

            $cnt = 0;
            $wpts_fnd = 0;
            if (find_wpts($bbox,\@wpts,$flag)) {
                $finding = 0;
                $wpts_fnd = scalar @wpts;
                prt("Got $wpts_fnd waypoints...\n") if (VERB9());
                foreach $ra (@wpts) {
                    show_navaid($ra) if (VERB9());
                    $typ = ${$ra}[0];
                    $nlat = ${$ra}[1];
                    $nlon = ${$ra}[2];
                    $nalt = ${$ra}[3];
                    $nfrq = ${$ra}[4];
                    $nrng = ${$ra}[5];
                    $nfrq2 = ${$ra}[6];
                    $nid = ${$ra}[7];
                    $name = ${$ra}[8];
                    $dist1 = ${$ra}[9];
                    $az11 = ${$ra}[10];
                    $dist2 = ${$ra}[11];
                    $az21 = ${$ra}[12];
                    if ($cnt == 0) {
                        $nxtwpt = $ra;
                    } else {
                        # if (($typ == 3)||($typ == 12)||($typ == 13)) { # my $navVOR = '3';
                        if (($typ == 3)||($typ == 12)) { # my $navVOR = '3';
                            $typ2 = ${$nxtwpt}[0];
                            # if (($typ2 == 3)||($typ2 == 12)||($typ2 == 13)) { # my $navVOR = '3';
                            if (($typ2 == 3)||($typ2 == 12)) { # my $navVOR = '3';
                                # must choose - closest, OR better nearest desired bearing, $nxt_az
                                $nlat2 = ${$nxtwpt}[1];
                                $nlon2 = ${$nxtwpt}[2];
                                $res = fg_geo_inverse_wgs_84 ($nxt_lat,$nxt_lon,$nlat,$nlon,\$az11,\$az12,\$dist1);
                                $res = fg_geo_inverse_wgs_84 ($nxt_lat,$nxt_lon,$nlat2,$nlon2,\$az21,\$az22,\$dist1);
                                # if $az11 closer to $nxt_az than current choice $az21
                                if (abs($nxt_az - $az11) < abs($nxt_az - $az21)) {
                                    prt("Changed next wpt due bearing ".int($az11 + 0.5)." closer to desired ".int($nxt_az)."\n") if (VERB2());
                                    $nxtwpt = $ra;
                                }
                            } else {
                                $nxtwpt = $ra;
                            }
                        }
                    }
                    $cnt++;
                }
            } else {
                $xcnt++;
                $nxt_max_m += nm_2_meter(10);
                if ($nxt_max_m > nm_2_meter($max_max_dist)) {
                    $nxt_max_m -= nm_2_meter(10);
                    prt("OOPS: NO waypoints with extended max to ".int(meter_2_nm($nxt_max_m))." nm.\n") if (VERB5());
                    $cnt = 0;
                    last;
                }
                prt("UGH: Got NO waypoints... extended max to ".int(meter_2_nm($nxt_max_m))." nm.\n") if (VERB5());

            }
        } 
        #########################################################################
        if ($cnt == 0) {
            my $nxt_spread = $deg_spread + 2;
            my $spread_find = 1;
            prt("\nIncreasing spread to $nxt_spread, to try some more...\n") if (VERB5());
            while ($spread_find) {
                $az1 = $nxt_az + $nxt_spread;
                $az1 -= 360 if ($az1 >= 360);
                $az2 = $nxt_az - $nxt_spread;
                $az2 += 360 if ($az2 < 0);
                $res = fg_geo_direct_wgs_84( $nxt_lat, $nxt_lon, $az1, $min_m, \$min_lat1, \$min_lon1, \$az );
                $res = fg_geo_direct_wgs_84( $nxt_lat, $nxt_lon, $az2, $min_m, \$min_lat2, \$min_lon2, \$az );
                $finding = 1;
                $nxt_max_m = $max_m;    # start with configured value
                $xcnt = 0;
                ####################################################################
                while ($finding) {
                    $res = fg_geo_direct_wgs_84( $nxt_lat, $nxt_lon, $az1, $nxt_max_m, \$max_lat1, \$max_lon1, \$az );
                    $res = fg_geo_direct_wgs_84( $nxt_lat, $nxt_lon, $az2, $nxt_max_m, \$max_lat2, \$max_lon2, \$az );
                    prt("BBOX: $min_lat1,$min_lon1 $max_lat1,$max_lon1 $min_lat2,$min_lon2 $max_lat2,$max_lon2\n") if (VERB9());
                    $bbox = latlon_2_bbox($min_lat1,$min_lon1,$max_lat1,$max_lon1,$min_lat2,$min_lon2,$max_lat2,$max_lon2);
                    prt("BBOX: $bbox\n") if (VERB5());

                    $cnt = 0;
                    $wpts_fnd = 0;
                    if (find_wpts($bbox,\@wpts,$flag)) {
                        $finding = 0;
                        $wpts_fnd = scalar @wpts;
                        prt("Got $wpts_fnd waypoints...\n") if (VERB9());
                        foreach $ra (@wpts) {
                            show_navaid($ra) if (VERB9());
                            $typ = ${$ra}[0];
                            $nlat = ${$ra}[1];
                            $nlon = ${$ra}[2];
                            $nalt = ${$ra}[3];
                            $nfrq = ${$ra}[4];
                            $nrng = ${$ra}[5];
                            $nfrq2 = ${$ra}[6];
                            $nid = ${$ra}[7];
                            $name = ${$ra}[8];
                            $dist1 = ${$ra}[9];
                            $az11 = ${$ra}[10];
                            $dist2 = ${$ra}[11];
                            $az21 = ${$ra}[12];
                            if ($cnt == 0) {
                                $nxtwpt = $ra;
                            } else {
                                # if (($typ == 3)||($typ == 12)||($typ == 13)) { # my $navVOR = '3';
                                if (($typ == 3)||($typ == 12)) { # my $navVOR = '3';
                                    $typ2 = ${$nxtwpt}[0];
                                    # if (($typ2 == 3)||($typ2 == 12)||($typ2 == 13)) { # my $navVOR = '3';
                                    if (($typ2 == 3)||($typ2 == 12)) { # my $navVOR = '3';
                                        # must choose - closest, OR better nearest desired bearing, $nxt_az
                                        $nlat2 = ${$nxtwpt}[1];
                                        $nlon2 = ${$nxtwpt}[2];
                                        $res = fg_geo_inverse_wgs_84 ($nxt_lat,$nxt_lon,$nlat,$nlon,\$az11,\$az12,\$dist1);
                                        $res = fg_geo_inverse_wgs_84 ($nxt_lat,$nxt_lon,$nlat2,$nlon2,\$az21,\$az22,\$dist1);
                                        # if $az11 closer to $nxt_az than current choice $az21
                                        if (abs($nxt_az - $az11) < abs($nxt_az - $az21)) {
                                            prt("Changed next wpt due bearing ".int($az11 + 0.5)." closer to desired ".int($nxt_az)."\n") if (VERB2());
                                            $nxtwpt = $ra;
                                        }
                                    } else {
                                        $nxtwpt = $ra;
                                    }
                                }
                            }
                            $cnt++;
                        }
                    } else {
                        $xcnt++;
                        $nxt_max_m += nm_2_meter(10);
                        if ($nxt_max_m > nm_2_meter($max_max_dist)) {
                            $nxt_max_m -= nm_2_meter(10);
                            prt("BAH: NO waypoints, spread $nxt_spread, extended to ".int(meter_2_nm($nxt_max_m))." nm.\n") if (VERB5());
                            last;
                        }
                        prt("UGH: NO waypoints, nxt spread $nxt_spread. Extended max to ".int(meter_2_nm($nxt_max_m))." nm.\n") if (VERB5());
                    }
                } 
                #########################################################################
                if ($cnt > 0) {
                    $spread_find = 0;
                } else {
                    $nxt_spread += 2;
                    if ($nxt_spread > $max_deg_spread) {
                        prt("ZUTE: Reached max deg spread $max_deg_spread with no waypoint found!\n") if (VERB5());
                        last;
                    } else {
                        prt("\nIncreased spread to $nxt_spread!\n") if (VERB5());
                    }
                }
            }
        }

        $apts_fnd = 0;
        if (find_apts($bbox,\@apts)) {
            $apts_fnd = scalar @apts;
            if (VERB2()) {
                prt("Got $apts_fnd airports...\n");
                foreach $ra (@apts) {
                    show_airport($ra);
                }
            }
        } else {
            # prt("UGH: Got NO airports...\n");
        }
        if ($cnt > 0) {
            $ra = $nxtwpt;
            $typ = ${$ra}[0];
            $nlat = ${$ra}[1];
            $nlon = ${$ra}[2];
            $nalt = ${$ra}[3];
            $nfrq = ${$ra}[4];
            $nrng = ${$ra}[5];
            $nfrq2 = ${$ra}[6];
            $nid = ${$ra}[7];
            $name = ${$ra}[8];
            $dist1 = ${$ra}[9];
            $az11 = ${$ra}[10];
            $dist2 = ${$ra}[11];
            $az21 = ${$ra}[12];
            $tmpxg .= "anno $nlon $nlat $name\n";
            $tmpxg .= bbox_2_xg($bbox);
            $res = fg_geo_inverse_wgs_84 ($nxt_lat,$nxt_lon,$nlat,$nlon,\$az11,\$az12,\$dist1);
            $total_dist += $dist1;
            $nxt_lat = $nlat;
            $nxt_lon = $nlon;
            $wpt_cnt++;
            push(@track,[$nlat,$nlon,$nid,$nalt]);
            set_extended_bbox($nlat,$nlon);

            # for display
            ###########################################################################
            $az12 = int($az1 + 0.5);
            $dist2 = int( ($dist1 / 1000) + 0.5 )." km (".int(meter_2_nm($dist1))."nm)";
            # 1234567890123456
            # 1667 km (1000nm)
            $dist2 .= ' ' while (length($dist2) < 16);
            if (($typ == 3)||($typ == 12)||($typ == 13)) { # my $navVOR = '3';
                $nfrq /= 100;
                $nfrq .= "." if (!($nfrq =~ /\./));
                $nfrq .= '0' while (length($nfrq) < 6);
            } else {
                $nfrq = ' '.$nfrq while (length($nfrq) < 6);
            }
            $nlon2 = sprintf("%12.6f",$nlon);
            $nlat2 = sprintf("%12.6f",$nlat);
            $acnt = sprintf("%2d",$wpt_cnt);
            $nid .= ' ' while (length($nid) < 3);
            $typ = get_nav_type($typ);
            prt("$acnt wpt $typ $nid $nfrq on $az12, dist $dist2 ($nlat2,$nlon2) $name (w=$wpts_fnd/a=$apts_fnd/x=$xcnt)\n");
            ###########################################################################

            $res = fg_geo_inverse_wgs_84 ($nxt_lat,$nxt_lon,$end_lat,$end_lon,\$az11,\$az12,\$dist1);
            $dist_nm =  meter_2_nm($dist1);
            $nxt_az = $az11;
            if ($dist_nm < $max_dist) {
                # too short - just a direct flight
                $ra = $g_aptlist[$end_off];
                $alt = ${$ra}[4];
                prt("Final bearing ".int($bgn_az+0.5)." degrees, distance ".int($dist_nm + 0.5)." nm\n");
                prt("END: $end_icao ($end_lat,$end_lon), total dist ".int(meter_2_nm($total_dist))." nm\n");
                push(@track,[$end_lat,$end_lon,$end_icao,$alt]);
                $home = 1;
                show_airport_all($ra);
            }
        } else {
            prt("FAILED\n");
            $home = 2;
        }
    }

    if (($home == 1) && length($tmpxg) && length($out_xg)) {
        $ra = $g_aptlist[$bgn_off];
        $tmpxg .= get_runways_xg($ra);
        $ra = $g_aptlist[$end_off];
        $tmpxg .= get_runways_xg($ra);
        $tmpxg .= "color $track_color\n";
        $cnt = scalar @track;
        for (my $i = 0; $i < $cnt; $i++) {
            $ra = $track[$i];
            $nlat = ${$ra}[0];
            $nlon = ${$ra}[1];
            $tmpxg .= "$nlon $nlat\n";
            if ($i == 0) {
                $ra = $g_aptlist[$bgn_off];
                $icao = ${$ra}[0];
                $tmpxg .= "anno $nlon $nlat BGN $icao\n";
            } elsif (($i + 1) == $cnt) {
                $ra = $g_aptlist[$end_off];
                $icao = ${$ra}[0];
                $tmpxg .= "anno $nlon $nlat END $icao\n";
            }
        }
        $tmpxg .= "NEXT\n";
        $typ = "# flight plan from $bgn_icao to $end_icao, generate by $pgmname on ".lu_get_YYYYMMDD_hhmmss_UTC(time())." UTC\n";
        $typ .= "# bounding box: bbox=$g_min_lon,$g_min_lat,$g_max_lon,$g_max_lat (min_lon,min_lat,max_lon,max_lat)\n";

        write2file($typ.$tmpxg,$out_xg);
        prt("Bounding boxes written to $out_xg\n");
    }
    if (($home == 1) && length($out_xml)) {
        my $xml = "<?xml version=\"1.0\"?>\n";
        $xml .= "<PropertyList>\n";
        $xml .= "  <version type=\"int\">2</version>\n";
        $ra = $g_aptlist[$bgn_off];
        $icao = ${$ra}[0];
        $xml .= "  <departure>\n";
        $xml .= "    <airport type=\"string\">$icao</airport>\n";
        #    <runway type="string">21</runway>
        $xml .= "  </departure>\n";
        $xml .= "  <destination>\n";
        $ra = $g_aptlist[$end_off];
        $xml .= "    <airport type=\"string\">".${$ra}[0]."</airport>\n";
        $xml .= "  </destination>\n";
        $xml .= "  <route>\n";
        $xml .= "    <wp>\n";
        $xml .= "      <type type=\"string\">basic</type>\n";
        $xml .= "      <departure type=\"bool\">true</departure>\n";
        $ra = $track[0];
        $nlat = ${$ra}[0];
        $nlon = ${$ra}[1];
        # $xml .= "      <ident type=\"string\">21</ident>\n";
        $xml .= "      <lon type=\"double\">$nlon</lon>\n";
        $xml .= "      <lat type=\"double\">$nlat</lat>\n";
        $xml .= "      <icao type=\"string\">$icao</icao>\n";
        $xml .= "    </wp>\n";

        $cnt = scalar @track - 1;
        for (my $i = 1; $i < $cnt; $i++) {
            $ra = $track[$i];
            $nlat = ${$ra}[0];
            $nlon = ${$ra}[1];
            $id   = ${$ra}[2];
            $alt  = ${$ra}[3];
            $alt  = (int(($alt + 500) / 1000) * 1000) + $stdalt;
            $xml .= "    <wp";
            $xml .= " n=\"$i\"";
            $xml .= ">\n";
            $xml .= "      <type type=\"string\">navaid</type>\n";
            #if (($i + 1) != $cnt) {
                $xml .= "      <alt-restrict type=\"string\">at</alt-restrict>\n";
                $xml .= "      <altitude-ft type=\"double\">$alt</altitude-ft>\n";
            #}
            $xml .= "      <ident type=\"string\">$id</ident>\n";
            $xml .= "      <lon type=\"double\">$nlon</lon>\n";
            $xml .= "      <lat type=\"double\">$nlat</lat>\n";
            $xml .= "    </wp>\n";
        }
        $xml .= "  </route>\n";
        $xml .= "</PropertyList>\n";
        write2file($xml,$out_xml);
        prt("XML gpx written to $out_xml\n");
    }
}

#########################################
### MAIN ###
parse_args(@ARGV);
if ($debug_on) {
    #set_defs();
}
process_inputs();
load_all_navaids();
get_flight_plan();
pgm_exit(0,"");
########################################

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,$cnt);
    my $verb = VERB2();
    $cnt = 0;
    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/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $g_aptdat = $sarg;
                prt("Set apt.dat.gz file to [$g_aptdat].\n") if ($verb);
            } elsif ($sarg =~ /^n/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $g_navdat = $sarg;
                prt("Set nav.dat.gz file to [$g_navdat].\n") if ($verb);
            } elsif ($sarg =~ /^v/) {
                if ($sarg =~ /^v.*(\d+)$/) {
                    $verbosity = $1;
                } else {
                    while ($sarg =~ /^v/) {
                        $verbosity++;
                        $sarg = substr($sarg,1);
                    }
                }
                $verb = VERB2();
                prt("Verbosity = $verbosity\n") if ($verb);
            } elsif ($sarg =~ /^l/) {
                if ($sarg =~ /^ll/) {
                    $load_log = 2;
                } else {
                    $load_log = 1;
                }
                prt("Set to load log at end. ($load_log)\n") if ($verb);
            } elsif ($sarg =~ /^o/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $out_file = $sarg;
                prt("Set out file to [$out_file].\n") if ($verb);
            } else {
                pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n");
            }
        } else {
            if ($cnt == 0) {
                $bgn_icao = $arg;
                prt("Set begin ICAO to [$bgn_icao]\n") if ($verb);
            } elsif ($cnt == 1) {
                $end_icao = $arg;
                prt("Set end ICAO to [$end_icao]\n") if ($verb);
            } else {
                pgm_exit(1,"Already have begin $bgn_icao, and end $end_icao!\nWhat is this [$arg]?\n");
            }
            $cnt++;

        }
        shift @av;
    }

    if ($debug_on) {
        prtw("WARNING: DEBUG is ON!\n");
        if (length($bgn_icao) ==  0) {
            $bgn_icao = $def_bgn;
            prt("Set DEFAULT begin to [$bgn_icao]\n");
        }
        if (length($end_icao) ==  0) {
            $end_icao = $def_end;
            prt("Set DEFAULT end to [$end_icao]\n");
        }
    }
    if (! -f $g_aptdat) {
        pgm_exit(1,"ERROR: Can NOT stat apt dat file $g_aptdat!\n");
    }
    if (! -f $g_navdat) {
        pgm_exit(1,"ERROR: Can NOT stat nav dat file $g_navdat!\n");
    }

    if (length($bgn_icao) ==  0) {
        pgm_exit(1,"ERROR: No begin icao found in command!\n");
    }
    if (length($end_icao) ==  0) {
        pgm_exit(1,"ERROR: No end icao found in command!\n");
    }
}

sub give_help {
    prt("$pgmname: version $VERS\n");
    prt("Usage: $pgmname [options] in-file\n");
    prt("Options:\n");
    prt(" --help  (-h or -?) = This help, and exit 0.\n");
    prt(" --verb[n]     (-v) = Bump [or set] verbosity. (def=$verbosity)\n");
    prt(" --load        (-l) = Load LOG at end. ($outfile)\n");
    prt(" --air <file>  (-a) = Set apt.dat.gz file (def=$g_aptdat) ".
        ((-f $g_aptdat) ? "ok" : "NOT FOUND")."\n");
    prt(" --nav <file>  (-a) = Set nav.dat.gz file (def=$g_navdat) ".
        ((-f $g_navdat) ? "ok" : "NOT FOUND")."\n");
    prt(" --out <file>  (-o) = Write output to this file.\n");
}

# eof - template.pl
