#!/usr/bin/perl -w
# NAME: atlas-trk.pl
# AIM: Read, and display an Atlas 'track' file - a text file, with a bunch of nmea
# text strings - those emitted from a GP unit... example
# $GPRMC,010044,A,3142.142,S,14838.309,E,000.0,342.0,2112110,0.000,E*50
# $GPGGA,010044,3142.142,S,14838.309,E,1,,,-9999,F,,,,*24
# $PATLA,115.80,280.0,116.80,29.0,374*66
# 17/02/2011 - review
# 24/12/2010 geoff mclane http://geoffair.net/mperl
use strict;
use warnings;
use File::Basename;  # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] )
use Cwd;
use Time::HiRes qw( usleep gettimeofday tv_interval );
# use constant PI    => 4 * atan2(1, 1);
my $perl_dir = 'C:\GTools\perl';
unshift(@INC, $perl_dir);
require 'lib_utils.pl' or die "Unable to load 'lib_utils.pl'! Check location and \@INC content.\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";
# log file stuff
our ($LF);
my $pgmname = $0;
if ($pgmname =~ /(\\|\/)/) {
    my @tmpsp = split(/(\\|\/)/,$pgmname);
    $pgmname = $tmpsp[-1];
}
my $outfile = $perl_dir."\\temp.$pgmname.txt";
open_log($outfile);

# user variables
my $load_log = 1;
my $in_file = '';
my $SG_EPSILON = 0.0000001;
#/** Meters to Feet */
my $SG_METER_TO_FEET = 3.28083989501312335958;
#/** Meters to Nautical Miles.  1 nm = 6076.11549 feet */
my $SG_METER_TO_NM = 0.0005399568034557235;

my $debug_on = 1;
my $def_dir = 'C:\FG\Atlas\Atlas-0.5.0\build\msvc\Tracks';
#my $def_file = $def_dir.'\atlas-track-01.txt';
my $def_file = $def_dir."\\left-gil-cir-04.txt";
#my $def_file = $def_dir."\\atlas-gil-08.trk.txt";
#my $def_file = $def_dir."\\atlas-gil-01.trk.txt";
### program variables
my @warnings = ();
my $cwd = cwd();
my $os = $^O;
my $gps_first = 1;
my $FG_PI = get_fg_PI();

# features
my $show_dist_calc = 0;
my $chk_checksum = 1;
my $use_pos_latlon = 1;
my $massage_latlon = 1;

my $min_flying_speed = 55; # Knots

# debug
my $dbg_01 = 0;
my $dbg_03 = 0;

# program variables
my $gps_count = 0;
my ($gps_time_rmc,$gps_ok_rmc,$gps_lat_rmc,$gps_long_rmc,$gps_speed,$gps_bearing,$gps_date,$gps_mvar);

my ($first_lon,$first_lat,$first_alt);
my ($last_lat,$last_lon,$last_alt);
my ($g_sg_az1,$g_sg_az2,$g_sg_dist);
my $clast_speed = '';
my $clast_alt = '';
my $clast_bearing = '';

my $g_total_dist = 0;
my $g_curr_heading = 0;

my $gps_min_lat = 400;
my $gps_max_lat = -400;
my $gps_min_lon = 400;
my $gps_max_lon = -400;

my ($g_time_gga,$g_lat_gga,$g_lon_gga,$g_fixqual,$g_nsat,$g_hdop_gga,$g_alt_gga,$g_gheight,$g_DGPS_age,$g_DGPS_ID);
my ($g_nav1_freq,$g_nav1_rad,$g_nav2_freq,$g_nav2_rad,$g_adf_freq);

my $tot_secs = 0;
my $gga_count = 0;
my @GPRMC = ();
my @GPGGA = ();

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

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 deg_to_rad($) {
    my $deg = shift;
    return ($deg * $FG_PI / 180);
}

sub get_lat_lon_at_dist_on_heading($$$$) {
    my ($lat,$lon,$dist,$heading) = @_;
    # // The simpliest lat/lon to distance formula
    # assumes a perfect sphere, of
    my $r = 365239.5; # feet!
    my $rad = deg_to_rad($heading);
    my $a = cos($rad) * $dist;
    my $o = sin($rad) * $dist;
    my $dlat = $a / $r;
    my $dlon = $o / cos(deg_to_rad($lat + $dlat)) / $r;
    my $nlat = ($lat + $dlat);
    my $nlon = ($lon + $dlon);
    return $nlat,$nlon;
}

