package RFC::Honeywell;
#$Id: Honeywell.pm,v 1.50 2018/04/03 06:42:24 sqko Exp $
use strict;
use warnings;
use RFC::Main qw();
use RFC::Header;
use RFC::BaseDevice;
use RFC::Cache;
our @ISA = qw(RFC::BaseDevice);

#############################################################################
#
#  Package RFC::Honeywell 
#
# This class implements the RFC::TempControl class for Honeywell temperature 
# controlers.
#
# The RFC::Honeywell module defines the following extra public member functions
#
# $id->P([$])            Sets or gets the proportional gain (Gain)
# $id->I([$])            Sets or gets the integration gain (Reset)
# $id->D([$])            Sets or gets the diffential gain (Rate)
# $id->I_min([$])        Sets or gets the minimum integration limit
# $id->I_max([$])        Sets or gets the maximum integration limit
# $id->stop()            Forces the controler to 'Hold' (if in a program)
# $id->start()           Starts the default program. Used toghethre with
#                        stop() when setting new temperature setpoint
#                        in order to properly use the 'hot start' option
#                        thus awoiding local setpoint 'stickiness'
#                        (basically to make the Honeywell behave as a Eurotherm) 
#
#############################################################################

BEGIN {
    use Exporter ();
    our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
    $VERSION = 1.0;
    %EXPORT_TAGS = (
        'standard'=>[],
                    );
    Exporter::export_ok_tags('standard');
    Exporter::export_tags('standard');
    @ISA = qw(Exporter);
}


sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    $self->{'_type'} = 'Honeywell';
    $self->{'_monitor_rows'} = '3,5,7,9,11';
    $self->{'_setup_tags'} = ['mode','controler_type','tty','address','minramp','maxramp','maxtemp','mintemp','show_plot','use_cache'];
    $self->{'_desc'} = {
        'mode' => 'Device control mode',
        'tty' => 'Communication device',
        'address' => 'Device address',
	'controler_type' => 'Controler type',
	'title' => 'Optional device title',
        'maxtemp' => 'maximum allowed setpoint, not enforced if not set',
        'mintemp' => 'Minimum allowed setpoint, not enforced if not set',
        'maxramp' => 'Maximum allowable ramprate in C/hour (no value means no limit)',
	'minramp' => 'Minimum allowable ramprate in C/hour',
	'use_cache'=>'Determines if persisten caching of read values is allowed',
    };
    $self->{'_default'} = {
        'mode' => 'Automatic',
        'tty' => 'ttyS0',
        'address' => 1,
	'controler_type' => 'Honeywell',
	'use_cache'=>'No',
	'minramp' => 0,
    };
    $self->{'_has_ramp'} = 1;
    $self->{'_set_fn_names'} = ['set_temp','set_ramp']; # List of function names which can trigger a notify event
    $self->{'_values_mode'} = ['Manual','Automatic'];
    $self->{'_values_controler_type'} = [(RFC::Device::list_TEMP())];
    $self->{'_section'} = 'TControl_'.$self->name;
    $self->{'_section'} = 'temperature' if (!$self->rig->config_section_exists($self->sectionname) && $self->name eq 'temperature');
    bless $self,$class;
    foreach my $tag (@{$self->{'_setup_tags'}})
    {
        $self->{'_'.$tag} = $self->get_conf($tag);
        $self->{'_'.$tag} = $self->get_default($tag) unless (defined($self->{'_'.$tag}));
    }
    $self->{'_file'} = SemaforeFile->new($userdir.'rig'.$self->rig->name.'/Honeywell_'.$self->name,'/home/celltest/Gas_rig'.$self->rig->name.'.lock');
    unless ($self->{'_file'}->exist)
    {
        ### Create file with rw for all           ###
        ### Yes I know 666 is the number of evil! ###
        $self->{'_file'}->lock_ex;
        $self->{'_file'}->writeline('');
        chmod 0666, $self->{'_file'}->filename;
        $self->{'_file'}->unlock;
    }
    $self->{'_read_names'} = ['read'];
    $self->{'_set_names'} = ['set_ramp','set_temp'];
    return $self;
}

sub DESTROY
{
    my $self = shift;
    $self->{'_file'}->DESTROY() if ($self->{'_file'});
    return undef;
}

