#!/usr/bin/perl -w
#
# hp5334-plotter.pl
# version 0.93 --  21 February2015
#
# Record time interval readings from HP 5334 counter
# Set switches, take one reading, then exit
# 
# Copyright 2015 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 POSIX qw(setsid);
use Getopt::Std;
use Time::HiRes qw(usleep);
use File::ReadBackwards;
use LinuxGpib;
use DateTime;
use n8ur qw(lower_case trim squash collapse round);
use n8ur_gpib qw(checkSRQ serviceSRQ logline);

# device name from /etc/gpib.conf
my $board = "gpib0";
my $device1 = "hp5334a";
my $device2 = "switch_1";
my $device3 = "switch_2";

my $brd;
my $dev1;
my $dev2;
my $dev3;

my $sw1 = "";
my $sw1a = "";
my $sw1b = "";
my $sw2 = "";
my $sw2a = "";
my $sw2b = "";
my $dut = "";
my $ref = "";
my $logfile;
my $result;
my $command;
my $count;

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

#----------
# display usage
my $opt_string = 'vhwa:b:c:d:t:z:s:f:';

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

usage: $0 [-v] [-z a|b|ab] [-h] 
	[-t iso|mjd] [-w] [-c comment ]
	[-d thermfile] -a ch -b ch [-s switch config]
	-f logfile

-a	 : start channel (1 - 10)
-b	 : stop channel (1 - 4)
-c	 : comment for logfile header
-d	 : read temperature from thermfile and append to log data
-f 	 : logfile using full pathname; for output to console, use "-f -"
-h	 : this (help) message
-s	 : switch configuration -- either 10 (inputs) or 7 (inputs)
-t	 : timestamp format: iso or mjd (default is mjd)
-v	 : aVerage for 100 readings
-w	 : wrap if reading > 500ms (take reciprocal)
-z	 : 50 ohm termination for channel a, b, or ab

EOF
}

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

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

# set variables to command line params

$logfile = $opt{f};

# Input A, start signal
my $a = 1;
if ($opt{a}) { $a = $opt{a}; }
usage() and exit if ($a > 10);

# Input B, stop signal
my $b = 1;
if ($opt{b}) { $b = $opt{b}; }
usage() and exit if ($b > 4);

# switch config, 7 or 10, default to 10
my $switch = 10;
if ($opt{s}) { $switch = $opt{s}; }
usage() and exit if (($switch != 7) && ($switch !=10));

# include therm data
my $therm_file = "";
if ($opt{d}) { 
	$therm_file = $opt{d};
}

# average
my $v = 0;
my $avg = "off";
if ($opt{v}) {
	$v = 1;
	$avg = "on";
	}

# wrap
my $w = 0;
if ($opt{w}) {
	$w = 1;
	}

my $tag = "mjd";
if ($opt{t}) { $tag = trim(lower_case($opt{t})); }

my $term = "";
if ($opt{z}) { $term = trim(lower_case($opt{z})); }

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

#----------
sub log_open {
# get time
my $dt = DateTime->now;
my $current_iso = $dt->ymd('-') . 'T' . $dt->hms(':');
my $current_mjd = $dt->mjd;

# set up logfile ; use non-buffered since log rate is slow
if (-e $logfile) {
	open (LOG, ">>$logfile") || die "Can't open logfile $logfile!\n";
} else {
	open (LOG, ">$logfile") || die "Can't create logfile $logfile!\n";
	if ($comment) { print LOG "# ",$comment,"\n"; }
	printf LOG "# Run started at %s (MJD %11.6F).\n",
		 $current_iso,$current_mjd;
	print LOG "# Data from $device1.";
	print LOG " 100 gate averaging $avg.\n";
	print LOG "# Start channel ",$a," ($dut).  ";
	print LOG "Stop channel ",$b," ($ref).\n";

	if ($therm_file) {
		print LOG "# Columns are MJD, phase in seconds, degrees C\n",
	} else {
		print LOG "# Columns are MJD, phase in seconds\n",
		}
	}
select(LOG), $| = 1;
}

#----------
sub device_init {
# initialize
$brd = LinuxGpib::ibfind($board);
$dev1 = LinuxGpib::ibfind($device1);
$dev2 = LinuxGpib::ibfind($device2);
$dev3 = LinuxGpib::ibfind($device3);

# clear
LinuxGpib::ibclr($dev1);
usleep(100);
LinuxGpib::ibclr($dev2);
usleep(100);
LinuxGpib::ibclr($dev3);
usleep(100);
}

