aboutsummaryrefslogtreecommitdiff
path: root/pkg/utilities/nttools/trebin/tuival.x
blob: 20354a5d5a8bc099d7f52a43923a2bc244fa3bb6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
include "trebin.h"

# tuival -- interpolate
# Interpolate to get one value.
# A zero value of iv_step means that the output independent-variable values
# may not be uniformly spaced, so the interval x1,x2 must be gotten from
# the xout array.  (This is relevant for linear interpolation only.)
#
# Phil Hodge, 18-Apr-1988  Subroutine created
# Phil Hodge, 20-May-1996  Include extrapolate and ext_value for extrapolation.
# Phil Hodge, 29-Jul-1998  For 1-D, fit a line instead of interpolating,
#			add iv_step to calling sequence, add subroutines
#			tu_range and tu_fit1.
# Phil Hodge, 22-Apr-1999  The test on whether x is outside the range of
#		the data did not consider that xa could be decreasing.
# Phil Hodge, 25-Apr-2000  Change calling sequence:  x --> xout, j, outrows;
#		this is to support the option of a nonuniformly spaced output
#		independent variable array.

procedure tuival (i_func, xa, ya, y2, n,
	extrapolate, ext_value, xout, j, outrows, iv_step, y)

int	i_func			# i: interpolation function code
double	xa[n]			# i: input independent-variable values
double	ya[n]			# i: input dependent-variable values
double	y2[n]			# i: used only by spline interpolation
int	n			# i: size of xa, ya, y2 arrays
bool	extrapolate		# i: extrapolate rather than use ext_value?
double	ext_value		# i: value to assign if x is out of bounds
double	xout[ARB]		# i: output independent variable array
int	j			# i: current element of xout
int	outrows			# i: size of xout array
double	iv_step			# i: spacing of x values (can be negative)
double	y			# o: the interpolated value
#--
double	x			# independent variable
double	x1, x2			# beginning and end of output pixel
double	dy			# an estimate of the error of interpolation
int	klo, khi		# indexes within xa that bracket x
int	npts			# number of points in interval

begin
	x = xout[j]

	# Initial guess for klo; we want xa[klo] <= x <= xa[klo+1].
	klo = (n + 1) / 2
	khi = klo

	# If x falls outside the range of the data, either extrapolate
	# (below) or assign a fixed value ext_value.
	if (!extrapolate && n > 1) {
	    if (xa[1] < xa[2] && (x < xa[1] || x > xa[n])) {	# increasing
		y = ext_value
		return
	    }
	    if (xa[1] > xa[2] && (x > xa[1] || x < xa[n])) {	# decreasing
		y = ext_value
		return
	    }
	}

	switch (i_func) {
	case I_NEAREST:

	    # Find klo such that xa[klo] <= x <= xa[klo+1], starting from
	    # current klo.
	    call tuhunt (xa, n, x, klo)
	    klo = max (klo, 1)
	    klo = min (klo, n)
	    khi = min (klo+1, n)
	    if (abs (x - xa[klo]) < abs (x - xa[khi]))
		y = ya[klo]
	    else
		y = ya[khi]

	case I_LINEAR:

	    # Get x1 and x2.
	    call tu_x1x2 (xout, j, outrows, iv_step, x1, x2)

	    # Get klo, khi, and npts.
	    call tu_range (xa, ya, n, x1, x2, klo, khi, npts)
	    if (npts > 1) {
		# Fit a line to the data from klo to khi inclusive.
		call tu_fit1 (xa, ya, x, klo, khi, y)
	    } else {
		# Interpolate.
		klo = klo - 1
		call tuhunt (xa, n, x, klo)
		klo = max (klo, 1)
		klo = min (klo, n-1)
		call tuiep1 (xa[klo], ya[klo], x, y)
	    }

	case I_POLY3:

	    call tuhunt (xa, n, x, klo)
	    klo = max (klo, 2)
	    klo = min (klo, n-2)
	    # Pass tuiep3 the four points at klo-1, klo, klo+1, klo+2.
	    call tuiep3 (xa[klo-1], ya[klo-1], x, y, dy)

	case I_SPLINE:

	    call tuhunt (xa, n, x, klo)
	    klo = max (klo, 1)
	    klo = min (klo, n-1)
	    call tuispl (xa[klo], ya[klo], y2[klo], x, y)
	}
end

# This routine gets the endpoints x1,x2 of the current output pixel.
# If the output independent variable array is uniformly spaced, x1 and x2
# will just be the current pixel xout[j] - or + iv_step/2.  If the output
# independent variable array is (or may be) nonuniformly spaced, indicated
# by iv_step being zero, then x1 and x2 will be the midpoints of intervals
# adjacent to xout[j] on the left and right.
#
# x1 may be less than, equal to, or greater than x2.

procedure tu_x1x2 (xout, j, outrows, iv_step, x1, x2)

double	xout[ARB]	# i: output independent variable array
int	j		# i: current element of xout
int	outrows		# i: size of xout array
double	iv_step		# i: spacing of x values (can be negative)
double	x1, x2		# o: endpoints of output pixel

