#!/usr/bin/perl
# frida v. usb - gps nmea logger
# (c) GNU-GPL Luka Frelih, 2007
#
# Author: Bruce S. Garlock
# Date:   2002-09-11
# Requirements: Device::SerialPort 0.12 (from cpan)
#
# Version: 0.2
#
#
# Description:  This perl script is for logging of data from a NMEA 183
# device on a serial port, to a specified logfile.
#
# It checks for NMEA checksum on all lines and only logs correctly received ones.
#
# A new log file is created each day.
#
# It also sets the system date and time based on the NMEA output and
# signals the state of GPS lock to a statusfile.
#

use strict;
use Device::SerialPort 0.12;
use Data::Dumper;

my $LOGDIR     = "/home/frida/log/gps";     # create logs here
my $LOGFILE    = "frida-gps.nmea";          # file name to output to
my $PORT       = "/dev/usb/tts/0";          # port to watch
my $STATUSFILE = "/tmp/frida-status-gps";
my $LOCFILE    = "/tmp/frida-location";

our $DATE = 'warmup';
my $date_been_set = 0;
my $lock_was_ok   = -1;

#testing
if (0) {
    $PORT   = "/home/luka/devgps";          # port to watch
    $LOGDIR = `pwd`;                        # path to data file
    chomp $LOGDIR;
}

our $lastlocation;

openlog();

#
# Loop forever, logging data to the log file
#
while (1) {
    our $ob;
    if ( !$ob ) {
        next unless opendev();
    }

    my @status = $ob->status;
    my $status = scalar @status;
    #	print STDOUT "STATUS $status\n";
    #	print STDOUT Dumper(\@status);
    if ( $status != 4 ) {
        print STDERR "Device gone!\n";
        closedev();
        sleep 2;
        opendev();
        next;
    }

    my $gpsinput;

    eval {
        local $SIG{ALRM} = sub { die "alarm\n" };    # NB \n required
        alarm 3;
        $gpsinput = <DEV>;
        alarm 0;
    };
    die if $@ && $@ ne "alarm\n";                    # propagate errors
    if ($@) {

        # timed out
        print STDERR "Device read timeout!\n";
        closedev();
        sleep 2;
        opendev();
        $ob->purge_all if $ob;
        sleep 1;
    }
    else {
        # got some data!
        if ($gpsinput) {
            my $logln = nmealine($gpsinput);
            if ( $logln ne '' ) {
                print LOG $logln;
                #print STDOUT $logln;
            }
        }
    }
}

die ("WTF: exit from main loop?");

sub nmealine {    # parse one line of gps data
    my $input = $_ = shift;
    my $match = /\$((..)(.*))\*(.*)$/;
    my $line   = $1;
    my $talker = $2;
    my $chksum = $4;
    chop($chksum);
    my @fields = split ',', $3;
    my $xor = 0;
    for ( split( //, $line ) ) { $xor ^= ord($_); }
    my $versum = uc( sprintf( "%02x", $xor ) );

    if ( $versum ne $chksum ) {
        print STDERR "checksum mismatch: $versum ne $chksum in input:\n";
        print STDERR $input;
        return '';
    }

    if ( ( $fields[0] eq "RMC" ) ) {
        #print Dumper(\@fields);
        my $ok = $fields[2] eq "A" ? "1" : "0";

	if ($ok) {
	    $lastlocation=$line;
       	    writelocation();		
	    system "./frida-blink-led 5";	
	}

        if ( $ok ne $lock_was_ok ) {
            system "echo $ok > $STATUSFILE";
           $lock_was_ok = $ok;
        }

        my $time = $fields[1];
        $time =~ /^(\d\d)(\d\d)(\d\d)(.*)$/;
        my $hh   = $1;
        my $mm   = $2;
        my $ss   = $3;
        my $date = $fields[9];
        $date =~ /^(\d\d)(\d\d)(\d\d)$/;
        my $YY = $3;
        my $MM = $2;
        my $DD = $1;
        my $CC = '20';

        if ( $date ne $DATE ) { # date changed, reopen log
            $DATE = $date;
            closelog();
            openlog();
        }

        if ( !$date_been_set && ( $YY > 6 ) ) # set system date
        {    
            #in my case the receiver starts up in march 2005
            #BusyBox v1.00 (2006.03.27-00:00+0000) multi-call binary
            #Usage: date [OPTION]... [MMDDhhmm[[CC]YY][.ss]] [+FORMAT]
            system "date -us '$MM/$DD/$CC$YY $hh:$mm:$ss'";
            print STDOUT "DATE SET: date -us $MM$DD$hh$mm$CC$YY.$ss\n";
            $date_been_set = 1;
        }
    }

    return '$' . $line . '*' . $chksum . "\n";
}

# gps device and filehandle
sub opendev {
    return 0 unless initdev();
    open( DEV, "<$PORT" )
      || die "Cannot open $PORT: $_";
}

sub closedev {
    close(DEV);
    our $ob;
    $ob->close || warn "close failed";
    undef $ob;
}

sub initdev {
    # Serial Settings
    our $ob = Device::SerialPort->new($PORT);

    if ( !$ob ) {
        undef $ob;
        print STDERR "Can't Open $PORT: $!\n";
        sleep 2;
        return 0;
    }

    $ob->baudrate(4800)   || die "failed setting baudrate";
    $ob->parity("none")   || die "failed setting parity";
    $ob->databits(8)      || die "failed setting databits";
    $ob->stopbits(1)      || die "failed setting stopbits";
    $ob->handshake("rts") || die "failed setting handshake";
    $ob->write_settings   || die "no settings";
    $ob->pulse_break_on(250);

    return 1;
}

# log filehandle
sub openlog {
    my $logpath = "$LOGDIR/$DATE-$LOGFILE";
    open( LOG, ">>$logpath" )
      || die "can't open log file $logpath for append: $!\n";

    select(LOG), $| = 1;    # set nonbuffered mode
    print STDERR "log now at $logpath\n";
}

sub closelog {
    close LOG;
    print STDERR "closed log\n";
}

sub writelocation {
    open(LOCFILE, ">$LOCFILE");	
    print LOCFILE $lastlocation."\n";
    close(LOCFILE);	
}