sub read_int
{
    my $self = shift;
    my $addr = $self->{'_address'};
    my $tty = $self->{'_tty'};
    my $tag = shift;
    my $high = int($tag/256);
    my $low = $tag%256;
    my $res = SocketClient::serial_client_raw($tty,'modbus','hc',$addr,3,$high,$low,0,1);
    $self->writedebug("Result of get_int for tag $tag: $res") if ($self->debug > 3);
    if (defined($res) && $res =~ /([\d\.\+\-]+)[\s\t]*\n/)
    {
        return $1;
    }
    return undef;
}

sub write_int
{
    my $self = shift;
    my $addr = $self->{'_address'};
    my $tty = $self->{'_tty'};
    my $tag = shift;
    my $high = int($tag/256);
    my $low = $tag%256;
    my $val = shift;
    my $highv = int($val/256);
    my $lowv = $val%256;
    $self->writedebug("Sending to tag $tag: $addr 6 $high $low $highv $lowv") if ($self->debug > 3);
    my $res = SocketClient::serial_client_raw($tty,'modbus','hc',$addr,6,$high,$low,$highv,$lowv);
    $self->writedebug("Result of write_int for tag $tag: $res") if ($self->debug > 3);
    return $res if ($res && $res =~ /error/i);
    return undef;
}

sub hex_to_int
{
    my $self = shift;
    my $high = shift;
    my $low = shift;
    my $val = hex($high) * 256 + hex($low);
    return $val;
}

sub read_float
{
    my $self = shift;
    my $addr = $self->{'_address'};
    my $tty = $self->{'_tty'};
    my $tag = shift;
    my $high = int($tag/256);
    my $low = $tag%256;
    my $res = SocketClient::serial_client_raw($tty,'modbus','hc',$addr,3,$high,$low,0,2);
    $self->writedebug("Result of get_float for tag $tag: $res") if ($self->debug > 3);
    if (defined($res) && $res =~ /([\d\.\+\-]+)[\s\t]*\n/)
    {
        return $1;
    }
    return undef;
}

sub write_float
{
    my $self = shift;
    my $addr = $self->{'_address'};
    my $tty = $self->{'_tty'};
    my $tag = shift;
    my $high = int($tag/256);
    my $low = $tag%256;
    my @val = $self->ieefloat(shift);
    $self->writedebug("Sending to tag $tag: $addr 16 $high $low 0 2 4".join(' ',@val)) if ($self->debug > 3);
    my $res = SocketClient::serial_client_raw($tty,'modbus','hc',$addr,16,$high,$low,0,2,4,@val);
    $self->writedebug("Result of write_float for tag $tag: $res") if ($self->debug > 3);
    return $res if ($res && $res =~ /error/i);
    return undef;
}


sub read_segment_nr
{
    my $self = shift;
    my $nr = shift;
    $nr = 1 unless ($nr);
    my $offset = $self->hex_to_int("28","00") + ($nr -1)*8;
    my $segment = $self->read_int($offset);
    my $event = $self->read_int($offset+1);
    my $ramp = $self->read_float($offset+2);
    my $value = $self->read_float($offset+4);
    my $aux = $self->read_float($offset+6);
    return ($segment,$event,$ramp,$value,$aux);
}

sub ramp_enable
{
    my $self = shift;
    my $addr = $self->{'_address'};
    my $tty = $self->{'_tty'};
    if (@_)
    {
        my $val = shift;
        my @res;
        if ($val)
        {
            @res = SocketClient::serial_client_raw($tty,'modbus','hc',$addr,21,9,6,0,3,0,150,0,1,0,1);
        }
        else
        {
            @res = SocketClient::serial_client_raw($tty,'modbus','hc',$addr,21,9,6,0,3,0,150,0,1,0,0);
        }
        select(undef,undef,undef,0.2); ## Allow controler to figure it out ###
    }
    my $p = SocketClient::serial_client_raw($tty,'modbus','h',$addr,20,7,6,0,3,0,150,0,1);
    $self->writedebug("Result of SP-Ramp enable: $p") if ($self->debug > 3);
    select(undef,undef,undef,0.2); ## Allow controler to figure it out ###                                                                               
    if (defined($p) && $p =~ /([\d\.\+\-]+)[\s\t]*\n/)
    {
        return $1+0;
    }
    return undef;
}