sub counter_init {
# time interval, auto trigger off, wait to be addressed, SRQ mask 1,reset
$command = "INFN5AU0WA1SM1RE";
LinuxGpib::ibwrt($dev1,$command,length($command));
usleep(100);

# GV1 to do 100 gate averaging
$command = "GV0";
if ($v) {
	$command = "GV1";
}
LinuxGpib::ibwrt($dev1,$command,length($command));
usleep(100);

# set 50 ohm input
$command = "";
if ($term eq "a") {
	$command = "AZ1";
	}
if ($term eq "b") {
	$command = "BZ1";
	}
if ($term eq "ab") {
	$command = "AZ1BZ1";
	}
if ($command) {
	LinuxGpib::ibwrt($dev1,$command,length($command));
	usleep(100);
	}

}
#----------
################
sub set_switch_7 {

        SWITCH1: {
                $a == 1 && do { $sw1 = "A1B1" ; last SWITCH1; };
                $a == 2 && do { $sw1 = "A2B1" ; last SWITCH1; };
                $a == 3 && do { $sw1 = "A3B1" ; last SWITCH1; };
                $a == 4 && do { $sw1 = "A4B1" ; last SWITCH1; };
                $a == 5 && do { $sw1 = "A4B2" ; last SWITCH1; };
                $a == 6 && do { $sw1 = "A4B3" ; last SWITCH1; };
                $a == 7 && do { $sw1 = "A4B4" ; last SWITCH1; };
}

        SWITCH2: {
                $b == 1 && do { $sw2 = "A1B1" ; last SWITCH2; };
                $b == 2 && do { $sw2 = "A2B1" ; last SWITCH2; };
                $b == 3 && do { $sw2 = "A3B1" ; last SWITCH2; };
                $b == 4 && do { $sw2 = "A4B1" ; last SWITCH2; };
                $b == 5 && do { $sw2 = "A4B2" ; last SWITCH2; };
                $b == 6 && do { $sw2 = "A4B3" ; last SWITCH2; };
		$b == 7 && do { $sw2 = "A4B4" ; last SWITCH2; };
}

        $command = $sw1;
        LinuxGpib::ibwrt($dev2,$command,length($command));
        usleep(10000);

        $command = $sw2;
        LinuxGpib::ibwrt($dev3,$command,length($command));
        usleep(10000);
}

#################
sub set_switch_10 {

# Switch position (for both A and B) is:
# 1A1 feeds 1B; 1A2 feeds 2A for DUTs; 1A3 and 1A4 are DUTs;  2B is for REFs

	DUT: {
		$a == 1 && do { $sw1a = "A1" ; $sw1b = "B1" ; last DUT; };
		$a == 2 && do { $sw1a = "A1" ; $sw1b = "B2" ; last DUT; };
		$a == 3 && do { $sw1a = "A1" ; $sw1b = "B3" ; last DUT; };
		$a == 4 && do { $sw1a = "A1" ; $sw1b = "B4" ; last DUT; };
		$a == 5 && do { $sw1a = "A2" ; $sw2a = "A1" ; last DUT; };
		$a == 6 && do { $sw1a = "A2" ; $sw2a = "A2" ; last DUT; };
		$a == 7 && do { $sw1a = "A2" ; $sw2a = "A3" ; last DUT; };
		$a == 8 && do { $sw1a = "A2" ; $sw2a = "A4" ; last DUT; };
		$a == 9 && do { $sw1a = "A3" ; last DUT; };
		$a == 10 && do { $sw1a = "A4" ; last DUT; };
}

	REF: {
		$b == 1 && do { $sw2b = "B1" ; last REF; };
		$b == 2 && do { $sw2b = "B2" ; last REF; };
		$b == 3 && do { $sw2b = "B3" ; last REF; };
		$b == 4 && do { $sw2b = "B4" ; last REF; };
}

	if ($a >= 1 and $a <= 4) { $dut = 1 . $sw1a . "-" . 1 . $sw1b; };
	if ($a >= 5 and $a <= 8) { $dut = 1 . $sw1a . "-" . 2 . $sw2a; };
	if ($a >= 9) { $dut = 1 . $sw1a; };
	$ref = 2 . $sw2b;


	$command = $sw1a . $sw1b;
	LinuxGpib::ibwrt($dev2,$command,length($command));
	usleep(100);

	$command = $sw2a . $sw2b;
	LinuxGpib::ibwrt($dev3,$command,length($command));
	usleep(100);
}

#############
sub get_therm {

my $file = $_[0];

my $bw
my $therm_time;
my $tempF;
my $tempC;
my $reading;
my $dt;
my $now;

if ( $bw = File::ReadBackwards->new( $file ); ) {
	$reading = $bw->readline;
	if (substr($reading,0,1) =~ "[0-9]") {
		($therm_time,$tempF) = split(/\s/,$reading);
	} else {
		return -99.9;
		}

	$tempC = round(1,($tempF-32)*(5/9));

	# test to see if therm reading date is sane
	$dt = DateTime->now;
	$now = round(6,$dt->mjd);

	# 0.021 is about 30 minutes in MJF
	if ( ($now - $therm_time) < 0.021 ) {
		return $tempC;
	} else {
		return -99.9;
	}
} else {
	return -99.9;
	}
}

#----------

# main loop

# init devices
device_init;

#set start and stop channel
if ($switch == 7) {
	set_switch_7;
} else {
	set_switch_10;
}

usleep(100);

# get counter ready
counter_init;

$result = 0;
$count = 0;
while ((!$result) && ($count <= 100)) {
	# sleep a bit
	if ($v) { sleep 10; } else { usleep 20000; }
	if (checkSRQ($brd) && (!$result)) {
		$result = substr(squash(trim(serviceSRQ($dev1))),1);
		}
	$count++
	}

# 5334 can't deal with negative time interval.  Assume if reading is
# > 500 milliseconds, that it's really early and take reciprocal.
if ( ($w) && ($result > 0.5) ) {
	$result = 0.0 - (1.0 - $result) 
	};


log_open;

if ($therm_file) {
	my $tempC = get_therm($therm_file);
	print LOG logline($tag,9,$result,-1,$tempC);
} else {
	 print LOG logline($tag,9,$result);
}

# return to local
LinuxGpib::ibloc($dev1);
usleep(100);
LinuxGpib::ibloc($dev2);
usleep(100);
LinuxGpib::ibloc($dev3);
usleep(100);

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

#----------
exit(0);
