#!/usr/bin/perl -w

# NMEA parser
# Waider / September 7, 2000

# Need a map?
# http://www.mapquest.com/cgi-bin/ia_find?link=btwn/twn-map_latlong_degrees_form
# grab all the input fields, especially the hidden ones.
# set latdeg latmin latsec lngdeg lngmin lngsec appropriately
# set "Find Map" to "Get Map"
# call ia_find
# look for http://sitemap.mapquest.com/mqmapgend?MQMapGenRequest,
#   that's your map.
# 65th character in URL seems to control the magnification:
# *->9->1->l->q->d->g->0->5->h
# cont.    region   city     street


# You can also try:
#
#http://www.mapblast.com/gif?
#
# Location of Icon
# Lat Long Icon
# Icon:
# 1 - circle with dot
# 2 - circle with dot, nontransparent
# 3 - small red circle
# 4 - big red circle
# 5 - cross
#&IC=53.1913:-6.0801:8:
#
# Centre of map
# Lat Long Scale
# Smaller number = closer zoom. seems to zoom up pretty well.
#&CT=53.1913:-6.0801:200000
#
# @ scale = 5000, ( 0.0020 N =~ 130 pixels, 0.0060 W =~ 228 pixels )
# Width/Height of map
#&W=456
#&H=259
#
# Required
# &FAM=mymapblast

while (<>) {
  next if !s/^\$//; # discard header
  next if !s/\r\n$//; # discard trailer

  # do the checksum thing
  if ( s/\*([0-9A-F][0-9A-F])$// ) {
	my ( $csum ) = eval( "0x$1" );
	for my $c ( split( // )) {
	  $csum ^= ord( $c );
	}

	if ( $csum ) {
	  next; # invalid checksum
	}
  }

  # Break it up into fields
  my @fields = split( /,/ );

  # Parse type of data
  shift @fields; # discard source/command

  # Woop. Check for proprietary sentence:
  if ( m/^P(...)(.*?),/ ) {
	if ( $1 eq 'GRM' ) { # GARMIN PROPRIETARY
	GARMIN:
	  {
		# E - estimated error
		if ( $2 eq 'E' ) {
		  if ( $fields[ 0 ] =~ /[0-9.]/) { # verify that we have data
			print "Estimated error: ";
			printf( "HPE: %f %s VPE: %f %s Spherical: %f %s\n", @fields);
		  }
		}
		# Z - altitude. Always in feet.
		if ( $2 eq 'Z' ) {
		  if ( $fields[ 0 ] =~ /[0-9.]/) {
			printf( "Altitude (%s): %d %s\n",
					$fields[ -1 ] == 2 ? "user" : "GPS",
					$fields[ 0 ], $fields[ 1 ] );
		  }
		}
		last GARMIN;
	  }
	} else {
	  # don't know what to do! AIE!
	  print "command $2 from $1\n";
	}
  } else {
	s/^(..)(.+?),//;
	my ( $source, $datatype ) = ( $1, $2 );

#	print "command $datatype from ";
	if ( $source eq 'GP' ) {
#	  print "GPS receiver\n";
	} elsif ( $source eq 'LC' ) {
#	  print "Loran-C receiver\n";
	} elsif (  $source eq 'OM' ) {
#	  print "Omega Navigation receiver\n";
	} elsif ($source eq 'II' ) {
#	  print "Integrated Instrumentation\n";
	} else {
	  print "$source ???\n";
	}

  COMMAND:
	{
	  # BOD Bearing, Origin to Destination
	  # GGA GPS Fix Data
	  if ( $datatype eq 'GGA' ) {
		print "--\n";
		my ( $time, $lat, $latd, $long, $longd, $qual, $nsat, $hdil,
			 $alt, $altu, $geo, $geou, $lastdgps, $dgpsid, @leftovers ) =
			   @fields;

		# Is this good data?
		if ( $qual && $#fields == 11 ) { # field count drops sometimes.
		  printf( "%02d:%02d:%02d ", substr( $time, 0, 2),
				  substr( $time, 2, 2 ), substr( $time, 4, 2 ));

		  printf( "%s%02d.%02d%02d %s%03d.%02d%02d ",
				  $latd eq 'N' ? " " : "-", # N/S indicator
				  substr( $lat, 0, 2 ),
				  substr( $lat, 2, 2 ) * 100/60,
				  substr( $lat, 4 ) * 100/60,
				  $latd eq 'W' ? " " : "-", # E/W indicator
				  substr( $long, 0, 3 ),
				  substr( $long, 3, 2 ) * 100/60,
				  substr( $long, 5 ) * 100/60
				);

		  printf( "alt: %s%s (WGS84 + %s%s)) ", $alt, $altu, $geo, $geou );

		  printf( "(%d sats) ", $nsat );
		  print "\n";
		} else {
		  print "Something up: F $#fields\n" if $#fields != 11;
		}
	  } elsif ( $datatype eq 'GLL' ) {
		print "Geographic Position, Latitude and Longitude\n";
		# N/S val, N/S, E/W val, E/W, A => valid
	  } elsif ( $datatype eq 'GSA' ) {
		print "GPS DOP and active satellites\n";
		# A/M - auto/manual
		# 2/3 - 2D/3D fix
		# 12 spaces for satellite PRNs
		# PDOP (dilution of precision)
		# HDOP
		# VDOP
	  } elsif ( $datatype eq 'GSV' ) {
		print "Satellites in view\n";
		# Number of sentences for full data
		# Sentence N of the above
		# Number of sats in view
		# Sat PRN
		# Elev
		# Azimuth
		# Signal strength
		# Repate for up to 4 sats per sentence, 3 GSV sentences per packet
	  } elsif ( $datatype eq 'RMB' ) {
		print "Recommended Minimum Navigation Information\n";
		# A/V okay/warning
		# cross track error, nautical miles
		# directon to steer
		# origin waypoint ID
		# destination waypoint ID
		# dest lat   DDMM.MM,N/S
		# dest long  DDMM.MM E/W
		# Range to dest, nautical
		# true bearing to dest
		# velocity towards dest
		# A/V arrival alarm
	  } elsif ( $datatype eq 'RMC' ) {
		print "Recommended minimum specific GPS/Transit data\n";
		# time HHMMSS UTC
		# A/V
		# LAT N/S
		# LONG E/W
		# Speed, Knots
		# Course Made Good, True
		# Date of fix DDMMYY
		# Magnetic Variation dist, dir
	  } elsif ( $datatype eq 'RTE' ) {
		print "Waypoints in active route\n";
		# Sentences of data
		# sentence num
		# c omplete, w first listed start of current leg
		# route identifier
		# Waypoint IDs
	  } elsif ( $datatype eq 'BOD' ) {
		print "Origin to destination bearing\n";
		# Bearing, T (true) from STart to Dest
		# Bearing, M (magnetic)
		# Dest
		# Start
	  } else {
		print "ERROR! Unhandled data $datatype\n";
	  }
	}
  }
}
