#!/usr/bin/perl -w

# program to suck up and store 
# spectra and other data from the GR 130 to ~/GR130/date.type

# TODO...well, many things.  Put this all in a database and be able
# to plot that (perhaps from another script).

# To use:  Run this from a terminal and then tell the unit to dump
# its data memory.  When done, this will type a message to hit
# <CR> to quit.  Various errors are printed if it can't find the 
# unit and so on.  Note the path to the USB-serial dongle is hardcoded
# and you will have to change it for yours.  It will then assume a
# GR-130 is the only thing connected to that dongle.
# Change that location in findspec(), the only place it is used.

# strict pragma and modules we use.
use strict;
use Device::SerialPort;
use File::HomeDir;
#globals
my $datadir; # will be filled with user's dir plus /GR130 later
my $port; # serial port object
my $debug =0; # verbosity while we get it working, 0 for none

my $rawread; # each chunk read
my $rawdata; # place to put them all
my $filedata;
my @filesraw; # data for each dumped file
my @filesZ; # place to put the Z's back into after split
my %header; # file header for each file in dump
my $numfiles;

###################### Subroutines #####################################
sub findSpec ()
{ # returns the filepath for the FTDI for GR 130 we find under /dev/serial/by-id
 my $apath;
 
 opendir(DIR,'/dev/serial/by-id') or die "No USB serial at all: $!\n";
 while (defined($apath = readdir(DIR)))
 { # match our usb serial dongle?
  if ($apath =~ m/usb-ftdi_usb_serial_converter_ftDXIAYU-if00-port0/) 
   { #@@@ note hardcoded id for serial converter -must change for each one
    $apath = '/dev/serial/by-id/' . $apath; #concatenate
    print "apath:$apath\n" if $debug; # debug
    closedir(DIR); # be nice and clean
    return $apath;
   } 
 }
 die "No GR 130 found - we can't get data if $apath isn't connected!";
}

################################################################################
sub setupSerial
{
	my $status;
	my $portpath = findSpec(); # get path to desired port
     $port = new Device::SerialPort ($portpath, 1) || die "Can't open $portpath: $!\n";
     $port->databits(8);
     $port->baudrate(9600); # GR 130 is 9.6kb?
     $port->parity("none");
     $port->stopbits(1);
     $port->handshake("none");
     $port->datatype('raw');
     $port->stty_icanon(0); #wird format, mixed ascii and binary
     $port->are_match("\n");# possible end strings, not used yet here
     $port->lookclear;      # empty the buffer
     $port->read_char_time(10);    #  wait for each character - a little
     $port->read_const_time(1000); #  milliseconds per unfulfilled "read" call
     $port->write_settings or undef $port; # die, sort of, if not working
     die "no port at $portpath: $!" unless $port;  # die, really 
     $status = $port->can_status;
     print "port can_status: $status\n" if $debug; # automatically detected
     sleep 1;  # give this time
 # should now have a port open to the device we wanted.    	
}
################################################################################
sub getrawdata
{
  my $bytecount = 0;	
  while (0 == $bytecount) # wait for a spectra
  {
   (undef,$bytecount,undef,undef) = $port->status();
   sleep 1;	# there are probably more coming
   print "."; # show we are alive and waiting
  }
  
  while ($bytecount)
  {
   (undef,$bytecount,undef,undef) = $port->status(); # more while we slept?
   ($bytecount,$rawread) = $port->read($bytecount);
    $rawdata .= $rawread; # add in more data
    sleep 1;  # there might be more on the way	
    (undef,$bytecount,undef,undef) = $port->status(); #update loop condition
     print "." if ($bytecount); # show we are alive and waiting
  }
  print "\n";
}
################################################################################
sub splitdata
{ # split raw data into chunks, named by timestamp, ext by type, into our dir
 @filesraw = ();
 @filesZ = ();	 
 @filesraw = split /ZZZZ/, $rawdata;
 shift @filesraw; # first one is blank due to ZZZZ at start
 $numfiles = scalar @filesraw;
 foreach $filedata (@filesraw)
 {	
  $filedata = "ZZZZ" . $filedata; # put the Z'a back so it's same as read
  push @filesZ, $filedata;	# create array of files' data
 }	
	
}
################################################################################
sub writefiles
{ 
my $rawport;
my $filename;
	
 foreach $rawport (@filesZ) 
  { #unpack the header for file naming
	$header{length} = unpack("x4S", $rawport); 	
	$header{type} = unpack ("x6a", $rawport); 
	$header{year} = unpack("x7H2",$rawport) + 2000;	#@@@ a y2100 bug
	$header{month} = unpack("x8H2",$rawport);
	$header{day} = unpack("x9H2",$rawport);
	$header{hour} = unpack("x10H2",$rawport);
	$header{minute} = unpack("x11H2",$rawport);
	$header{second} = unpack("x12H2",$rawport);
	$header{clock_time} = unpack("x14S",$rawport);
	
	$header{volt} = unpack("x13c",$rawport)/10;
	$header{FS} = (unpack("x16c",$rawport)) ? "3.0 MeV" : "1.5 MeV";
	$header{CsPeak} = unpack("x18S",$rawport)/10;
	$header{doseunit} = unpack("x22a",$rawport);
#TODO - rest of header for later uses
	 	
	$filename = "$datadir" . '/' . $header{year} . '_' . $header{month}
	 . '_' . $header{day} . '_' . $header{hour} . $header{minute} . 
	'.' . $header{type}; # time as name and type as extension
	print "writing file:$filename\n" if $debug; 	
	open (OUT, ">$filename") or die "Couldn't open $filename for writing:$!\n";
	print (OUT $rawport) or die "Couldn't write $filename: $!\n"; 
	close OUT or die "couldn't close $filename:$!\n";	
  } # end while files	
} 
################################################################################
sub printrawjunk
{
 foreach $filedata (@filesZ)
 {
  print "*************************new file******************************\n";
  print $filedata;
  print "\n\n";
 }	
}
################################################################################
sub getdatadir
{
 $datadir = File::HomeDir->my_home;
 $datadir .= "/GR130";
 
 print "datadir:$datadir\n" if $debug;	
	
 # make directory if not exist already
 unless (-d $datadir)
{
 mkdir $datadir or die "Couldn't make $datadir, $!\n";
} 	
}
################################################################################


########################################################################
########################## main code ***********************************
########################################################################
getdatadir(); # find out or create where we'll write files
# set autoflush for dot output while waiting for upload
$| = 1;
setupSerial(); # get connected
print "Waiting: send me data and I'll put it in files!\n";
getrawdata();
splitdata();
writefiles();
printrawjunk() if ($debug > 1);
print "numfiles:$numfiles\n";
my $len = length $rawdata;
print "raw bytecount: $len\n" if $debug;
print "Hit <CR> to exit\n";
my $dummy = <>;
