#!/usr/bin/perl

# Copyright (C) 2009, Adam Boggs (spottertool@aircrafter.org)
# All rights reserved.
#
# This work is licensed under the Creative Commons Attribution License 3.0.
# To view a copy of this license, visit:
# http://creativecommons.org/licenses/by/3.0/us/

# spottertool.pl - Read data from gpsd and periodically log it or send
#                  it to SpotterNetwork.
# Version 1.0

# This software interfaces with SpotterNetwork.  The author of this software
# is not responsible for its use or any actions that may violate the
# SpotterNetwork terms of use.  The SpotterNetwork Terms and Conditions
# of Use can be viewed at http://www.spotternetwork.org/tou.php
#
# TODO:
#    - add separate intervals for logs vs. spotternetwork updates
#    - curses interface
#    - support different log formats (eg. GoogleEarth, CSV, others?)
#
# If you would like to share any enhancements or bug fixes you make with
# others, please send the changes back to me and I will include them in the
# release.  Enjoy!

# User configurable parameters
$id = "XXXXXXXXXXXXX";	# Your spotter network personal ID
$active = 1;		# Whether you are actively chasing (1) or not (0)
$log_file = true;	# Whether to log position reports to a file or not
$log_screen = true;	# Whether to log position reports to the screen or not
$update_sn = false;	# Whether to update your position on SpotterNetwork
$update_interval = 120; # seconds, should be >120 if $update_sn is set.
$debug = false;		# Whether to dump the raw gpsd data for debugging


##### End user configurable part #####

use IO::Socket;
use LWP::Simple;
require HTTP::Request;

# open gpsd socket
my $gpsd_socket = new IO::Socket::INET (
		PeerAddr => 'localhost',
		PeerPort => 'gpsd',
		Proto => 'tcp',
);
die "Could not create socket: $!\n" unless $gpsd_socket; 

# open log file if specified
if ($log_file) {
    my $date = `date +%Y-%m-%d.%H:%M:%S`;
    chomp $date;
    $log_filename = "GPS-" . $date . ".log";
    print "Logging to file $log_filename.\n";
    open LOGFILE, ">>$log_filename" || die "Failed to open file $log_filename";
    print LOGFILE "# timestamp\tLattitude\tLongitude\tElevation\tComment\n";
    close LOGFILE;
}

if ($log_screen) {
    print "# timestamp\tLattitude\tLongitude\tElevation\tComment\n";
}

# Tell gpsd to output all data in a single line.
print $gpsd_socket "o\n";
while ($input = <$gpsd_socket>) {
    $gps = gpsd_parse($input);

    # Dump the hash for debugging
    if ($debug) {
	while ( my ($key, $value) = each(%$gps) ) {
	    print "$key => $value\n";
	}
    }

    if ($gps->{tag} ne "?") {

	# convert altitude to ft and speed to mph
	$mph = $gps->{speed} * 2.23693629;
	$altitude = $gps->{altitude} * 3.2808399;

	if ($update_sn) {
	    # update spotter network
	    $url = "http://www.spotternetwork.org/update.php?lat=$gps->{latitude}&lon=$gps->{longitude}&elev=$altitude&dir=$gps->{course}&mph=$mph&gps=1&active=$active&ver=1&id=$id";
#	    print "$url\n";
	    $response = get $url;
	} else {
	    $response = "[Not sent to SpotterNetwork]";
	}

	gps_log($response);
	sleep $update_interval;
    } else {
	gps_log("[Lost reception]");
	sleep 5;
    }

    # get next line from gpsd
    print $gpsd_socket "o\n";
}
close($gpsd_socket);




sub gpsd_parse 
{
    my $line = shift;
    my %gps = ();

    $line =~ s/\s*$//g;   # remove cr/lf from end

    # line looks like:
    # GPSD,O=GLL 1241231908.000 0.005 40.061400000 -105.177806667 1604.600 ? ? 100.9000 0.000 0.000 ? ? ? 3
    # tag timestamp time_error latitude longitude altitude horiz_error vert_error course speed vert_vel course_error speed_error vert_vel_error nmea_mode

    @ll = split (/=/, $line);
    chomp @ll;

    # Now split into associative array... smart perl way to do this?
    ($gps{tag}, $gps{timestamp}, $gps{time_error}, $gps{latitude}, $gps{longitude}, $gps{altitude}, $gps{horiz_error}, $gps{vert_error}, $gps{course}, $gps{speed}, $gps{vert_vel}, $gps{course_error}, $gps{speed_error}, $gps{vert_vel_error}, $gps{nmea_mode}) =  split(/ /, @ll[1]);

    return \%gps;
}

# Log the gps data to a file and/or the screen
sub gps_log
{
    my $comment = shift;

    if ($log_file) {
	open LOGFILE, ">>$log_filename" || die "Failed to open file $log_filename";
	print LOGFILE "$gps->{timestamp}\t$gps->{latitude}\t$gps->{longitude}\tgps->{altitude}\t$gps->{course}\t$gps->{speed}\t\"$comment\"\n";
	close LOGFILE;
    }

    if ($log_screen) {
	print "$gps->{timestamp}\t$gps->{latitude}\t$gps->{longitude}\t$gps->{altitude}\t$gps->{course}\t$gps->{speed}\t\"$comment\"\n";
    }
}