sub set_segment_ramp
{
    my $self = shift;
    my $nr = shift;
    $nr = 1 unless ($nr);
    my $offset = $self->hex_to_int("28","00") + ($nr -1)*8;
    my $ramp = shift;
    my $res = $self->write_float($offset+2,$ramp);
    $self->writedebug("ramp set error: '$res'") if ($res);
    select(undef,undef,undef,0.2);
    return $self->read_segment_nr($nr);
}

sub set_segment_temp
{
    my $self = shift;
    my $nr = shift;
    $nr = 1 unless ($nr);
    my $offset = $self->hex_to_int("28","00") + ($nr -1)*8;
    my $temp = shift;
    my $res = $self->write_float($offset+4,$temp);
    $self->writedebug("Temp set error: '$res'") if ($res);
    select(undef,undef,undef,0.2);
    return $self->read_segment_nr($nr);
}

sub stop
{
    my $self = shift;
    ## Hold ##
    my $res = $self->write_int(7693,0);
    $self->writedebug("Hold error: '$res'") if ($res);
    sleep(1);
    ## reset ##
    $res = $self->write_int(7695,0);
    $self->writedebug("Reset error: '$res'") if ($res);
    return 0;
}

sub start
{
    my $self = shift;
    $self->stop();
    sleep(1);
    my $res = $self->write_int(7692,1);
    $self->writedebug("Start error: '$res'") if ($res);
    return 1;
}

sub monitor()
{
    my $self = shift;
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    my $addr = $self->{'_address'};
    my $tty = $self->{'_tty'};
    my $p = SocketClient::serial_client_raw($tty,'modbus','h',$addr,20,7,6,0,3,0,122,0,2);
    $self->writedebug("Result of get PV,SP,Output: $p") if ($self->debug > 3);
    my $aset;
    my $temp;
    my $pow;
    my $val = $self->get_temp($data);
    my $ramp = $self->get_ramp($data);
    if ($p)
    {
        my @arr = split(/[\s\t\n\r]+/,$p);
        $pow = $arr[-1];
        $aset = $arr[-2];
        $temp = $arr[-3];
    }
    foreach ($val,$aset,$ramp,$pow,$temp)
    {
        $_ = -32768 unless defined($_);
    }
    return join(' ','Setpoint',$val,'Active_temp',$temp,'Ramprate',$ramp,'Local_setpoint',$aset,'Output_power',$pow);
}

sub P
{
    my $self = shift;
    my $addr = $self->{'_address'};
    my $tty = $self->{'_tty'};
    if (@_)
    {
        my $p = shift;
        $self->writedebug("Setting new Gain to '$p'") if ($self->debug > 3);
        my @val = $self->ieefloat($p);
        my @res = SocketClient::serial_client_raw($tty,'modbus','h',$addr,21,11,6,0,3,0,1,0,2,@val);
        select(undef,undef,undef,0.2); ## Allow controler to figure it out ###
    }
    my $p = SocketClient::serial_client_raw($tty,'modbus','h',$addr,20,7,6,0,3,0,1,0,2);
    $self->writedebug("Result of P(): $p") if ($self->debug > 3);
    if (defined($p) && $p =~ /([\d\.\+\-]+)[\s\t]*\n/)
    {
        return $1;
    }
    return undef;
}

sub I
{
    my $self = shift;
    my $addr = $self->{'_address'};
    my $tty = $self->{'_tty'};
    if (@_)
    {
        my $p = shift;
        $self->writedebug("Setting new Reset to '$p'") if ($self->debug > 3);
        my @val = $self->ieefloat($p);
        my @res = SocketClient::serial_client_raw($tty,'modbus','h',$addr,21,11,6,0,3,0,3,0,2,@val);
        select(undef,undef,undef,0.2); ## Allow controler to figure it out ###
    }
    my $p = SocketClient::serial_client_raw($tty,'modbus','h',$addr,20,7,6,0,3,0,3,0,2);
    $self->writedebug("Result of I(): $p") if ($self->debug > 3);
    if (defined($p) && $p =~ /([\d\.\+\-]+)[\s\t]*\n/)
    {
        return $1;
    }
    return undef;
}

