#!/usr/bin/perl -w

##########################################################
#
# Capture and display data from Standard Counter
#
# (C) 2011 Doug Coulter, GPL v2 license -- share and enjoy,
# but if you improve it, be sure and share back, to
# clab@swva.net, or www.coultersmithing.com/forums
#
#
# See use section below for any modules you'll need to install first
##########################################################
# It's probably obvious I'm coding this like I was a C programmer
# (because I am), and squishing fancy expressions into single liners
# would just make the inevitable changes this is going to get harder
# to figure out later -- for a C programmer (me).
#
# Anything I thought was dodgy is marked with an @@@ for searching
#
# If you're a perl purist or stylist, sorry 'bout that.  TMTOWTDI
# and, TANSTAAFL for good measure.
##########################################################

# stuff we "use", this wheel's been round for awhile
use strict;  # let the interpreter catch as many errors of mine as possible

# functions needed for getmypath()
use File::Spec::Functions qw/:ALL/; # changes things like ./ to a real path
use File::Basename; # to get path to program for config files and logs
# gnuplot for plots
use Graphics::GnuplotIF qw(GnuplotIF);

# stuff for GTK2 GUI to come later
# use Glib qw/TRUE FALSE/; # needed later for timer events too, like below
#add a timer to run each second
#   Glib::Timeout->add (1000,\&new_screen);

# note: serial port setup below needs either Device::SerialPort or Win32::Serialport perl module, "required" into perl once we know which opsys we've got

###########################################################

# globals

my $debug = 0; # set non zero to get extra debug stuff printed out on STDIO
my $running = 1; # mode control flag
my $ttyport;   # @@@ set in PIC
my $port; # serial port object pointer
my $opsys; # true if linux, false if windows
my $portname; # full name/path of port (for linux)
my $path; # path to this program, stripped of name from getmypath();

my $currentline; # most recent text from std counter
my @cfields; # above, split into fields
my $csec; # total seconds
my $cg1; # geiger count one sec
my $cg10; # geiger count 10 sec
my $cgt; # geiger total
my $cn1; # neutron one sec
my $cn10; #neutron 10 sec
my $cnt;  # neutron total counts

# arrays for plotting

my @x;		# gets time for X axis of plots
my @geig1; # and various things to plot on Y
my @geig10;
my @neut1;
my @neut10;
#
my @gfil; # filtered data

my $curindex;

my $fila = 0.03;
my $filb = 1.0 - $fila;
my $filacc;
###########################################################
# get friggin pathname to program directory for use in config and log files
# we need this insane dance as the program path in $0 might be ./name
# which is kind of hard to use right.  We want an absolute path where
# the script lives, and this gets it (with two modules needed!)
sub getmypath()
{
 my $path = rel2abs($0);
# print ("\nmunged path is: $path\n");
 $path = dirname($path);
#print ("\nfinally, path to program is: $path\n"); 
 return $path;
}
###########################################################
# setup serial port and return false if error, more involved than
# it should have to be, but...that's life.  Even nastier on windows
# once we get that far, count on it.  Probably going to be real hard
# to support later windows than XP or any 64 bit.  Unless you like
# running user programs as Administrator and so on.

