#!/usr/bin/perl -w
#
# austron.pl
# version 0.9 -- 29 August 2003
#
# Query B&B Electronics 232SDA12 DAC and log results
# Version tweaked to read Austron 2100F LORAN-C Receiver
# 
# Copyright 2003 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 Time::HiRes qw(usleep);
use Device::SerialPort;
use n8ur qw(trim collapse squash round);

#----------
# some variables that need to be declared
my $quiet = "1";
my $lockfile;
my $command;
my $counter;
my $sum;
my $result;
my $first;
my $first_int;
my $first_avg;
my $samples;
my $count_in;
my $reading;
my $ref;
my $byte1;
my $byte2;
my $value;
my $volts;
my $avg_volts;
my $last_avg_volts;
my $microseconds;
my $places;

#----------
# handle signals and errors -- mainly to clear lockfile on termination
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:  austron.pl [-dh] -i log_interval -p serial_port -f logfile

-d	: run as daemon
-h	: this (help) message
-f	: logfile using full pathname;
	  for output to console, use "-f -"
-i	: logging interval in seconds (average of 1 second readings)
-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}) || (!$opt{f}) || (!$opt{p}));

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

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

$interval = 1;
if ($opt{i}) {
	$interval = $opt{i};
}

#----------
# set up first serial port
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->dtr_active(1)	|| die "fail setting dtr";
$ob->rts_active(1)	|| die "fail setting rts";
$ob->write_settings || die "no settings";

# end of message characters for serial input
#$ob->are_match("\n","\r");
#----------
# set up logfile
open (LOG, ">>$logfile") ||
	die "Can't open logfile $logfile!\n";
# set nonbuffered mode if we are sampling at 5 seconds or greater
if ($interval >= 5) {
	select(LOG), $| = 1;
	}

#----------
# resolution with 5V reference is about 1.211mv
$ref = 5.0/4095;
$command = "!0RA" . chr(0x0);
$sum = 0.0;
$counter = 1;
$value = 0.0;
$volts = 0.0;
$avg_volts = 0.0;
$last_avg_volts = 0.0;
$first = 0;
$first_int = 0;
$first_avg = 1;

# set number of places to match averaging level -- 100 seconds adds one
# place; doubtful we have long enough averaging to go further than that
if ($interval < 100) {
	$places = 3;
	}
else {
	$places = 4;
	}

# main loop
while (1) {
	$ob->write($command);
	($count_in,$reading) = $ob->read(32);
	if (($count_in > 0) && ($first > 1)){
		$byte1 = ord(substr($reading,0,1));
		$byte2 = ord(substr($reading,1,1));
		$value = ($byte1*256 + $byte2);
		$volts = $value * $ref;
		$sum += $volts; 
	
		if ($counter == $interval) {
			$avg_volts = round($places,$sum/$counter);
			if (($avg_volts == $last_avg_volts) &&
				($first_int > 0)) {
				$avg_volts += 0.00001;
				}
			$last_avg_volts = $avg_volts;
			$first_int++;

			# assume 1 volt = 1 microsecond
			$microseconds = $avg_volts*10e-7; 

			my ($sec,$min,$hour,$mday,$mon,$year,
				$wday,$yday,$isdst)
		  	= gmtime;

			printf LOG 
		  	"%4.4u-%2.2u-%2.2uT%2.2u:%2.2u:%2.2u %7.5E\n",
	  		$year+1900,$mon+1,$mday,$hour,$min,$sec,$microseconds;

			$counter = 1;
			$sum = 0;
		} else {
			$counter++;
			}
	}
	$first++;
	# sleep a wee bit less than a second because of processing time
	usleep(990000);
}	
#----------
exit 0;