# @_ = (3352.3955,S) == -33 52.3955 == -33.8732583333333
sub get_latitude {
	my ($deg, $min) = unpack "a2a*", $_[0];
	my $lat = $deg + $min / 60;
	$lat = - $lat if $_[1] =~ /[Ss]/;
	return $lat;
}

# @_ = (15113.3678,E) == +151 13.6678 == 151.222796666667
sub get_longitude {
	my ($deg, $min) = unpack "a3a*", $_[0];
	my $long = $deg + $min / 60;
	$long = - $long if $_[1] =~ /[Ww]/;
	return $long;
}

sub get_message_checksum($) {
    my ($line) = shift;
    my @arr = split(//, $line);
    my $len = length $line;
    my $cnt = scalar @arr;
    #prt( "len $len, count $cnt, ");
    my $chksum = 0;
    my ($let,$val,$ins,$i,$rem,$hex,$ok);
    $ins = 0;
    $rem = '';
    for ($i = 0; $i < $cnt; $i++) {
        $let = $arr[$i];
        $val = ord($let);
        if ($ins) {
            if ($let eq '*') {
                $ins = 0;
                $i++;
                for ( ;$i < $cnt; $i++) {
                    $rem .= $arr[$i];
                }
            } else {
                $chksum ^= $val;
            }
        } else {
            if ($let eq '$') {
                $ins = 1;
            }
        }
    }
    $hex = sprintf("%02X", $chksum);
    $ok = (($rem eq $hex) ? "ok" : "FAILED");
    prt( "[01] checksum = [$hex] ($rem) $ok\n" ) if ($dbg_01);
    return $hex;
}

sub get_gps_info_example_not_used($) {
    my ($rfield) = @_;
    # Now process the new RMC record - example
    ### $GPRMC,130210.030,A,3352.3955,S,15113.3678,E,0.257256,125.59,260605,,*10
    $gps_time_rmc = join ':', unpack "a2" x 3, ${$rfield}[1];
	$gps_ok_rmc = ${$rfield}[2];
	$gps_lat_rmc = get_latitude(${$rfield}[3..4]);
	$gps_long_rmc = get_longitude(${$rfield}[5..6]);
	$gps_speed = ${$rfield}[7];
	$gps_bearing = ${$rfield}[8];
	$gps_date = join '/', unpack "a2" x 3, ${$rfield}[9];
	$gps_mvar = ${$rfield}[10] . ${$rfield}[11];
	# field[12] is checksum
}

# from : http://www.codepedia.com/1/The+GPRMC+Sentence
# The GPRMC sentence consists of twelve comma-delimited words.
# but seems to miss mentioning the magnetic variation
# eg - $GPRMC,012255,A,3139.338,S,14837.161,E,115.3,341.6,1702111,0.000,E*56
# 0 - The Command Word '$GPRMC'
# 1 - Satellite-Derived Time - current time, in UTC, in a compressed form "HHMMSS.XXX,"
#     where HH represents hours, MM represents minutes, SS represents seconds, and XXX represents milliseconds
# 2 - Satellite Fix Status - 'A' = Active, 'V' = inValid
# 3 - Latitude Decimal Degrees - format "HHMM.M" where HH represents hours and MM.M represents minutes
# 4 - Latitude Hemisphere - 'N' = North, 'S' = South
# 5 - Longitude Decimal Degrees - format "HHHMM.M" where HHH represents hours and MM.M represents minutes
# 6 - Longitude Hemisphere - 'E' = East, 'W' = West
# 7 - Speed - rate over land, in Knots
# 8 - Bearing - azimuth, in degrees between 0 and 360
# 9 - UTC Date - 2 two-digit numbers for days, months plus year (2 or 3 digits)
# 10-11 - 0.000,E - Magnetic Variation (missed from spec?) - BUT FG uses a dummy '0.000,E' WHY?
# 12 - 2 digit hex after '*' - The Checksum - XOR of everything between '$' and '*', as a 2 digit HEX value
sub process_GPRMC_msg {
	my ($line) = @_;
	my @field = split /[,*]/, $line; # split on comma, and * (checksum)
    my $tcnt = scalar @field;
    my $secs = 0;
	# recommended minimum specific GPS/Transit data
	if (($tcnt >= 12) && ($field[0] eq '$GPRMC')) {
		#  Each time we see this
		# sentence, print out the accumulated information
		# from the previous burst.
		#gps_output() unless ($gps_first) ;
		$gps_first = 0;

		# Now process the new RMC record - example
        #    0     1          2 3  ...    4 5 ...      6 7        8      9      10/11 12
        ### $GPRMC,130210.030,A,3352.3955,S,15113.3678,E,0.257256,125.59,260605, ,    *10
		$gps_time_rmc = join ':', unpack "a2" x 3, $field[1]; #// HHMMSS utc
		$gps_ok_rmc = $field[2];
		$gps_lat_rmc = get_latitude(@field[3..4]);
		$gps_long_rmc = get_longitude(@field[5..6]);
		$gps_speed = $field[7]; # ground speed, in Knots
		$gps_bearing = $field[8];
		$gps_date = join '/', unpack "a2" x 3, $field[9]; #// DDMMYYY (YYY = years since 1900)
		$gps_mvar = $field[10] . $field[11];
        $secs = get_time_in_seconds($gps_time_rmc);
        #              0=secs 1=lat         2=lon          3=speed     4=bearing
        push(@GPRMC, [ $secs, $gps_lat_rmc, $gps_long_rmc, $gps_speed, $gps_bearing ]);
		# field[12] is checksum
		###last SWITCH;
        if ($use_pos_latlon) {
            my $latpos = ($gps_lat_rmc + 90);
            my $lonpos = ($gps_long_rmc + 180);
            $gps_min_lat = $latpos if ($latpos < $gps_min_lat);
            $gps_max_lat = $latpos if ($latpos > $gps_max_lat);
            $gps_min_lon = $lonpos if ($lonpos < $gps_min_lon);
            $gps_max_lon = $lonpos if ($lonpos > $gps_max_lon);
        } else {
            $gps_min_lat = $gps_lat_rmc if ($gps_lat_rmc < $gps_min_lat);
            $gps_max_lat = $gps_lat_rmc if ($gps_lat_rmc > $gps_max_lat);
            $gps_min_lon = $gps_long_rmc if ($gps_long_rmc < $gps_min_lon);
            $gps_max_lon = $gps_long_rmc if ($gps_long_rmc > $gps_max_lon);
        }
        if ($chk_checksum) {
            my $cs = get_message_checksum($line);
            if ($cs ne $field[12]) {
                prtw("WARNING: GPGGA message [$line] BAD checksum [$cs] vs [".$field[12]."]!\n");
            }
        }
        return 1;
	} else {
        prtw("WARNING: GPGGA has field count of $tcnt!\n");
        pgm_exit(1,"");
    }
    return 0;
}

# 0      1      2        3 4         5 6 7 8 9   10 11 12 13 14  15
# $GPGGA,012255,3139.338,S,14837.161,E,1, , ,1862,F,  ,  ,  ,   *04
# from : http://aprs.gids.nl/nmea/#gga
# $GPGGA
# Global Positioning System Fix Data 
# Off   Name                 Example      Data  Description  
# 0 -   Sentence Identifier  $GPGGA       Global Positioning System Fix Data 
# 1 -   Time                 170834       17:08:34 Z 
# 2-3   Latitude             4124.8963,N  41d 24.8963' N or 41d 24' 54" N 
# 4-5   Longitude            08151.6838,W 81d 51.6838' W or 81d 51' 41" W 
# 6 -   Fix Quality:         1            0=Invalid - 1=GPS fix - 2=DGPS fix 1 Data is from a GPS fix 
# 7 -   Number of Satellites 05           5 Satellites are in view 
# 8 -   Horizontal Dilution of Precision (HDOP) 1.5 Relative accuracy of horizontal position 
# 9-10  Altitude             280.2,M      280.2 meters above mean sea level 
# 11-12 Height of geoid above WGS84 ellipsoid -34.0,M -34.0 meters 
# 13 -  Time since last DGPS update blank No last update 
# 14 -  DGPS reference station id blank No station id 
# 15 -  Checksum *75 Used by program to check for transmission errors 
sub process_GPGGA_msg {
	my ($line) = @_;
	my @field = split /[,*]/, $line; # split on comma, and * (checksum)
    my $len = scalar @field;
    my $secs = 0;
    # GPS fix data
    if (($len > 15) && ($field[0] eq '$GPGGA')) {
        $g_time_gga = join ':', unpack "a2" x 3, $field[1];
        $g_lat_gga = get_latitude(@field[2..3]); # 	    // Latitude // DDMM.MMM, 'N' or 'S'
        $g_lon_gga = get_longitude(@field[4..5]);
        $g_fixqual = $field[6];
        $g_nsat = $field[7];
        $g_hdop_gga = $field[8];
        $g_alt_gga = $field[9]; # // Altitude
        if ($g_alt_gga <= -9999) {
            # invalid altitude - leave as is
        } else {
            $g_alt_gga *= $SG_METER_TO_FEET;
	    }
	    # char *units = tokens[10];	// 'F' or 'M' #// If units are metres, convert them to feet.
	    #if (strcmp(units, "M") == 0) { #d->alt *= SG_METER_TO_FEET;   #}
        # $field[10] is altitude units (M or F)
        $g_gheight = $field[11];
        # $field[12] is geoid height units (M or F)
        $g_DGPS_age = $field[13];
        $g_DGPS_ID = $field[14];
        # field[15] is checksum;
        $secs = get_time_in_seconds($g_time_gga);
        #              0=secs 1=lat       2=lon       3=alt       4=geod alt
        push(@GPGGA, [ $secs, $g_lat_gga, $g_lon_gga. $g_alt_gga, $g_gheight ]);
        if ($chk_checksum) {
            my $cs = get_message_checksum($line);
            if ($cs ne $field[15]) {
                prtw("WARNING: GPGGA message [$line] BAD checksum [$cs] vs [".$field[15]."]!\n");
            }
        }
        return 1;
    } else {
        prtw("WARNING: Count = $len - NOT GPGGA sentence [$line]\n");
    }
    return 0;
}

# $PATLA,115.80,280.0,116.80,29.0,251*60
sub process_PATLA_msg {
	my ($line) = @_;
	my @field = split /[,*]/, $line; # split on comma, and * (checksum)
    my $tcnt = scalar @field;
	# recommended minimum specific GPS/Transit data
	if (($tcnt >= 7) && ($field[0] eq '$PATLA')) {  # (tokenCount == 7))
	    #// NAV1, NAV2 and ADF
	    $g_nav1_freq = $field[1];
        $g_nav1_rad  = $field[2];
	    $g_nav2_freq = $field[3];
        $g_nav2_rad  = $field[4];
        $g_adf_freq  = $field[5];
	    #// VOR frequencies are transmitted in the PATLA line as
	    #// floats (eg, 112.30), but we store them as ints (112300).
	    $g_nav1_freq = int($g_nav1_freq * 1000);
	    $g_nav2_freq = int($g_nav2_freq * 1000);
        if ($chk_checksum) {
            my $cs = get_message_checksum($line);
            if ($cs ne $field[6]) {
                prtw("WARNING: PATLA message [$line] BAD checksum [$cs] vs [".$field[6]."]!\n");
            }
        }
        return 1;
    } else {
        prtw("WARNING: PATLA message [$line] count is $tcnt, NOT 7!\n");
    }
    return 0;
}

sub bark_if_RMC_ne_GGA()
{
    my $diff1 = abs($gps_lat_rmc - $g_lat_gga);
    if ($diff1 > $SG_EPSILON) {
        prtw("WARNING: RMC $gps_lat_rmc NE GGA $g_lat_gga ($diff1)\n");
    }
    my $diff2 = abs($gps_long_rmc - $g_lon_gga);
    if ($diff2 > $SG_EPSILON) {
        prtw("WARNING: RMC $gps_long_rmc NE GGA $g_lon_gga ($diff2)\n");
    }
}


sub get_time_in_seconds($) {
    my ($txt) = @_;
    my @arr = split(/:/,$txt);
    if (scalar @arr == 3) {
        my $secs = $arr[2];
        $secs += $arr[1] * 60;
        $secs += $arr[0] * 60 * 60;
        return $secs;
    }
    return 0;
}

sub get_hhmmss($) {
    my ($secs) = @_;
    my $mins = int($secs / 60);
    $secs = $secs - ($mins * 60);
    my $hrs = int($mins / 60);
    $mins = $mins - ($hrs * 60);
    $hrs = "0$hrs" if ($hrs < 10);
    $mins = "0$mins" if ($mins < 10);
    $secs = "0$secs" if ($secs < 10);
    return "$hrs:$mins:$secs";
}

sub get_decimal_stg($$$) {
    my ($dec,$il,$dl) = @_;
    my (@arr);
    if ($dec =~ /\./) {
        @arr = split(/\./,$dec);
        if (scalar @arr == 2) {
            $arr[0] = " ".$arr[0] while (length($arr[0]) < $il);
            $dec = $arr[0];
            if ($dl > 0) {
                $dec .= ".";
                $arr[1] = substr($arr[1],0,$dl) if (length($arr[1]) > $dl);
                $dec .= $arr[1];
            }
        }
    } else {
        $dec = " $dec" while (length($dec) < $il);
        if ($dl) {
            $dec .= ".";
            while ($dl--) {
                $dec .= "0";
            }
        }
    }
    return $dec;
}


sub get_heading_stg($) {
    my ($hdg) = @_;
    #return get_decimal_stg($hdg,3,1);
    return get_decimal_stg($hdg,3,0).'D';
}

sub get_sg_dist_stg($) {
    my ($sg_dist) = @_;
    my $sg_dlen = 5;
    my $sg_km = $sg_dist / 1000;
    my $sg_im = int($sg_dist);
    my $sg_ikm = int($sg_km + 0.5);
    # if (abs($sg_pdist) < $CP_EPSILON)
    my $sg_dist_stg = "";
    if (abs($sg_km) > $SG_EPSILON) { # = 0.0000001; # EQUALS SG_EPSILON 20101121
        if ($sg_ikm && ($sg_km >= 1)) {
            $sg_km = int(($sg_km * 10) + 0.05) / 10;
            $sg_dist_stg .= get_decimal_stg($sg_km,$sg_dlen - 2,1)." km";
        } else {
            #$sg_dist_stg .= "$sg_im m, <1km";
            $sg_dist_stg .= get_decimal_stg($sg_im,$sg_dlen,0)." m.";
        }
    } else {
        #$sg_dist_stg .= "0 m";
        $sg_dist_stg .= get_decimal_stg('0',$sg_dlen,0)." m.";
    }
    return $sg_dist_stg;
}

sub get_dist_stg($$) {
    my ($dlat,$dlon) = @_;
    my ($sg_az1,$sg_az2,$sg_dist,$res);
    $res = fg_geo_inverse_wgs_84 ($first_lat,$first_lon,$dlat,$dlon,\$g_sg_az1,\$g_sg_az2,\$g_sg_dist);
    $res = fg_geo_inverse_wgs_84 ($last_lat,$last_lon,$dlat,$dlon,\$sg_az1,\$sg_az2,\$sg_dist);
    $g_total_dist += $sg_dist;
    $g_sg_az1 = int(($g_sg_az1 * 10) + 0.05) / 10;
    $g_sg_az2 = $g_sg_az1 + 180;
    $g_sg_az2 -= 360 if ($g_sg_az2 >= 360);
    $sg_az1 = int(($sg_az1 * 10) + 0.05) / 10;
    $g_curr_heading = $sg_az1; # only keep to first decimal place
    return "hm: ".get_heading_stg($g_sg_az2)." at ".get_sg_dist_stg($g_sg_dist);
}

sub get_last_dist_stg($$) {
    my ($dlat,$dlon) = @_;
    my ($sg_az1,$sg_az2,$sg_dist);
    my $res = fg_geo_inverse_wgs_84 ($first_lat,$first_lon,$dlat,$dlon,\$g_sg_az1,\$g_sg_az2,\$g_sg_dist);
    $res = fg_geo_inverse_wgs_84 ($last_lat,$last_lon,$dlat,$dlon,\$sg_az1,\$sg_az2,\$sg_dist);
    $g_total_dist += $sg_dist;
    $g_sg_az1 = int(($g_sg_az1 * 10) + 0.05) / 10;
    $g_sg_az2 = $g_sg_az1 + 180;
    $g_sg_az2 -= 360 if ($g_sg_az2 >= 360);
    # $g_sg_az2 = int(($g_sg_az2 * 10) + 0.05) / 10;
    my $sg_km = $sg_dist / 1000;
    my $sg_im = int($sg_dist);
    my $sg_ikm = int($sg_km + 0.5);
    # if (abs($sg_pdist) < $CP_EPSILON)
    my $dist_hdg = " ";
    #my $dist_hdg = " (SGD: ";
    $sg_az1 = int(($sg_az1 * 10) + 0.05) / 10;
    $g_curr_heading = $sg_az1; # only keep to first decimal place
    if (abs($sg_km) > $SG_EPSILON) { # = 0.0000001; # EQUALS SG_EPSILON 20101121
        if ($sg_ikm && ($sg_km >= 1)) {
            $sg_km = int(($sg_km * 10) + 0.05) / 10;
            $dist_hdg .= "$sg_km km";
        } else {
            $dist_hdg .= "$sg_im m, <1km";
        }
    } else {
        $dist_hdg .= "0 m";
    }
    $dist_hdg .= " on $sg_az1";
    # $dist_hdg .= ")";
    return $dist_hdg;
}


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,$clat,$clon);
    my ($len,$last_sp,$flag,$ccnt,$last_sec,$first_sec,$ctime,$secs,$diff,$dmsg,$hmsg,$ok,$calt);
    my ($cradm,$last_radm,$spdm,$flying);
    $lnn = 0;
    $ok = 0;
    $last_radm = '';
    $flying = 0;
    if ($show_dist_calc) {
        prt("Line#   Time     Latitude     Longitude     Alt    Speed  Hdg  Calculated heading, distance...\n");
    } else {
        prt("Line#   Time     Latitude     Longitude     Alt    Speed  Bearing...\n");
    }
    foreach $line (@lines) {
        chomp $line;
        $lnn++;
        if ($line =~ /^\$GPRMC,(.+)\*(.+)$/) {
            $inc = $1;
            if ( process_GPRMC_msg($line) ) {
                #prt("$lnn: $inc\n");
                $clat = sprintf("%3.8f",$gps_lat_rmc);
                $clon = sprintf("%4.8f",$gps_long_rmc);
                $ctime = $gps_time_rmc;
                $secs = get_time_in_seconds($gps_time_rmc);
                $flag = 0;
                if ($gps_count) {
                    if (abs($last_lat - $gps_lat_rmc) < $SG_EPSILON) {
                        $len = length($clat);
                        $clat = ' ' x $len;
                        $flag |= 1;
                    }
                    if (abs($last_lon - $gps_long_rmc) < $SG_EPSILON) {
                        $len = length($clon);
                        $clon = ' ' x $len;
                        $flag |= 2;
                    }
                    if ($last_sec == $secs) {
                        $len = length($ctime);
                        $ctime = ' ' x $len;
                    } else {
                        $diff = abs($secs - $last_sec);
                        $tot_secs += $diff if ($diff < 10);
                    }
                } else {
                    $first_lat = $gps_lat_rmc;
                    $first_lon = $gps_long_rmc;
                    $first_sec = $secs;
                    $last_lat = $gps_lat_rmc;
                    $last_lon = $gps_long_rmc;
                }
                $gps_count++;
                $ccnt = sprintf("%6d",$gps_count);
                $dmsg = get_dist_stg($gps_lat_rmc,$gps_long_rmc);
                #$hmsg = get_heading_stg($g_curr_heading);
                $hmsg = get_heading_stg($gps_bearing);
                #prt("$ccnt: $ctime $clat $clon $gps_speed $hmsg $dmsg (line $lnn)\n") if ($flag != 3);
                $ok = 1 if ($flag != 3);
                $last_lat = $gps_lat_rmc;
                $last_lon = $gps_long_rmc;
                $last_sp  = $gps_speed;
                $last_sec = $secs;
                $flying = 1 if ($gps_speed >= $min_flying_speed);
                $spdm = int($gps_speed + 0.5);
                $spdm = " $spdm" while (length($spdm) < 3);
                $spdm .= "kts";
            }
        } elsif ($line =~ /^\$GPGGA,(.+)$/) {
            if ( process_GPGGA_msg($line) ) {
                $ok |= 2;
                $gga_count++;
                bark_if_RMC_ne_GGA() if ($ok & 1);
                if ($g_alt_gga < 0) {
                    if ($g_alt_gga <= -9999) {
                        $calt = "n/a";
                    } else {
                        $calt = int($g_alt_gga - 0.5);
                    }
                } else {
                    $calt = int($g_alt_gga + 0.5);
                }
                $calt = " $calt" while (length($calt) < 5);
                $calt .= "ft";
            }
        } elsif ($line =~ /^\$PATLA,(.+)$/) {
            if ( process_PATLA_msg($line) ) {
                $cradm = "$g_nav1_freq,$g_nav1_rad,$g_nav2_freq,$g_nav2_rad,$g_adf_freq";
                if ($cradm ne $last_radm) {
                    $last_radm = $cradm;
                } else {
                    $cradm = '';
                }
                $ok |= 4;
            }
        }
        if ($ok == 7) {
            #prt("$ccnt: $ctime $clat $clon $calt $gps_speed $hmsg $dmsg $cradm (line $lnn)\n");
            if ($clast_speed eq $spdm) {
                $len = length($spdm);
                $spdm = ' ' x $len;
            } else {
                $clast_speed = $spdm;
            }
            if ($clast_alt eq $calt) {
                $len = length($calt);
                $calt = ' ' x $len;
            } else {
                $clast_alt = $calt;
            }
            if ($clast_bearing eq $hmsg) {
                $len = length($hmsg);
                $hmsg = ' ' x $len;
            } else {
                $clast_bearing = $hmsg;
            }
            if ($dbg_03) {
                if ($show_dist_calc) {
                    prt("$ccnt: $ctime $clat $clon $calt $spdm $hmsg $dmsg $cradm (line $lnn)\n");
                } else {
                    prt("$ccnt: $ctime $clat $clon $calt $spdm $hmsg\n");
                }
            }
            $ok = 0;
        }
    }
    $ccnt = sprintf("%6d",$gps_count);
    $ctime = get_hhmmss($tot_secs);
    if ($use_pos_latlon) {
        $gps_min_lat -= 90;
        $gps_min_lon -= 180;
        $gps_max_lat -= 90;
        $gps_max_lon -= 180;
    }

    # massage the lengths for display
    if ($massage_latlon) {
        $gps_min_lat = sprintf("%3.8f",$gps_min_lat);
        $gps_min_lon = sprintf("%4.8f",$gps_min_lon);
        $gps_max_lat = sprintf("%3.8f",$gps_max_lat);
        $gps_max_lon = sprintf("%4.8f",$gps_max_lon);
    }

    prt("$ccnt: $ctime lat $gps_min_lat to $gps_max_lat, lon $gps_min_lon to $gps_max_lon (line $lnn)\n");
    prt("Box $gps_min_lat, $gps_min_lon - Width  ".($gps_max_lon - $gps_min_lon)."\n");
    prt("    $gps_max_lat, $gps_max_lon - Height ".($gps_max_lat - $gps_min_lat)."\n");
}