sub D
{
    my $self = shift;
    my $addr = $self->{'_address'};
    my $tty = $self->{'_tty'};
    if (@_)
    {
        my $p = shift;
        $self->writedebug("Setting new Rate to '$p'") if ($self->debug > 3);
        my @val = $self->ieefloat($p);
        my @res = SocketClient::serial_client_raw($tty,'modbus','h',$addr,21,11,6,0,3,0,2,0,2,@val);
        select(undef,undef,undef,0.2); ## Allow controler to figure it out ###
    }
    my $p = SocketClient::serial_client_raw($tty,'modbus','h',$addr,20,7,6,0,3,0,2,0,2);
    $self->writedebug("Result of D(): $p") if ($self->debug > 3);
    if (defined($p) && $p =~ /([\d\.\+\-]+)[\s\t]*\n/)
    {
        return $1;
    }
    return undef;
}

sub I_max
{
    my $self = shift;
    my $addr = $self->{'_address'};
    my $tty = $self->{'_tty'};
    if (@_)
    {
        my $p = shift;
        $self->writedebug("Setting new high Integraal Limit to '$p'") if ($self->debug > 3);
        my @val = $self->ieefloat($p);
        my @res = SocketClient::serial_client_raw($tty,'modbus','h',$addr,21,11,6,0,3,0,16,0,2,@val);
        select(undef,undef,undef,0.2); ## Allow controler to figure it out ###
    }
    my $p = SocketClient::serial_client_raw($tty,'modbus','h',$addr,20,7,6,0,3,0,16,0,2);
    $self->writedebug("Result of High Integral Limit(): $p") if ($self->debug > 3);
    if (defined($p) && $p =~ /([\d\.\+\-]+)[\s\t]*\n/)
    {
        return $1;
    }
    return undef;
}

sub I_min
{
    my $self = shift;
    my $addr = $self->{'_address'};
    my $tty = $self->{'_tty'};
    if (@_)
    {
        my $p = shift;
        $self->writedebug("Setting new Low Integraal Limit to '$p'") if ($self->debug > 3);
        my @val = $self->ieefloat($p);
        my @res = SocketClient::serial_client_raw($tty,'modbus','h',$addr,21,11,6,0,3,0,17,0,2,@val);
        select(undef,undef,undef,0.2); ## Allow controler to figure it out ###
    }
    my $p = SocketClient::serial_client_raw($tty,'modbus','h',$addr,20,7,6,0,3,0,17,0,2);
    $self->writedebug("Result of Low Integral Limit(): $p") if ($self->debug > 3);
    if (defined($p) && $p =~ /([\d\.\+\-]+)[\s\t]*\n/)
    {
        return $1;
    }
    return undef;
}


sub print_config()
{
    my $self = shift;
    my @arr = ();
    push @arr,'Name: '.$self->name;
    push @arr,'Type: '.$self->type;
    push @arr,'Mode: '.$self->mode;
    push @arr,'Tty: '.$self->{'_tty'};
    push @arr,'Address: '.$self->{'_address'};
    return join(', ',@arr);    
}

sub get_temp()
{
    my $self = shift;
    my $temp = 0;
    my @lines = $self->{'_file'}->readlines;
    $temp = $lines[0] if (@lines);
    return $temp;
}

sub read_nocache()
{
    my $self = shift;
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
        $data = RFC::Cache->new();
        $data->debug($self->debug()) if ($self->debug);
    }
    $self->check_cache($data);
    my $addr = $self->{'_address'};
    my $tty = $self->{'_tty'};
    my $cached = $data->get($tty,'modbus',$addr,'temp');
    return $cached if (defined($cached));
    my $temp = SocketClient::serial_client_raw($tty,'modbus','hc',$addr,3,0,226,0,2);
    if (defined($temp) && $temp =~ /error/i)
    {
	$self->writedebug("Error getting temp: $temp",1);
	return undef;
    }
    $self->writedebug("Result of get temp: $temp") if ($self->debug > 3);
    if (defined($temp) && $temp =~ /([\d\.\+\-]+)[\s\t]*\n/)
    {
	$temp = $1;
    }
    else
    {
	$temp = -32768 unless defined($temp);
    }
    $data->set($tty,'modbus',$addr,'temp',$temp);
    $self->set_cache($temp);
    $self->{'_last_read'} = Time::HiRes::time;
    return $temp;
}

sub get_ramp()
{
    my $self = shift;
    return $self->get_ramp_up();
}

