#!/usr/bin/perl -w
#
# hp3421a.pl
# version 0.3 -- 26 March 2005
#
# Record data from Austron 2100F phase meter output via HP 3421A 
# 
# Copyright 2004 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.

# The Austron 2100F has a nominal 0 to 1 volt output which tracks its phase
# meter, with scales of either 0-1us or 0-10us.  The average output voltage
# measured by my 3421A is actually 0.0100v for "zero" and 0.9947v for "one".
# This program averages the reading and scales between these ranges.

# assumes using channels 03 and 04 on the 3421A unless overridden

use strict;
use POSIX qw(setsid);
use Getopt::Std;
use Time::HiRes qw(usleep);
use LinuxGpib;
use DateTime;
use n8ur qw(trim squash collapse round);
use n8ur_gpib qw(checkSRQ serviceSRQ serviceSRQmulti ibwrite logline);

##########################
my $board = "gpib0";
my $device = "hp3421a";
##########################

#----------
# handle signals and errors -- mainly to clear lockfile on termination
sub sig_handler {
	sleep 2;
	close LOG;
	exit(0);
}

sub marker {
	print LOG "##### NEW SEGMENT ####\n";
}

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

#----------
# display usage
my $opt_string = 'h:i:t:c:f:';

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

usage: $0 [-h] [-a interval] [-t mjd|iso|none] [-c channel] -f logfile

-i	: sample interval -- default 10 seconds
-c	: channel -- range 0-29, default 3
-t	: timetag format: mjd,iso,none (default is ISO8601)
-h	: this (help) message
-f	: logfile using full pathname;
	  for output to console, use "-f -"

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

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

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

my $interval = 10;
if ($opt{i}) {
	$interval = $opt{i};
	}

my $channel = "03";
if ($opt{c}) {
	$channel = $opt{c};
	}

my $tag = "iso";
if ($opt{t}) {
	$tag = lc($opt{t});
	}

my $logfile = $opt{f};

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

# The Austron 2100F has a nominal 0 to 1 volt output which tracks its phase
# meter, with scales of either 0-1us or 0-10us.  The average output voltage
# measured by my 3421A is actually 0.0100v for "zero" and 0.9947v for "one".
# This routine averages the reading and scales between these ranges.
sub scale_austron {
	my $reading = shift;

	# make sure we don't go below 0
	if ($reading < 0.010015) {
		$reading = 0.010015;
		}
	$reading = $reading - 0.010015;

	# make sure we don't go above 1
	if ($reading > 0.99476) {
		$reading = 0.99476;
		}

	# the range is 0.9847 and we scale by the inverse of this -- 1.00526
	# while we're at it, convert to nanoseconds
	return round(4,($reading * 1.00526))*1e-6;
}	

# The Spectracom 8164 WWVB receiver outputs nominally 0 to 5 volts for a
# range of 50 microseconds.  The actual minimum is 0.00034 and the maximum
# is 4.956 volts.  The output is inverted (i.e., 5 volts is 0us, and 0
# volts is 50us.
sub scale_spectracom {
	my $reading = shift;

	# make sure we don't go below 0
	if ($reading < 0.00034) {
		$reading = 0.00034;
		}

	# make sure we don't go above 5
	if ($reading > 4.956) {
		$reading = 4.956;
		}
	# the range is 4.956 and we scale by 5.000/4.956 -- 1.009
	$reading = $reading * 1.009;

	# invert
	$reading = 5.000 - $reading;
	
	# while we're at it, convert to microseconds
	return round(4,$reading)*1e-5;
}	

#----------
# initialize device
my $brd = LinuxGpib::ibfind($board);
my $dev = LinuxGpib::ibfind($device);
my $status;

# clear -- reinitialize the 3421A, which takes several seconds
#$status = LinuxGpib::ibclr($dev);
#sleep 6;
# set for SRQ;
ibwrite($dev,"M1\n");

# write header line
my $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).\n",$current_iso,$current_mjd;
print LOG "# Sampled every $interval seconds, phase values in seconds.\n";
print LOG "# Data from HP 3421A.  Col. 2 is Austron 2100F; Col. 3 is Spectracom 8164.\n";

#----------
# main loop
my @result = "";

# main loop
ibwrite($dev,"DCV03,04\n");

while (1) {
        if (checkSRQ($brd)) {
		@result = serviceSRQmulti($dev,2);
		$result[1] = trim(squash($result[1]));
		$result[2] = trim(squash($result[2]));
	}
	if ($result[1]) {
		print LOG logline($tag,5,scale_austron($result[1]),
			scale_spectracom($result[2]));
		$result[1] = "";
		$result[2] = "";
		ibwrite($dev,"DCV03,04\n");
	}
	usleep(856000);
	sleep($interval-1);
}	

#----------

exit 0;
