.nminterp.spline
author A.M. Thurnherr <athurnherr@yahoo.com>
Mon, 13 Apr 2020 10:52:46 -0400
changeset 39 56bdfe65a697
permissions -rw-r--r--
.

#======================================================================
#                    . N M I N T E R P . S P L I N E 
#                    doc: Wed Nov 22 21:01:09 2000
#                    dlm: Fri Sep 23 16:52:31 2011
#                    (c) 2000 A.M. Thurnherr
#                    uE-Info: 19 50 NIL 0 0 72 2 2 4 NIL ofnI
#======================================================================

# spline interpolation for [fillgaps]

# HISTORY:
#	Apr  3, 2006: - created from [.interp.spline]
#	Jul 28, 2006: - made to work
#	Jul 30, 2006: - added max-gap support
#				  - BUG: extended interpolation into last interval
#				  - BUG: assertions made it incompatible with decreasing data
#	Aug  8, 2008: - renamed
#	Sep 23, 2011: - removed $xv from interpolate() args
#				  - added error when xf<0 (%RECNO)

#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#
# THE FOLLOWING VARIABLES MUST BE SET GLOBALLY (i.e. during loading)
#
#	$ISOpts				string of allowed options
#	$ISOptsUsage		usage information string for options
#
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

$ISOpts = '';
$ISOptsUsage = '';

#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#
# &ISUsage() 	mangle parameters (options, really)
#
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

sub ISUsage() {}

#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#
# &ISInit(f,xf)				init interpolation of field f
#       f					field number
#		xf					x field number
#       <ret val>           none
#
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

my(@y2);

sub ISInit($$) {
	my($f,$xf) = @_;
	my($k,$i,$p,$qn,$sig,$un,@u);

	croak(".nminterp.spline: cannot use %RECNO as x-field (implementation restriction)\n")
		unless ($xf >= 0);

	my($pi,$ppi,$li);				# find idx of first 3 & last valid recs
	for (my($r)=0; $r<=$#ants_; $r++) {
		if (numberp($ants_[$r][$f])) {
			unless (defined($ppi)) {
				$ppi = $pi; $pi = $i; $i = $r;
			}
			$li = $r;
		}
	}
	unless (defined($ppi)) {
		&antsInfo("WARNING: not enough <$antsLayout[$f]> data for spline interpolation");
		return;
	}
		

	my($fpi) = $pi;
	$y2[$pi][$f] = $u[$pi] = 0;

	for (; $i<=$li; $i++) {
		next unless numberp($ants_[$i][$f]);
		
		$sig = ($ants_[$pi][$xf]-$ants_[$ppi][$xf]) /
				($ants_[$i][$xf]-$ants_[$ppi][$xf]);
		$p = $sig*$y2[$pi][$f] + 2;
		$y2[$i][$f] = ($sig-1)/$p;
		$u[$i] = ($ants_[$i][$f]-$ants_[$pi][$f]) /
					($ants_[$i][$xf]-$ants_[$pi][$xf])
						- ($ants_[$pi][$f]-$ants_[$ppi][$f]) /
							($ants_[$pi][$xf]-$ants_[$ppi][$xf]);
		$u[$i] = (6*$u[$i]/($ants_[$i][$xf]-$ants_[$ppi][$xf]) -
					$sig * $u[$pi]) / $p;

		$ppi = $pi; $pi = $i;
	}

	$qn = $un = 0;

	$y2[$li][$f] = ($un-$qn*$u[$pi]) / ($qn*$y2[$pi][$f]+1);
	my($pk) = $li;
	for ($k=$pi; $k>=$fpi; $k--) {
		next unless numberp($ants_[$k][$f]);
		$y2[$k][$f] = $y2[$k][$f] * $y2[$pk][$f] + $u[$k];
		$pk = $k;
	}
}

#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#
# &interpolate(xf,mg,f,lvr,cr)	interpolate field f
#		xf							x field
#		mg							max allowed x gap
#		f							field number to interpolate
#		lvr							last record with valid y val
#		cr							current record
#		<ret val>					interpolated value
#
# NB:
#	- handle f == xf
#	- return undef if interpolation cannot be carried out
#	- x VALUES MAY NOT BE MONOTONIC
#
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

sub interpolate($$$$$$)
{
	my($xf,$mg,$f,$lvr,$cr) = @_;
	
	return $ants_[$cr][$xf] if ($xf == $f);

	return $ants_[$lvr][$f]							# edge values are ok
		if equal($ants_[$cr][$xf],$ants_[$lvr][$xf]);

	my($nvr1) = $cr + 1;							# find next
	while ($nvr1 <= $#y2 && !numberp($y2[$nvr1][$f])) { $nvr1++; }
	return undef									# none or gap too large
		if ($nvr1>$#ants_ || abs($ants_[$nvr1][$xf]-$ants_[$lvr][$xf])>$mg);
	return $ants_[$nvr1][$f]						# edge values are ok
		if equal($ants_[$cr][$xf],$ants_[$nvr1][$xf]);

	my($nvr2) = $nvr1 + 1;
	while ($nvr2 <= $#y2 && !numberp($y2[$nvr2][$f])) { $nvr2++; }

	my($h) = $ants_[$nvr1][$xf] - $ants_[$lvr][$xf];
	my($a) = ($ants_[$nvr1][$xf] - $ants_[$cr][$xf]) / $h;
	my($b) = ($ants_[$cr][$xf] - $ants_[$lvr][$xf]) / $h;

	return $a*$ants_[$lvr][$f] + $b*$ants_[$nvr1][$f] +
			(($a*$a*$a-$a) * $y2[$nvr1][$f] +
			 ($b*$b*$b-$b) * $y2[$nvr2][$f]) *
			 	($h*$h)/6;
}

#----------------------------------------------------------------------

1;