sub process_message_arrays($$) {
    my ($rgmc,$rggp) = @_;
    my $cntgmc = scalar @{$rgmc};
    my $cntggp = scalar @{$rggp};
    prt("Got $cntgmc GPGMC, and $cntggp GPGGP messages\n");
    if ($cntgmc == $cntggp) {
        my ($i,$spd_kt,$okspd,$bgni,$endi,$secs,$bgns,$ends);
        #                0=secs 1=lat         2=lon          3=speed     4=bearing
        # push(@GPRMC, [ $secs, $gps_lat_rmc, $gps_long_rmc, $gps_speed, $gps_bearing ]);
        #                0=secs 1=lat       2=lon       3=alt       4=geod alt
        # push(@GPGGA, [ $secs, $g_lat_gga, $g_lon_gga. $g_alt_gga, $g_gheight ]);
        $bgns = 0;
        $ends = 0;
        for ($i = 0; $i < $cntgmc; $i++) {
            $spd_kt = ${$rgmc}[$i][3];
            $secs   = ${$rgmc}[$i][0];
            if ($spd_kt >= $min_flying_speed) {
                $bgns = $secs;
                last;
            }
        }
        prt("Bgn: Discarded $i - speed below $min_flying_speed...\n");
        $okspd = 0;
        $bgni = $i;
        for (; $i < $cntgmc; $i++) {
            $spd_kt = ${$rgmc}[$i][3];
            $secs   = ${$rgmc}[$i][0];
            last if ($spd_kt < $min_flying_speed);
            $okspd++;
            $ends = $secs;
        }
        $endi = $i;
        prt("End: Discarded ".($cntgmc - $i)." - speed below.\n");
        if ($okspd) {
            my ($lat,$lon,$hdg,$clat,$clon,$dtm,$len,$chdg,$cspd);
            my $llat = 0;
            my $llon = 0;
            my $ltm = '';
            my $lhdg = 0;
            my $lspd = 0;
            my $lsec = 0;
            prt("Got $okspd ok records... From $bgns to $ends secs, or ".get_hhmmss($ends - $bgns)." of flight\n");
            for ($i = $bgni; $i < $endi; $i++) {
                $lat    = ${$rgmc}[$i][1];
                $lon    = ${$rgmc}[$i][2];
                $spd_kt = ${$rgmc}[$i][3];
                $hdg    = ${$rgmc}[$i][4];
                $secs   = ${$rgmc}[$i][0];
                $clat = sprintf("%3.8f",$lat);
                $clon = sprintf("%4.8f",$lon);
                $dtm = get_hhmmss($secs - $bgns);

                $spd_kt = int($spd_kt + 0.5);
                $hdg = int($hdg + 0.5);
                $chdg = sprintf("%03d", $hdg);
                $cspd = sprintf("%03d",$spd_kt);
                if ($lat == $llat) {
                    $len = length($clat);
                    $clat = ' ' x $len;
                }
                if ($lon == $llon) {
                    $len = length($clon);
                    $clon = ' ' x $len;
                }
                if ($lsec == $secs) {
                    $len = length($dtm);
                    $dtm = ' ' x $len;
                }
                if ($hdg == $lhdg) {
                    $len = length($chdg);
                    $chdg = ' ' x $len;
                }
                if ($spd_kt == $lspd) {
                    $len = length($cspd);
                    $cspd = ' ' x $len;
                }
                prt("$dtm: $clat,$clon $cspd $chdg\n");
                $llat = $lat;
                $llon = $lon;
                $lsec = $secs;
                $lspd = $spd_kt;
                $lhdg = $hdg;
            }
        }

    } else {
        prtw("WARNING: Message counts NOT equal!\n");
    }
}

