#!/usr/bin/perl -w
#
# ut_plus-logger.pl
# version 0.9 -- 24 April 2005
#
# Query Motorola UT+ GPS and log results
# 
# Copyright 2005 by John R. Ackermann  N8UR (jra@febo.com)
#
# This program may be copied, modified, distributed and used for 
# any legal purpose provided that (a) the copyright notice above as well
# as these terms are retained on all copies; (b) any modifications that 
# correct bugs or errors, or increase the program's functionality, are 
# sent via email to the author at the address above; and (c) such 
# modifications are made subject to these license terms.

use strict;
use vars qw( $OS_win $ob $port );
use POSIX qw(setsid);
use Getopt::Std;
use Device::SerialPort;
use n8ur qw(trim squash collapse round);
use DateTime;


#----------
# handle signals and errors -- mainly to clear lockfile on termination
my $lockfile;
sub sig_handler {
	undef $ob;
	if (-e $lockfile) {unlink $lockfile;}
	close LOG;
	exit(0);
}

# there's got to be a better way!
$SIG{'HUP'} = 'sig_handler';
$SIG{'INT'} = 'sig_handler';
$SIG{'KILL'} = 'sig_handler';
$SIG{'STOP'} = 'sig_handler';
$SIG{'TERM'} = 'sig_handler';
#----------

#----------
# display usage
my $opt_string = 'dhi:f:p:';

sub usage() {
print STDERR << "EOF";

usage: $0 [-dh] -f logfile -i log_interval -t timestamp -p serial_port

-d	: run as daemon
-h	: this (help) message
-f	: logfile using full pathname;
	  for output to console, use "-f -"
-i	: logging interval in seconds
-t	: timestamp (mjd or iso; default mjd)
-p	: serial port ("ttyS1")

EOF
}
#----------

#----------
# daemonize
sub daemonize {
	chdir '/' or die "Can't chdir to /: $!";
	umask 0;
	open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
	open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!";
	open STDERR, '>/dev/null' or die "Can't write to /dev/null: $!";
	defined(my $pid = fork) or die "Can't fork: $!";
	exit if $pid;
	setsid or die "Can't start a new session: $!";
}
#----------

getopts( "$opt_string", \my %opt ) or usage() and exit;

# print usage
usage() and exit if $opt{h};

# run as daemon?
&daemonize if $opt{d};

# set variables to command line params
my $portbase = $opt{p};
my $logfile = $opt{f};

my $interval = $opt{i};

my $tags = "mjd";
if ($opt{t}) {
        $tags = lc($opt{t});
        }


# initialize other variables
my $count_in;
my $result;
my $sig_sum;
my $avg_sig;
my ($h0,$h1,$h2,$h3,
    $month,$day,$year0,$year1,$hour,$min,$sec,$frac0,$frac1,$frac2,$frac3,
    $lat0,$lat1,$lat2,$lat3,$lon0,$lon1,$lon2,$lon3,$height0,
    $height1,$height2,$height3,
    $nul0,$nul1,$nul2,$nul3,
    $vel0,$vel1,$heading0,$heading1,
    $dop0,$dop1,$dop_type,
    $num_visible,$num_tracked,
    $sat_id0,$sat_mode0,$c_no0,$status0,
    $sat_id1,$sat_mode1,$c_no1,$status1,
    $sat_id2,$sat_mode2,$c_no2,$status2,
    $sat_id3,$sat_mode3,$c_no3,$status3,
    $sat_id4,$sat_mode4,$c_no4,$status4,
    $sat_id5,$sat_mode5,$c_no5,$status5,
    $sat_id6,$sat_mode6,$c_no6,$status6,
    $sat_id7,$sat_mode7,$c_no7,$status7,
    $rx_status,$checksum,$cr,$lf);

my $receive_status;
my $visible;
my $tracked;
my $header;
my @tmp_array;
my @sat_array;
my $min_strength;
my $max_strength;
my $i;
my $dt;
my $timetag;
#----------
# set up serial port
my $quiet = "1";
my $port = "/dev/" . $portbase;
$lockfile = "/var/lock/LCK.." . $portbase;

$ob = Device::SerialPort->new ($port,$quiet,$lockfile);
	die "Can't open serial port $port!\n" unless ($ob);
$ob->baudrate(9600)	|| die "fail setting baud after 0";
$ob->parity("none")	|| die "fail setting parity";
$ob->databits(8)	|| die "fail setting databits";
$ob->stopbits(1)	|| die "fail setting stopbits";
$ob->handshake("none")	|| die "fail setting handshake";
$ob->write_settings || die "no settings";
$ob->are_match("\r\n");