sub get_ramp_down()
{
    my $self = shift;
    my $addr = $self->{'_address'};
    my $tty = $self->{'_tty'};
    my $ramp = SocketClient::serial_client_raw($tty,'modbus','h',$addr,20,7,6,0,3,0,109,0,2);
    $self->writedebug("Result of get ramp down: $ramp") if ($self->debug > 3);
    if (defined($ramp) && $ramp =~ /([\d\.\+\-]+)[\s\t]*\n/)
    {
	$ramp = $1;
    }
    else
    {
	$ramp = -32768 unless defined($ramp);
    }
    return $ramp;
}

sub get_ramp_up()
{
    my $self = shift;
    my $addr = $self->{'_address'};
    my $tty = $self->{'_tty'};
    my $ramp = SocketClient::serial_client_raw($tty,'modbus','h',$addr,20,7,6,0,3,0,108,0,2);
    $self->writedebug("Result of get ramp up: $ramp") if ($self->debug > 3);
    if (defined($ramp) && $ramp =~ /([\d\.\+\-]+)[\s\t]*\n/)
    {
	$ramp = $1;
    }
    else
    {
	$ramp = -32768 unless defined($ramp);
    }
    return $ramp;
}

sub read_output()
{
    my $self = shift;
    my $addr = $self->{'_address'};
    my $tty = $self->{'_tty'};
    return SocketClient::serial_client_raw($tty,'modbus','h',$addr,20,7,6,0,3,0,122,0,2);
}


sub mintemp()
{
    my $self = shift;
    my $min = -273; ## Well no need to break the universe...                                                                    
    $min = $self->{'_mintemp'} if (defined($self->{'_mintemp'}) && $self->{'_mintemp'});
    return $min;
}

sub maxtemp()
{
    my $self = shift;
    my $max = 6000; ## No need to go higher than the surface of the sun...                                                      
    $max = $self->{'_maxtemp'} if (defined($self->{'_maxtemp'}) && $self->{'_maxtemp'});
    return $max;
}


sub minramp()
{
    my $self = shift;
    my $min = 0;
    $min = $self->{'_minramp'} if (defined($self->{'_minramp'}) && $self->{'_minramp'});
    return $min;
}

sub maxramp()
{
    my $self = shift;
    my $max = 1e100;
    $max = $self->{'_maxramp'} if (defined($self->{'_maxramp'}) && $self->{'_maxramp'});
    return $max;
}

sub check_ramp
{
    my $self = shift;
    my $ramp = shift;
    my $user = shift;
    if ($ramp > $self->maxramp())
    {
        $ramp = $self->maxramp();
        unless (@_)
        {
            if ($user)
            {
                $self->rig->errorlog("Warning: Temperature ramp for $self was to be set above maximum ($ramp C/hour)!",$user);
            }
            else
            {
                $self->rig->errorlog("Warning: Temperature ramp for $self was to be set above maximum ($ramp C/hour)!");
            }
        }
    }
    if ($ramp < $self->minramp())
    {
        $ramp = $self->minramp();
        unless (@_)
        {
            if ($user)
            {
                $self->rig->errorlog("Warning: Temperature ramp for $self was to be set below minimum ($ramp C/hour)!",$user);
            }
            else
            {
                $self->rig->errorlog("Warning: Temperature ramp for $self was to be set below minimum ($ramp C/hour)!");
            }
        }
    }
    return $ramp;
}



sub set_ramp
{
    my $self = shift;
    $self->ramp_enable(0);
    my $ramp = $self->check_ramp(@_);
    $self->set_ramp_up($ramp);
    select(undef,undef,undef,0.2); ## Allow controler to figure it out ###                                                                                           
    $self->set_ramp_down($ramp);
    $self->ramp_enable(1);
    $self->notify('set_ramp',$ramp,@_);
    $self->proglog("Temperature ramp for ".$self->name." set to $ramp C/hour",@_);
    return $ramp;
}

