#!/usr/bin/perl
#
# Data Acquisition Tool for the V&A VA18B Multimeter
#
# Copyright (C) 2009  Martin Schewe  <multimeter@schewe.com>
#
# You always find the most recent version on http://multimeter.schewe.com.
# $Id: multimeter 9 2009-05-05 16:20:36Z ms $
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#




# modprobe spcp8x5 !!!
#chmod go+rwx /dev/ttyUSB0 !!
# nebo KERNEL=="ttyUSB*", MODE="0660", GROUP="users"

use POSIX;
use Time::HiRes qw(gettimeofday);


#
# Set terminal parameters of the TTY that the optocoupler is connected to
#
sub init_tty {
	my $fd = fileno(shift);
	my $termios = POSIX::Termios->new();

	$termios->getattr($fd) or die "Can't get terminal parameters for $dev";
	$termios->setispeed(B2400);
	$termios->setiflag(0);
	$termios->setlflag(0);
	$termios->setattr($fd, TCSANOW);
}


#
# Read next (complete) binary string from the optocoupler
#
sub next_bin_str {
	my $bin_str;
	while (!eof(USB_TTY) and length($bin_str) != 14 * 4) {
		my $byte = getc(USB_TTY);
		my $nibble_number = ord($byte) >> 4 & 0xf;
		
		$bin_str .= substr(unpack("B*", $byte), 4);
		length($bin_str) == $nibble_number * 4 or $bin_str = "";
	}
	$bin_str;
}


#
# Decode the measured data from the binary string
#
sub decode_bin_str {
	my ($AC, $DC, $auto, $unknown1,
	    $minus, $digi1, $dot1, $digi2, $dot2, $digi3, $dot3, $digi4,
	    $micro, $unknown2, $kilo, $diode_test,
	    $milli, $percent, $mega, $cont_check,
	    $unknown3, $ohm, $rel, $hold,
	    $amp, $volt, $hz, $unknown4,
	    $min, $unknown5, $celsius, $max) = shift =~
    	   /^(.)(.)(.)(.)(.)(.{7})(.)(.{7})(.)(.{7})(.)(.{7})
	    (.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.) *$/x;
	
	my %digi = (
		"1111101" => 0,
		"0000101" => 1,
		"1011011" => 2,
		"0011111" => 3,
		"0100111" => 4,
		"0111110" => 5,
		"1111110" => 6,
		"0010101" => 7,
		"1111111" => 8,
		"0111111" => 9,
	);

	my $val = ($minus ? "-" : "") . $digi{$digi1} . ($dot1 ? "." : "") .
					$digi{$digi2} . ($dot2 ? "." : "") .
					$digi{$digi3} . ($dot3 ? "." : "") .
					$digi{$digi4};
	
	my $flags = join(" ", $AC         ? "AC"         : (),
			      $DC         ? "DC"         : (),
			      $auto       ? "auto"       : (),
			      $diode_test ? "diode_test" : (),
			      $cont_check ? "cont_check" : (),
			      $rel        ? "rel"        : (),
			      $hold       ? "hold"       : (),
			      $min        ? "min"        : (),
			      $max        ? "max"        : ());
	
	my $unit = ($micro   ? "u"   : "") .
		   ($kilo    ? "k"   : "") .
		   ($milli   ? "m"   : "") .
		   ($mega    ? "M"   : "") .
		   ($percent ? "%"   : "") .
		   ($ohm     ? "Ohm" : "") .
		   ($amp     ? "A"   : "") .
		   ($volt    ? "V"   : "") .
		   ($hz      ? "Hz"  : "") .
		   ($celsius ? "C"   : "");

	$val, $flags, $unit;
}


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


$dev = shift || "/dev/ttyUSB4";
open USB_TTY, $dev or die "Can't open $dev";
init_tty(\*USB_TTY);

# force immediate flushing to stdout
$| = 1;

my $last_timestamp;

while ($bin_str = next_bin_str) {
	my ($val, $flags, $unit) = decode_bin_str($bin_str);

	$last_timestamp ||= gettimeofday;
	my $timestamp = gettimeofday;

	printf "%7.2f\t%9.3f\t%s\t%s\n", $timestamp - $last_timestamp,
					 $val, $unit, $flags;
	# $last_timestamp = $timestamp;
}