#----------

#----------
# set up logfile
open (LOG, ">>$logfile") ||
	die "Can't open logfile $logfile!\n";
# set nonbuffered mode
select(LOG), $| = 1;

#----------
# main loop

# shut down outputs
# turn off Ea
my $Ea_string = chr(0x40) . chr(0x40) . "Ea" . chr(0x00) . chr(0x24) . 
	chr(0x0d) . chr(0x0a);
my $Bb_string = chr(0x40) . chr(0x40) . "Bb" . chr(0x00) . chr(0x20) . 
	chr(0x0d) . chr(0x0a);
$ob->write($Ea_string);
# turn of Bb
$ob->write($Bb_string);
# flush
$ob->lookclear;
$result = "";
  until ("" ne $result) {
      $result = $ob->streamline;       # poll until data ready
      die "Aborted without match\n" unless (defined $result);
  }
$ob->lookclear;
$result = "";
  until ("" ne $result) {
      $result = $ob->streamline;       # poll until data ready
      die "Aborted without match\n" unless (defined $result);
  }
sleep 1;

while (1) {

	#----------
	# get status
	$ob->write($Ea_string);
	$ob->lookclear;
	$result = "";
	$count_in = 0;
	($count_in, $result) = $ob->read(76);

	# process
	if ($count_in == 76) {
		($h0,$h1,$h2,$h3,
		$month,$day,$year0,$year1,$hour,$min,$sec,
		$frac0,$frac1,$frac2,$frac3,
		$lat0,$lat1,$lat2,$lat3,$lon0,$lon1,$lon2,$lon3,$height0,
		$height1,$height2,$height3,
		$nul0,$nul1,$nul2,$nul3,
		$vel0,$vel1,$heading0,$heading1,
		$dop0,$dop1,$dop_type,
		$num_visible,$num_tracked,
		$sat_id0,$sat_mode0,$c_no0,$status0,
		$sat_id1,$sat_mode1,$c_no1,$status1,
		$sat_id2,$sat_mode2,$c_no2,$status2,
		$sat_id3,$sat_mode3,$c_no3,$status3,
		$sat_id4,$sat_mode4,$c_no4,$status4,
		$sat_id5,$sat_mode5,$c_no5,$status5,
		$sat_id6,$sat_mode6,$c_no6,$status6,
		$sat_id7,$sat_mode7,$c_no7,$status7,
		$rx_status,$checksum,$cr,$lf) = split(//,$result);

		$visible = ord($num_visible);
		$tracked = ord($num_tracked);

		# create sorted array of signal strength
		$tmp_array[0] = ord($c_no0);
		$tmp_array[1] = ord($c_no1);
		$tmp_array[2] = ord($c_no2);
		$tmp_array[3] = ord($c_no3);
		$tmp_array[4] = ord($c_no4);
		$tmp_array[5] = ord($c_no5);
		$tmp_array[6] = ord($c_no6);
		$tmp_array[7] = ord($c_no7);

		@sat_array = sort { $a <=> $b } @tmp_array;

		$i = 0;
		$min_strength = 0;
		$max_strength = 0;
		until ($min_strength > 0) {
			$min_strength = $sat_array[$i];
			$i++;
			}
		if ($tracked == 0) {
			$min_strength = 0;
			}
		$max_strength = $sat_array[7];

		# get average signal strength
		$sig_sum = ord($c_no0)+ord($c_no1)+ord($c_no2)+ord($c_no3)+
		ord($c_no4)+ord($c_no5)+ord($c_no6)+ord($c_no7);
		$avg_sig = $sig_sum/$tracked;
		

		# print routine
		$dt = DateTime->now;

	        $timetag = "";
        	if ($tags eq "mjd") {
                	$timetag = sprintf("%6.6f",round(6,$dt->mjd));
        	}
        	if ($tags eq "iso") {
                	$timetag = $dt->ymd('-') . 'T' . $dt->hms(':');
        	}

		printf LOG "%s %u %u %4.2f %u %u",
		$timetag,$visible,$tracked,$avg_sig,$min_strength,
		$max_strength;
		printf LOG "   %u %u %u %u %u %u %u %u   %s\n",
		ord($c_no0),ord($c_no1),ord($c_no2),ord($c_no3),
		ord($c_no4),ord($c_no5),ord($c_no6),ord($c_no7),
		unpack("b8",$rx_status);

	}

	# sleep for loop
	sleep $interval;
}	
#----------

exit 0;