sub set_ramp_up()
{
    my $self = shift;
    my $ramp = shift;
    $ramp = $ramp;
    my $addr = $self->{'_address'};
    my $tty = $self->{'_tty'};
    my @val = $self->ieefloat($ramp);
    my @res = SocketClient::serial_client_raw($tty,'modbus','h',$addr,21,11,6,0,3,0,108,0,2,@val);
    select(undef,undef,undef,0.2); ## Allow controler to figure it out ###
    $self->writedebug("Result of set ramp up: ".join("\n",@res)) if ($self->debug > 3);
    if (@res)
    {
	return $self->unp(split(/[\s\t]/,$res[0]));
    }
    else
    {
	$self->proglog("Error: Temperature ramp up for ".$self->name." could not be set to $ramp C/hour",@_);
    }
    return $ramp;
}

sub set_ramp_down()
{
    my $self = shift;
    my $ramp = shift;
    $ramp = $ramp;
    my $addr = $self->{'_address'};
    my $tty = $self->{'_tty'};
    my @val = $self->ieefloat($ramp);
    my @res = SocketClient::serial_client_raw($tty,'modbus','h',$addr,21,11,6,0,3,0,109,0,2,@val);
    select(undef,undef,undef,0.2); ## Allow controler to figure it out ###
    $self->writedebug("Result of set ramp down: ".join("\n",@res)) if ($self->debug > 3);
    if (@res)
    {
	return $self->unp(split(/[\s\t]/,$res[0]));
    }
    else
    {
	$self->proglog("Error: Temperature ramp down for ".$self->name." could not be set to $ramp C/hour",@_);
    }
    return $ramp;
}


sub set_temp
{
    my $self = shift;
    $self->start();
    sleep(1);
    $self->stop();
    sleep(1);
    $self->ramp_enable(0);
    select(undef,undef,undef,0.5);
    my $temp = $self->set_temp_value(@_);
    select(undef,undef,undef,0.5);
    $self->ramp_enable(1);
    return $temp;
}

sub set_temp_value
{
    my $self = shift;
    my $temp = shift;
    my $min = $self->{'_mintemp'};
    my $max = $self->{'_maxtemp'};
    if (defined($max) && $temp > $max)
    {
        $temp = $max;
        $self->proglog("Temperature was to be set above maximum of $max C",@_);
    }
    if (defined($min) && $temp < $min)
    {
        $temp = $min;
        $self->proglog("Temperature was to be set belov minimum of $min C",@_);
    }
    if ($self->mode =~ /auto/i)
    {
        my $addr = $self->{'_address'};
        my $tty = $self->{'_tty'};
        my @val = $self->ieefloat($temp);
        my $res = SocketClient::serial_client_raw($tty,'modbus','hc',$addr,16,0,226,0,2,4,@val);
        if (!$res || $res =~ /error/i)
        {
	    $self->proglog("Error: Could not set temperature for ".$self->name.', Result: '.$res);
        }
        select(undef,undef,undef,0.2); ## Allow controler to figure it out ###
        $self->writedebug("Result of set temp: $res") if ($self->debug > 3);
	$self->proglog("Temperature for ".$self->name." set to $temp C",@_);
        $self->{'_file'}->writeline($temp);
        $self->notify('set_temp',$temp,@_);
        return $temp;
    }
    $self->proglog("Temperature for ".$self->name." set manually to $temp C",@_);
    $self->notify('set_temp',$temp,@_);
    return $temp;
}


sub test()
{
    my $self = shift;
    my $res = $self->read();
    return 1 if defined($res);
    return 0;
}

sub test_string()
{
    my $self = shift;
    my $temp = $self->get_temp();
    chomp $temp;
    my $ramp = $self->get_ramp();
    chomp $ramp;
    my $res = $self->read();
    return "Temperature controler ".$self->name.", Setpoint: $temp C, ramprate $ramp C/hour, active_setpoint: $res C";
}


sub ieefloat($)
{
    my $self = shift;
    my $fv = shift;
    my $ss =  pack( "f", $fv );
    my @res = ();
    for ( 0..length($ss) - 1  ) { 
	push @res, ord( substr( $ss, 3 - $_, 1 ) );
    }
    return @res;
}

sub unp(@)
{
    my $self = shift;
    my @input = @_;
    $self->writedebug(join(' ',@input)) if ($self->debug > 3);
    $self->die("Not enough arguments for unp()") unless (@input > 12);
    my @vals = @input[10..13];
    my $s = '';
    foreach (@vals)
    {
	$s .= chr($_);
    }
    my $f = sprintf "%16.8f", unpack( "f", reverse($s));
    return $f;
}

return 1;
