#!/usr/bin/perl -w
#
# oncore-m12.pl
# version 0.9 -- 22 June 2005
#
# Query Motorola M12+ Timing 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 $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;

my (	$header0,$header1,$header2,$header3,
	$mm,$dd,$y0,$y1,$h,$m,$s,$f0,$f1,$f2,$f3,$f4,
	$a0,$a1,$a2,$a3,$o0,$o1,$o2,$o3,
	$h0,$h1,$h2,$h3,$msl0,$msl1,$msl2,$msl3,
	$ua0,$ua1,$ua2,$ua3,$uo0,$uo1,$uo2,$uo3,
	$uh0,$uh1,$uh2,$uh3,$umsl0,$umsl1,$umsl2,$umsl3,
#	$V0,$V1,$v0,$v1,$heading0,$heading1,
	$V0,$V1,$v0,$v1,$heading0,
	$dop0,$dop1,$numvis,$numtrack,
	$svid0,$mode0,$strength0,$IODE0,$stat0_0,$stat1_0,
	$svid1,$mode1,$strength1,$IODE1,$stat0_1,$stat1_1,
	$svid2,$mode2,$strength2,$IODE2,$stat0_2,$stat1_2,
	$svid3,$mode3,$strength3,$IODE3,$stat0_3,$stat1_3,
	$svid4,$mode4,$strength4,$IODE4,$stat0_4,$stat1_4,
	$svid5,$mode5,$strength5,$IODE5,$stat0_5,$stat1_5,
	$svid6,$mode6,$strength6,$IODE6,$stat0_6,$stat1_6,
	$svid7,$mode7,$strength7,$IODE7,$stat0_7,$stat1_7,
	$svid8,$mode8,$strength8,$IODE8,$stat0_8,$stat1_8,
	$svid9,$mode9,$strength9,$IODE9,$stat0_9,$stat1_9,
	$svid10,$mode10,$strength10,$IODE10,$stat0_10,$stat1_10,
	$svid11,$mode11,$strength11,$IODE11,$stat0_11,$stat1_11,
	$rxstat0,$rxstat1,$clockbias0,$clockbias1,
	$offset0,$offset1,$offset2,$offset3,$osc_temp0,$osc_temp1,
	$utc_param,$gmt_offset_sign,$gmt_offset_hour,$gmt_offset_min,
	$id0,$id1,$id2,$id3,$id4,$id5,$checksum,$cr,$lf);
	
#----------
# 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;

#----------
# write header line
$dt = DateTime->now;
my $current_iso = $dt->ymd('-') . 'T' . $dt->hms(':');
my $current_mjd = $dt->mjd;
printf LOG "# Run started at %s (MJD %11.6F); data every %i seconds.\n",
        $current_iso,$current_mjd,$interval;

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

# shut down outputs
# turn off Ha
my $Ha_string = chr(0x40) . chr(0x40) . "Ha" . chr(0x00) . chr(0x29) . 
	chr(0x0d) . chr(0x0a);
my $Bb_string = chr(0x40) . chr(0x40) . "Bb" . chr(0x00) . chr(0x20) . 
	chr(0x0d) . chr(0x0a);