#########################################
### MAIN ###
parse_args(@ARGV);
### prt( "$pgmname: in [$cwd]: Hello, World...\n" );
process_in_file($in_file);
process_message_arrays(\@GPRMC,\@GPGGA);
pgm_exit(0,"Normal exit(0)");
########################################
sub give_help {
    prt("$pgmname: version 0.0.1 2010-09-11\n");
    prt("Usage: $pgmname [options] in-file\n");
    prt("Options:\n");
    prt(" --help (-h or -?) = This help, and exit 0.\n");
}
sub need_arg {
    my ($arg,@av) = @_;
    pgm_exit(1,"ERROR: [$arg] must have following argument!\n") if (!@av);
}

sub parse_args {
    my (@av) = @_;
    my ($arg,$sarg);
    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)");
            } else {
                pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n");
            }
        } else {
            $in_file = $arg;
            prt("Set input to [$in_file]\n");
        }
        shift @av;
    }

    if ((length($in_file) ==  0) && $debug_on) {
        $in_file = $def_file;
    }
    if (length($in_file) ==  0) {
        pgm_exit(1,"ERROR: No input files found in command!\n");
    }
    if (! -f $in_file) {
        pgm_exit(1,"ERROR: Unable to find in file [$in_file]! Check name, location...\n");
    }
}

# eof - template.pl
