#!/usr/bin/perl

#  nmea.perl
# Convert generic NMEA output into something more spreadsheet-friendly.
# Understands some Garmin-specific sentences.
#
# Note: we depend on the $GPRMC sentence appearing once in every
# "data burst" from the GPS, preferably as the first sentence.
# If this is not true for a particular GPS, the code that calls
# output() must be moved to a more appropriate place.
#
# Dave Martindale, May 2000
#

$maxtrack = 12;
$dbgon = 1; # 0 for runtime

if ($dbgon == 1) {
$infile = 'gps01.txt';
$outfile = 'tempgps1.csv';
} else {
$infile = shift or die "No input file given ...\n";
$outfile = shift or die "No output file given ...\n";
}

###$infile = "<-";
###$outfile = ">-";
###open (INFILE, $infile) or die "couldn't open input file $infile\n";
###open (OUTFILE, $outfile) or die "couldn't open output file $outfile\n";
open (INFILE, "<$infile") or die "couldn't open input file $infile\n";
open (OUTFILE, ">$outfile") or die "couldn't open output file $outfile\n";

print OUTFILE "GPRMC,,,,,,,,";
print OUTFILE "GPGGA,,,,,,,,,,";
print OUTFILE "GPGSA,,,,,,,,,,,,,,,,,";
print OUTFILE "GPGSV,", "," x (4 * $maxtrack);
print OUTFILE "GPVTG,,,,";
print OUTFILE "GPGLL,,,,";
print OUTFILE "PGRME,,,";
print OUTFILE "PGRMZ,,";
print OUTFILE "PGRMM";
print OUTFILE "\n";

print OUTFILE "Time,Valid,Lat,Long,SOG,CMG,Date,MVar,";		# GPRMC
print OUTFILE "Time,Lat,Long,Qual,Sats,HDOP,Alt,GeoH,DGPSAge,DGPSID,";	# GPGGA
print OUTFILE "Auto,Dim,PRN,,,,,,,,,,,,PDOP,HDOP,VDOP,";	# GPGSA
print OUTFILE "View,";						# GPGSV
for ($sat = 1; $sat <= $maxtrack; $sat++) {
	print OUTFILE "PRN ", $sat, ",Elev,Azim,S/N,";
}
print OUTFILE "CRS (T),CRS (M),SOG (Kt),SOG (km/h),";		# GPVTG
print OUTFILE "Lat,Long,Time,Valid,";				# GPGLL
print OUTFILE "HPE,VPE,SPE,";					# PGRME
print OUTFILE "Alt,Dim,";					# PGRMZ
print OUTFILE "Datum";						# PGRMM
print OUTFILE "\n";

sub output {
	print OUTFILE $time_rmc, ',', $ok_rmc, ',', $lat_rmc, ',',
		$long_rmc, ',', $speed, ',', $cmg, ',', $date, ',',
		$mvar, ',';
	print OUTFILE $time_gga, ',', $lat_gga, ',', $long_gga, ',',
		$fixqual, ',', $nsat, ',', $hdop_gga, ',', $alt_gga, ',',
		$gheight, ',', $DGPS_age, ',', $DGPS_ID, ',';
	print OUTFILE $fixsel, ',', $fixdeg, ',',
		join(',', @satsused), ',', $pdop, ',', $hdop, ',',
		$vdop, ',';
	print OUTFILE $nsiv, ',';
	for ($sat = 0; $sat < $maxtrack; $sat++) {
		if ($sat < $nsiv) {
			print OUTFILE $prn[$sat], ',', $elev[$sat], ',',
				$azim[$sat], ',', $signal[$sat], ',';
		} else {
			print OUTFILE ",,,,";
		}
	}
	print OUTFILE $crs_t, ',', $crs_m, ',', $sog_kt, ',', $sog_km, ',';
	print OUTFILE $lat_gll, ',', $long_gll, ',', $time_gll, ',',
		$ok_gll, ',';
	print OUTFILE $hpe, ',', $vpe, ',', $epe, ',';
	print OUTFILE $alt_rmz, ',', $alt_type, ',';
	print OUTFILE $datum;
	print OUTFILE "\n";
}

sub latitude {
	my ($deg, $min) = unpack "a2a*", $_[0];
	my $lat = $deg + $min / 60;
	$lat = - $lat if $_[1] =~ /[Ss]/;
	return $lat;
}

sub longitude {
	my ($deg, $min) = unpack "a3a*", $_[0];
	my $long = $deg + $min / 60;
	$long = - $long if $_[1] =~ /[Ww]/;
	return $long;
}