sub setupserial()
{
# first find out what the opsys is
 if ($^O =~ m/linux/i) # special variable tells us opsys
 {
  require Device::SerialPort; # the correct one for linux
  $opsys = 1;  # true for linux
  $ttyport = "usb-CLAB_STD_CTR_000-if00";   # @@@ for linux only, set in PIC
  print ("\nUsing Linux serial driver\n");
 } 
 elsif ($^O =~ m/MSWin32/i)
 {
  require Win32::SerialPort; # the one for windoze
  $opsys = 0; # a big zero for win32
  $ttyport = "COM3"; # @@@ guess for now
  print ("\nUsing Windows serial driver??\n");
 }
 else
 {
  die "Unknown opsys: $^O"; # can't do much without a serial driver
 }

# now, init serial port from code defaults or config file
# @@@ this is the linux code, windows will be different?


    my $cfgfile = sprintf("%s.cfg", $ttyport);
    $cfgfile = catfile($path,$cfgfile); # with full path to program dir
    
    if($opsys) {$portname = sprintf("/dev/serial/by-id/%s", $ttyport);}
    else {$portname = $ttyport;}  # windows case, com? or something	 

    if ( -e $cfgfile ) {   # cfgfile exists
      $port = Device::SerialPort->start ($cfgfile) or die "Can't start $cfgfile\n";
	} else {   # cfgfile does not exist (e.g. 1st time for a tty device)
      $port = new Device::SerialPort ($portname, 1) || die "Can't open $portname: $!\n";
	}

    # some of this not needed if config file exists, this is for suspenders (and first times)
    # @@@ put this in else above, so can reset config by deleting the config on disk
      $port->databits(8);
      $port->baudrate(57600);
      $port->parity("none");
      $port->stopbits(1);
      $port->handshake("none");
      $port->stty_icanon(1);    # this is what gets us on line boundaries
      $port->are_match("\n");                       # possible end strings
      $port->lookclear;                             # empty the buffer
      $port->write_settings || undef $port; # die, sort of, if not working
      $port->save($cfgfile) || warn "Can't save $cfgfile: $!\n"; # save for next time

}
#############################################
#my $ctr = 0;
sub getline()
{ # get a line from std counter
#print("\nlookfor:$ctr\n");
#$ctr++;

 my $gotit = "";               # start clean
 while ($gotit eq "")
 { # block in loop till an end of line (\n) found
  $gotit = $port->lookfor;      # poll until data ready
  die "Aborted without match\n" unless (defined $gotit); # @@@ fixme
  my $match = $port->matchclear;# so we dont keep reading the same line over
 }
 return $gotit;
}

#############################################
sub transcurrent($) # first time I've used a prototype, required by strict(!)
{ # translate current line into locals
 my $line = shift;
 
 my $h; # for breaking up time
 my $m;
 my $s;
 
 @cfields = split(/\s+/,$line); # break on whitespace
 
 $cg1 = $cfields[2]; # geiger counts in second
 $cg10 = $cfields[4]; # geiger counts in last 10 seconds
 $cgt = $cfields[6]; # total geiger counts
 
 if (defined $cfields[7]) # eg if we're putting out neutron counts too
 {
  $cn1 = $cfields[8];
  $cn10 = $cfields[10];
  $cnt = $cfields[12];
 } else
 {
  $cn1 = $cn10 = $cnt = 0; # make them defined, but zero
 }
 # figure out total seconds
 ($h,$m,$s) = split(/:/,$cfields[0]); # break up time and calculate total seconds
 $csec = $s + 60 * $m + 3600 * $h;
# print ("TotalSec:$csec\n");
 $geig1[$curindex] = $cg1;
 $geig10[$curindex] = $cg10;
 $gfil[$curindex] = $filacc = $cg1 * $fila + $filacc * $filb;
 # neutrons here if defined
 $x[$curindex++] = $csec; # x axis for all plots
}

#############################################
sub resetcount()
{ # reset hardware and local data aq variables
 $port->purge_rx; # clean house
 $port->write("!"); #reset clock and counters
 getline(); # throw away first line after that (likely garbage)
 @geig1 = (); #
 @geig10 = ();
 @neut1 = ();
 @neut10 = ();
 @x = (); # clear arrays too
 $curindex = 0;  # stuff them starting at the start
 @gfil = ();
 $filacc = 20;
}
#############################################

#///////////////// Main program

$path = getmypath(); #cheesh.  Needed for setupserial() config file location



print ("\nStandard Counter Data aquisition\n");
print ("Running on Opsys: $^O \n"); # cute
print ("program path is: $path\n");
 
setupserial(); # try to get the serial port for the Standard Counter, or report errors
resetcount(); # initialize hardware and plot arrays

my $plot = Graphics::GnuplotIF->new(title => "Geigers, Neutrons", style => 										"linespoints", xlabel => 'Seconds', ylabel =>'Counts per minute');
$plot->gnuplot_set_plot_titles("CPM-1","CPM-10", "Smoothed");
$plot->gnuplot_cmd ("set log y");
$plot->gnuplot_cmd ("set grid");


 while ($running) # loop forever looking for input to work with
 { # run till ctl-c
  $currentline = getline(); 
  print ("$currentline\n");
  transcurrent ($currentline); # parse line into local vars
  
  $plot->gnuplot_plot_xy( \@x, \@geig1, \@geig10, \@gfil ); # rewrite plot
 

 }