$ob->write($Ha_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
	$result = "";
		($header0,$header1,$header2,$header3,
		$mm,$dd,$y0,$y1,$h,$m,$s,$f0,$f1,$f2,$f3,$f4,
		$a0,$a1,$a2,$a3,$o0,$o1,$o2,$o3,
		$h0,$h1,$h2,$h3,$msl0,$msl1,$msl2,$msl3,
		$ua0,$ua1,$ua2,$ua3,$uo0,$uo1,$uo2,$uo3,
		$uh0,$uh1,$uh2,$uh3,$umsl0,$umsl1,$umsl2,$umsl3,
#		$V0,$V1,$v0,$v1,$heading0,$heading1,
		$V0,$V1,$v0,$v1,$heading0,
		$dop0,$dop1,$numvis,$numtrack,
		$svid0,$mode0,$strength0,$IODE0,$stat0_0,$stat1_0,
		$svid1,$mode1,$strength1,$IODE1,$stat0_1,$stat1_1,
		$svid2,$mode2,$strength2,$IODE2,$stat0_2,$stat1_2,
		$svid3,$mode3,$strength3,$IODE3,$stat0_3,$stat1_3,
		$svid4,$mode4,$strength4,$IODE4,$stat0_4,$stat1_4,
		$svid5,$mode5,$strength5,$IODE5,$stat0_5,$stat1_5,
		$svid6,$mode6,$strength6,$IODE6,$stat0_6,$stat1_6,
		$svid7,$mode7,$strength7,$IODE7,$stat0_7,$stat1_7,
		$svid8,$mode8,$strength8,$IODE8,$stat0_8,$stat1_8,
		$svid9,$mode9,$strength9,$IODE9,$stat0_9,$stat1_9,
		$svid10,$mode10,$strength10,$IODE10,$stat0_10,$stat1_10,
		$svid11,$mode11,$strength11,$IODE11,$stat0_11,$stat1_11,
		$rxstat0,$rxstat1,$clockbias0,$clockbias1,
		$offset0,$offset1,$offset2,$offset3,$osc_temp0,$osc_temp1,
		$utc_param,$gmt_offset_sign,$gmt_offset_hour,$gmt_offset_min,
		$id0,$id1,$id2,$id3,$id4,$id5,$checksum,$cr,$lf)
			= ();

	$count_in = 0;
	$ob->lookclear;
	$ob->write($Ha_string);
	($count_in, $result) = $ob->read(154);

	if ($count_in == 154) {
		($header0,$header1,$header2,$header3,
		$mm,$dd,$y0,$y1,$h,$m,$s,$f0,$f1,$f2,$f3,$f4,
		$a0,$a1,$a2,$a3,$o0,$o1,$o2,$o3,
		$h0,$h1,$h2,$h3,$msl0,$msl1,$msl2,$msl3,
		$ua0,$ua1,$ua2,$ua3,$uo0,$uo1,$uo2,$uo3,
		$uh0,$uh1,$uh2,$uh3,$umsl0,$umsl1,$umsl2,$umsl3,
#		$V0,$V1,$v0,$v1,$heading0,$heading1,
		$V0,$V1,$v0,$v1,$heading0,
		$dop0,$dop1,$numvis,$numtrack,
		$svid0,$mode0,$strength0,$IODE0,$stat0_0,$stat1_0,
		$svid1,$mode1,$strength1,$IODE1,$stat0_1,$stat1_1,
		$svid2,$mode2,$strength2,$IODE2,$stat0_2,$stat1_2,
		$svid3,$mode3,$strength3,$IODE3,$stat0_3,$stat1_3,
		$svid4,$mode4,$strength4,$IODE4,$stat0_4,$stat1_4,
		$svid5,$mode5,$strength5,$IODE5,$stat0_5,$stat1_5,
		$svid6,$mode6,$strength6,$IODE6,$stat0_6,$stat1_6,
		$svid7,$mode7,$strength7,$IODE7,$stat0_7,$stat1_7,
		$svid8,$mode8,$strength8,$IODE8,$stat0_8,$stat1_8,
		$svid9,$mode9,$strength9,$IODE9,$stat0_9,$stat1_9,
		$svid10,$mode10,$strength10,$IODE10,$stat0_10,$stat1_10,
		$svid11,$mode11,$strength11,$IODE11,$stat0_11,$stat1_11,
		$rxstat0,$rxstat1,$clockbias0,$clockbias1,
		$offset0,$offset1,$offset2,$offset3,$osc_temp0,$osc_temp1,
		$utc_param,$gmt_offset_sign,$gmt_offset_hour,$gmt_offset_min,
		$id0,$id1,$id2,$id3,$id4,$id5,$checksum,$cr,$lf)
			= split(//,$result);

		$visible = ord($numvis);
		$tracked = ord($numtrack);

		@tmp_array = ();
		# create sorted array of signal strength
		$tmp_array[0] = ord($strength0);
		$tmp_array[1] = ord($strength1);
		$tmp_array[2] = ord($strength2);
		$tmp_array[3] = ord($strength3);
		$tmp_array[4] = ord($strength4);
		$tmp_array[5] = ord($strength5);
		$tmp_array[6] = ord($strength6);
		$tmp_array[7] = ord($strength7);
		$tmp_array[8] = ord($strength8);
		$tmp_array[9] = ord($strength9);
		$tmp_array[10] = ord($strength10);
		$tmp_array[11] = ord($strength11);

		@sat_array = ();
		@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[11];

		# get average signal strength
		$sig_sum = ord($strength0)+ord($strength1)+
		ord($strength2)+ord($strength3)+ord($strength4)+
		ord($strength5)+ord($strength6)+ord($strength7)+
		ord($strength8)+ord($strength9)+ord($strength10)+
		ord($strength11);
		$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 %u %u %u %u\n",
		ord($strength0),ord($strength1),ord($strength2),ord($strength3),
		ord($strength4),ord($strength5),ord($strength6),ord($strength7),
		ord($strength8),ord($strength9),ord($strength10),
		ord($strength11);

	}

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

exit 0;