$first = 1;
while ($line = <INFILE>) {
	chomp($line);
	@field = split /[,*]/, $line; # split on comma, and * (checksum)
	SWITCH: {

		# recommended minimum specific GPS/Transit data
		if ($field[0] eq '$GPRMC') {

			# We don't know exactly what sentences to expect
			# from an arbitrary GPS, but we assume that RMC
			# will always be included.  Each time we see this
			# sentence, print out the accumulated information
			# from the previous burst.
			output() unless ($first) ;
			$first = 0;

			# Now process the new RMC record
			$time_rmc = join ':', unpack "a2" x 3, $field[1];
			$ok_rmc = $field[2];
			$lat_rmc = latitude(@field[3..4]);
			$long_rmc = longitude(@field[5..6]);
			$speed = $field[7];
			$cmg = $field[8];
			$date = join '/', unpack "a2" x 3, $field[9];
			$mvar = $field[10] . $field[11];
			# field[12] is checksum
			last SWITCH;
		}
	
		# GPS fix data
		if ($field[0] eq '$GPGGA') {
			$time_gga = join ':', unpack "a2" x 3, $field[1];
			$lat_gga = latitude(@field[2..3]);
			$long_gga = longitude(@field[4..5]);
			$fixqual = $field[6];
			$nsat = $field[7];
			$hdop_gga = $field[8];
			$alt_gga = $field[9];
			# $field[10] is altitude units (always M)
			$gheight = $field[11];
			# $field[12] is geoid height units (always M)
			$DGPS_age = $field[13];
			$DGPS_ID = $field[14];
			# field[15] is checksum;
			last SWITCH;
		}

		# GPS DOP and active satellites
		if ($field[0] eq '$GPGSA') {
			$fixsel = $field[1];	# A for auto selection, M for manual
			$fixdeg = $field[2];	# 2 or 3 for 2D or 3D fix
			@satsused = @field[3..14];
			$pdop = $field[15];
			$hdop = $field[16];
			$vdop = $field[17];
			# field[18] is checksum;
			last SWITCH;
		}

		# satellites in view
		if ($field[0] eq '$GPGSV') {
			# $field[1] is total number of sentences for full data
			# $field[2] is current sentence number
			$nsiv = $field[3];
			#
			# Unpack next 16 fields as PRN, elevation, azimuth, and
			# signal quality for next 4 satellites
			#
			$sat = ($field[2] - 1) * 4;
			$lim = $sat + 4;
			if ($nsiv < $lim) {
				$lim = $nsiv;
			}
			$f = 4;
			while ($sat < $lim) {
				$prn[$sat] = $field[$f++];
				$elev[$sat] = $field[$f++];
				$azim[$sat] = $field[$f++];
				$sig = $field[$f++];
				$sig = '' if $sig == 0;
				$signal[$sat] = $sig;
				$sat++;
			}
			# field[20] is checksum;
			last SWITCH;
		}

		# track/ground speed
		if ($field[0] eq '$GPVTG') {
			$crs_t = $field[1];
			# $field[2] is reference (always T);
			$crs_m = $field[3];
			# $field[4] is reference (always M);
			$sog_kt = $field[5];
			# $field[6] is units (always N);
			$sog_km = $field[7];
			# $field[8] is units (always K);
			# $field[9] is checksum;
			last SWITCH;
		}

		# position error info
		if ($field[0] eq '$PGRME') {
			$hpe = $field[1];
			# $field[2] is units (always M)
			$vpe = $field[3];
			# $field[4] is units (always M)
			$epe = $field[5];
			# $field[6] is units (always M)
			# field[7] is checksum;
			last SWITCH;
		}

		# latitude/longitude
		if ($field[0] eq '$GPGLL') {
			$lat_gll = latitude(@field[1..2]);
			$long_gll = longitude(@field[3..4]);
			$time_gll = join ':', unpack "a2" x 3, $field[5];
			$ok_gll = $field[6];
			# field[7] is checksum;
			last SWITCH;
		}

		# altitude
		if ($field[0] eq '$PGRMZ') {
			$alt_rmz = $field[1];
			# $field[2] is units (always F)
			$alt_type = $field[3]; # 2: user alt, 3: 3D fix
			# field[4] is checksum;
			last SWITCH;
		}

		# datum
		if ($field[0] eq '$PGRMM') {
			$datum = $field[1];
			# field[2] is checksum;
			last SWITCH;
		}
	}
}

close INFILE;
close OUTFILE;

if ($dbgon == 1) {
system($outfile) ### = 'tempgps1.csv';
}

#eof
