aboutsummaryrefslogtreecommitdiff
path: root/pkg/xtools/center1d.x
blob: 33a6ec3dd182ade713d9ba08043b8fd4ddc24fff (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
# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.

include	<math/iminterp.h>
include	<pkg/center1d.h>

define	MIN_WIDTH	3.		# Minimum centering width
define	EPSILON		0.001		# Accuracy of centering
define	EPSILON1	0.005		# Tolerance for convergence check
define	ITERATIONS	100		# Maximum number of iterations
define	MAX_DXCHECK	3		# Look back for failed convergence
define	INTERPTYPE	II_SPLINE3	# Image interpolation type


# CENTER1D -- Locate the center of a one dimensional feature.
# A value of INDEF is returned in the centering fails for any reason.
# This procedure just sets up the data and adjusts for emission or
# absorption features.  The actual centering is done by C1D_CENTER.
# If twidth <= 1 return the nearest minima or maxima.

real procedure center1d (x, data, npts, width, type, radius, threshold)

real	x				# Initial guess
int	npts				# Number of data points
real	data[npts]			# Data points
real	width				# Feature width
int	type				# Feature type
real	radius				# Centering radius
real	threshold			# Minimum range in feature

real	xc				# Center

int	x1, x2, nx
real	a, b, rad, wid
pointer	sp, data1

real	c1d_center()

begin

	# Check starting value.
	if (IS_INDEF(x) || (x < 1) || (x > npts))
	    return (INDEF)

	# Set parameters.  The minimum in the error radius
	# is for defining the data window.  The user error radius is used to
	# check for an error in the derived center at the end of the centering.

	call c1d_params (INDEFI, INDEFR)
	wid = max (width, MIN_WIDTH)
	rad = max (2., radius)

	# Determine the pixel value range around the initial center, including
	# the width and error radius buffer.  Check for a minimum range.

	x1 = max (1., x - wid / 2 - rad - wid)
	x2 = min (real (npts), x + wid / 2 + rad + wid + 1)
	nx = x2 - x1 + 1
	call alimr (data[x1], nx, a, b)
	if (b - a < threshold)
	    return (INDEF)

	# Allocate memory for the continuum subtracted data vector.  The X
	# range is just large enough to include the error radius and the
	# half width.

	x1 = max (1., x - wid / 2 - rad)
	x2 = min (real (npts), x + wid / 2 + rad + 1)
	nx = x2 - x1 + 1

	call smark (sp)
	call salloc (data1, nx, TY_REAL)
	call amovr (data[x1], Memr[data1], nx)

	# Make the centering data positive, subtract the continuum, and
	# apply a threshold to eliminate noise spikes.

	switch (type) {
	case EMISSION:
	    a = min (0., a)
	    call asubkr (data[x1], a + threshold, Memr[data1], nx)
	    call amaxkr (Memr[data1], 0., Memr[data1], nx)
	case ABSORPTION:
	    call anegr (data[x1], Memr[data1], nx)
	    call asubkr (Memr[data1], threshold - b, Memr[data1], nx)
	    call amaxkr (Memr[data1], 0., Memr[data1], nx)
	default:
	    call error (0, "Unknown feature type")
	}

	# Determine the center.
	xc = c1d_center (x - x1 + 1, Memr[data1], nx, width)

	# Check user centering error radius.
	if (!IS_INDEF(xc)) {
	    xc = xc + x1 - 1
	    if (abs (x - xc) > radius)
		xc = INDEF
	}

	# Free memory and return the center position.
	call sfree (sp)
	return (xc)
end


# C1D_PARAMS -- Set parameters.

procedure c1d_params (interp, eps)

int	interp		# Interpolation type
real	eps		# Accuracy of centering

int	first
data	first /YES/

int	interptype
real	epsilon
common	/c1d_common/ interptype, epsilon

begin
	if (!IS_INDEFI(interp))
	    interptype = interp
	else if (first == YES)
	    interptype = INTERPTYPE

	if (!IS_INDEFR(eps))
	    epsilon = eps
	else if (first == YES)
	    epsilon = EPSILON

	first = NO
end


# C1D_CENTER -- One dimensional centering algorithm.
# If the width is <= 1. return the nearest local maximum.

real procedure c1d_center (x, data, npts, width)

real	x				# Starting guess
int	npts				# Number of points in data vector
real	data[npts]			# Data vector
real	width				# Centering width

int	i, j, iteration, dxcheck
real	xc, wid, hwidth, dx, dxabs, dxlast
real	a, b, sum1, sum2, intgrl1, intgrl2
pointer	asi1, asi2, sp, data1

real	asigrl()

int	interptype
real	epsilon
common	/c1d_common/ interptype, epsilon

define	done_	99

begin
	# Find the nearest local maxima as the starting point.
	# This is required because the threshold limit may have set
	# large regions of the data to zero and without a gradient
	# the centering will fail.

	for (i=x+.5; (i<npts) && (data[i]<=data[i+1]); i=i+1)
	    ;
	for (; (i>1) && (data[i]<=data[i-1]); i=i-1)
	    ;
	for (j=x+.5; (j>1) && (data[j]<=data[j-1]); j=j-1)
	    ;
	for (; (j<npts) && (data[j]<=data[j+1]); j=j+1)
	    ;

	if (abs(i-x) < abs(x-j))
	    xc = i 
	else
	    xc = j

	if (width <= 1.)
	    return (xc)

	wid = max (width, MIN_WIDTH)

	# Check data range.
	hwidth = wid / 2
	if ((xc - hwidth < 1) || (xc + hwidth > npts))
	    return (INDEF)

	# Set interpolation functions.
	call asiinit (asi1, interptype)
	call asiinit (asi2, interptype)
	call asifit (asi1, data, npts)

	# Allocate, compute, and interpolate the x*y values.
	call smark (sp)
	call salloc (data1, npts, TY_REAL)
	do i = 1, npts
	    Memr[data1+i-1] = data[i] * i
	call asifit (asi2, Memr[data1], npts)
	call sfree (sp)

	# Iterate to find center.  This loop exits when 1) the maximum
	# number of iterations is reached, 2) the delta is less than
	# the required accuracy (criterion for finding a center), 3)
	# there is a problem in the computation, 4) successive steps
	# continue to exceed the minimum delta.

	dxlast = npts
	do iteration = 1, ITERATIONS {
	    # Ramp centering function.
	    # a = xc - hwidth
	    # b = xc + hwidth
	    # intgrl1 = asigrl (asi1, a, b)
	    # intgrl2 = asigrl (asi2, a, b)
	    # sum1 = intgrl2 - xc * intgrl1
	    # sum2 = intgrl1

	    # Triangle centering function.
	    a = xc - hwidth
	    b = xc - hwidth / 2
	    intgrl1 = asigrl (asi1, a, b)
	    intgrl2 = asigrl (asi2, a, b)
	    sum1 = (xc - hwidth) * intgrl1 - intgrl2
	    sum2 = -intgrl1
	    a = b
	    b = xc + hwidth / 2
	    intgrl1 = asigrl (asi1, a, b)
	    intgrl2 = asigrl (asi2, a, b)
	    sum1 = sum1 - xc * intgrl1 + intgrl2
	    sum2 = sum2 + intgrl1
	    a = b
	    b = xc + hwidth
	    intgrl1 = asigrl (asi1, a, b)
	    intgrl2 = asigrl (asi2, a, b)
	    sum1 = sum1 + (xc + hwidth) * intgrl1 - intgrl2
	    sum2 = sum2 - intgrl1

	    # Return no center if sum2 is zero.
	    if (sum2 == 0.)
		break

	    # Limit dx change in one iteration to 1 pixel.
	    dx = sum1 / abs (sum2)
	    dxabs = abs (dx)
	    xc = xc + max (-1., min (1., dx))

	    # Check data range.  Return no center if at edge of data.
	    if ((xc - hwidth < 1) || (xc + hwidth > npts))
		break

	    # Convergence tests.
	    if (dxabs < epsilon)
		goto done_
	    if (dxabs > dxlast + EPSILON1) {
		dxcheck = dxcheck + 1
		if (dxcheck > MAX_DXCHECK)
		    break
	    } else if (dxabs > dxlast - EPSILON1) {
		xc = xc - max (-1., min (1., dx)) / 2
		dxcheck = 0
	    } else {
		dxcheck = 0
	        dxlast = dxabs
	    }
	}

	# If we get here then no center was found.
	xc = INDEF

done_	call asifree (asi1)
	call asifree (asi2)
	return (xc)
end