begin
	if (iv_step != 0) {
	    # output is uniformly spaced
	    x1 = xout[j] - iv_step / 2.d0
	    x2 = xout[j] + iv_step / 2.d0
	} else {
	    if (outrows == 1) {
		x1 = xout[1]
		x2 = xout[1]
	    } else if (j == 1) {
		x2 = (xout[1] + xout[2]) / 2.d0
		x1 = xout[1] - (x2 - xout[1])
	    } else if (j == outrows) {
		x1 = (xout[outrows-1] + xout[outrows]) / 2.d0
		x2 = xout[outrows] + (xout[outrows] - x1)
	    } else {
		x1 = (xout[j-1] + xout[j]) / 2.d0
		x2 = (xout[j] + xout[j+1]) / 2.d0
	    }
	}
end

# tu_range -- find range of indexes
# This routine finds the range of indexes in xa and ya corresponding to
# the interval x1 to x2.  The range is klo to khi; klo will be less than
# or equal to khi, even though xa can be either increasing or decreasing.
# klo and khi will also be within the range 1 to n inclusive, even if
# x1 to x2 is outside the range xa[1] to xa[n].  npts will be set to
# the actual number of elements of xa within the interval x1 to x2;
# thus, npts can be zero.
#
# x1 and x2 will be interchanged, if necessary, so that the array index
# in xa corresponding to x1 will be smaller than the array index corresponding
# to x2, i.e. so klo will be smaller than khi.
#
# Note that klo is used as an initial value when hunting for a value in xa,
# so klo must have been initialized to a reasonable value before calling
# this routine.

procedure tu_range (xa, ya, n, x1, x2, klo, khi, npts)

double	xa[n]		# i: array of independent-variable values
double	ya[n]		# i: array of dependent-variable values
int	n		# i: size of xa, ya, y2 arrays
double	x1, x2		# io: endpoints of output pixel
int	klo, khi	# io: range of indices in xa within x1,x2
int	npts		# o: number of elements in xa within x1,x2
#--
double	temp		# for swapping x1 and x2, if appropriate
int	k1, k2

begin
	# Swap x1 and x2 if necessary, so that klo will be less than or
	# equal to khi.
	if (xa[1] <= xa[2]) {	# input X is increasing
	    if (x1 > x2) {
		temp = x1; x1 = x2; x2 = temp
	    }
	} else {		# input X is decreasing
	    if (x1 < x2) {
		temp = x1; x1 = x2; x2 = temp
	    }
	}

	k1 = klo
	call tuhunt (xa, n, x1, k1)
	k1 = k1 + 1			# next point
	klo = k1
	klo = min (klo, n)

	k2 = k1
	call tuhunt (xa, n, x2, k2)
	khi = max (k2, 1)
	khi = min (khi, n)

	# npts can be different from khi - klo + 1, and it can be zero.
	npts = k2 - k1 + 1
end

# tu_fit1 -- fit a line (1-D) to data
# This routine fits a straight line to (xa[k],ya[k]) for k = klo to khi
# inclusive.  The fit is then evaluated at x, and the result is returned
# as y.  There must be at least two points, i.e. khi > klo.

procedure tu_fit1 (xa, ya, x, klo, khi, y)

double	xa[ARB]		# i: array of independent-variable values
double	ya[ARB]		# i: array of dependent-variable values
double	x		# i: independent variable
int	klo, khi	# i: range of indices in xa covered by iv_step
double	y		# o: value of fit at x
#--
double	sumx, sumy, sumxy, sumx2
double	xmean, ymean
double	slope, intercept
int	k

begin
	sumx = 0.d0
	sumy = 0.d0
	do k = klo, khi {
	    sumx = sumx + xa[k]
	    sumy = sumy + ya[k]
	}
	xmean = sumx / (khi - klo + 1)
	ymean = sumy / (khi - klo + 1)

	sumxy = 0.d0
	sumx2 = 0.d0
	do k = klo, khi {
	    sumxy = sumxy + (xa[k] - xmean) * (ya[k] - ymean)
	    sumx2 = sumx2 + (xa[k] - xmean)**2
	}
	slope = sumxy / sumx2
	intercept = (ymean - slope * xmean)

	y = intercept + slope * x
end


# tuiep1 -- linear interpolation
# Interpolate to get one value.  X is supposed to be between xa[1] and
# xa[2], but it is not an error if x is outside that interval.  Note
# that xa, ya are subarrays of the arrays with the same names elsewhere.

procedure tuiep1 (xa, ya, x, y)

double	xa[2]			# i: pair of independent-variable values
double	ya[2]			# i: pair of dependent-variable values
double	x			# i: independent variable
double	y			# o: the interpolated value
#--
double	p			# fraction of distance between indep var val

begin
	if (x == xa[1]) {
	    y = ya[1]
	} else if (x == xa[2]) {
	    y = ya[2]
	} else {
	    p = (x - xa[1]) / (xa[2] - xa[1])
	    y = p * ya[2] + (1.-p) * ya[1]
	}
end