1 /*
2  * Copyright (c) 2019 Kevin Townsend (KTOWN)
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <math.h>
8 #include <errno.h>
9 #include <zephyr/kernel.h>
10 #include <zsl/zsl.h>
11 #include <zsl/interp.h>
12 
13 int
zsl_interp_lerp(zsl_real_t v0,zsl_real_t v1,zsl_real_t t,zsl_real_t * v)14 zsl_interp_lerp(zsl_real_t v0, zsl_real_t v1, zsl_real_t t, zsl_real_t *v)
15 {
16 	int rc;
17 
18 	/* Ensure t = 0.0..1.0 */
19 	if ((t < 0.0f) || (t > 1.0f)) {
20 		rc = -EINVAL;
21 		*v = NAN;
22 		goto err;
23 	}
24 
25 	*v = (1.0f - t) * v0 + t * v1;
26 
27 	return 0;
28 err:
29 	return rc;
30 }
31 
32 int
zsl_interp_find_x(struct zsl_interp_xy xy[],size_t n,zsl_real_t x,int * idx)33 zsl_interp_find_x(struct zsl_interp_xy xy[], size_t n, zsl_real_t x, int *idx)
34 {
35 	int rc;
36 	unsigned int idx_upper; /* Upper limit */
37 	unsigned int idx_mid;   /* Midpoint */
38 	unsigned int idx_lower; /* Lower limit */
39 	int order;              /* Ascending (1) or descending (0) */
40 
41 	/* Init lower and upper limits. */
42 	idx_lower = 0;
43 	idx_upper = n;
44 
45 	/* Make sure we have an appropriately large dataset. */
46 	if (n < 2) {
47 		*idx = -1;
48 		rc = -EINVAL;
49 		goto err;
50 	}
51 
52 	/* Determine order (1 = ascending, 0 = descending). */
53 	order = (xy[n - 1].x >= xy[0].x);
54 
55 	/* xy[0] and xy[n-1] bounds checks. */
56 	if ((x > xy[n - 1].x && order) || (x < xy[n - 1].x && !order)) {
57 		/* Out of bounds on the high end. */
58 		*idx = n;
59 		rc = -EINVAL;
60 		goto err;
61 	} else if ((x < xy[0].x && order) || (x > xy[0].x && !order)) {
62 		/* Out of bounds on the low end. */
63 		*idx = -1;
64 		rc = -EINVAL;
65 		goto err;
66 	}
67 	;
68 
69 	/* Repetitive mid-point computation until a match is made. */
70 	while (idx_upper - idx_lower > 1) {
71 		idx_mid = (idx_upper + idx_lower) >> 1;
72 		if ((x >= xy[idx_mid].x && order) ||
73 		    (x <= xy[idx_mid].x && !order)) {
74 			/* Set lower limit to current mid-point. */
75 			idx_lower = idx_mid;
76 		} else {
77 			/* Set upper limit to current mid-point. */
78 			idx_upper = idx_mid;
79 		}
80 	}
81 
82 	/* Set the output index value. */
83 	if (x == xy[0].x) {
84 		/* Return absolute lower limit. */
85 		*idx = 0;
86 	} else if (x == xy[n - 1].x) {
87 		/* Return absolute upper limit. */
88 		*idx = n - 2;
89 	} else {
90 		/* Return a value in between. */
91 		*idx = idx_lower;
92 	}
93 
94 	return 0;
95 err:
96 	return rc;
97 }
98 
99 int
zsl_interp_nn(struct zsl_interp_xy * xy1,struct zsl_interp_xy * xy3,zsl_real_t x2,zsl_real_t * y2)100 zsl_interp_nn(struct zsl_interp_xy *xy1, struct zsl_interp_xy *xy3,
101         zsl_real_t x2, zsl_real_t *y2)
102 {
103 	int rc;
104 	zsl_real_t delta;
105 
106 	/* Make sure there is a delta x between xy1 and xy3. */
107 	delta = xy3->x - xy1->x;
108 	if (delta < 1E-6F && -delta < 1E-6F) {
109 		rc = -EINVAL;
110 		*y2 = NAN;
111 		goto err;
112 	}
113 
114 	/* Ensure that x1 <= x2 <= x3. */
115 	if ((x2 < xy1->x) || (x2 > xy3->x)) {
116 		rc = -EINVAL;
117 		goto err;
118 	}
119 
120 	/* Determine which value is closest, rounding up on 0.5. */
121 	*y2 = x2 >= (xy1->x * 1.5f) ? xy3->y : xy1->y;
122 
123 	return 0;
124 err:
125 	return rc;
126 }
127 
128 int
zsl_interp_nn_arr(struct zsl_interp_xy xy[],size_t n,zsl_real_t x,zsl_real_t * y)129 zsl_interp_nn_arr(struct zsl_interp_xy xy[], size_t n,
130         zsl_real_t x, zsl_real_t *y)
131 {
132 	int rc;
133 	int idx;
134 
135 	/* Find the starting position in xy[] for x. */
136 	rc = zsl_interp_find_x(xy, n, x, &idx);
137 	if (rc) {
138 		*y = NAN;
139 		goto err;
140 	}
141 
142 	/* Perform nearest neighbour interp. between xy[idx] and xy[idx+1]. */
143 	rc = zsl_interp_nn(&xy[idx], &xy[idx + 1], x, y);
144 	if (rc) {
145 		*y = NAN;
146 		goto err;
147 	}
148 
149 	return 0;
150 err:
151 	return rc;
152 }
153 
154 int
zsl_interp_lin_y(struct zsl_interp_xy * xy1,struct zsl_interp_xy * xy3,zsl_real_t x2,zsl_real_t * y2)155 zsl_interp_lin_y(struct zsl_interp_xy *xy1, struct zsl_interp_xy *xy3,
156 	     zsl_real_t x2, zsl_real_t *y2)
157 {
158 	int rc;
159 	zsl_real_t delta;
160 
161 	/* Make sure there is a delta on x between xy1 and xy3. */
162 	delta = xy3->x - xy1->x;
163 	if (delta < 1E-6F && -delta < 1E-6F) {
164 		rc = -EINVAL;
165 		*y2 = NAN;
166 		goto err;
167 	}
168 
169 	/* Ensure that x2 >= x1 && x2 <= x3. */
170 	if ((x2 < xy1->x) || (x2 > xy3->x)) {
171 		rc = -EINVAL;
172 		*y2 = NAN;
173 		goto err;
174 	}
175 
176 	/*
177 	 *      (x2 -  x1)(y3 - y1)
178 	 * y2 = ------------------- + y1
179 	 *           (x3 - x1)
180 	 */
181 
182 	*y2 = ((x2 - xy1->x) * (xy3->y - xy1->y)) / (xy3->x - xy1->x) + xy1->y;
183 
184 	return 0;
185 err:
186 	return rc;
187 }
188 
189 int
zsl_interp_lin_y_arr(struct zsl_interp_xy xy[],size_t n,zsl_real_t x,zsl_real_t * y)190 zsl_interp_lin_y_arr(struct zsl_interp_xy xy[], size_t n,
191 		 zsl_real_t x, zsl_real_t *y)
192 {
193 	int rc;
194 	int idx;
195 
196 	/* Find the starting position in xy[] for x. */
197 	rc = zsl_interp_find_x(xy, n, x, &idx);
198 	if (rc) {
199 		*y = NAN;
200 		goto err;
201 	}
202 
203 	/* Perform linear interpolation of x between xy[idx] and xy[idx+1]. */
204 	rc = zsl_interp_lin_y(&xy[idx], &xy[idx + 1], x, y);
205 	if (rc) {
206 		*y = NAN;
207 		goto err;
208 	}
209 
210 	return 0;
211 err:
212 	return rc;
213 }
214 
215 int
zsl_interp_lin_x(struct zsl_interp_xy * xy1,struct zsl_interp_xy * xy3,zsl_real_t y2,zsl_real_t * x2)216 zsl_interp_lin_x(struct zsl_interp_xy *xy1, struct zsl_interp_xy *xy3,
217 	     zsl_real_t y2, zsl_real_t *x2)
218 {
219 	int rc;
220 	zsl_real_t min, max, delta;
221 
222 	/* Make sure there is a delta on x between xy1 and xy3. */
223 	delta = xy3->x - xy1->x;
224 	if (delta < 1E-6F && -delta < 1E-6F) {
225 		rc = -EINVAL;
226 		*x2 = NAN;
227 		goto err;
228 	}
229 
230 	/* Ensure that y2 is within the range of min and max. */
231 	min = xy1->y < xy3->y ? xy1->y : xy3->y;
232 	max = xy1->y < xy3->y ? xy3->y : xy1->y;
233 	if ((y2 <  min) || (y2 > max)) {
234 		rc = -EINVAL;
235 		*x2 = NAN;
236 		goto err;
237 	}
238 
239 	/*
240 	 *      (y3 - y2) * x1 + (y2 - y1) * x3
241 	 * x2 = -------------------------------
242 	 *                y3 - y1
243 	 */
244 
245 	*x2 = ((xy3->y - y2) * xy1->x + (y2 - xy1->y) *
246 	       xy3->x) / (xy3->y - xy1->y);
247 
248 	return 0;
249 err:
250 	return rc;
251 }
252 
253 int
zsl_interp_cubic_calc(struct zsl_interp_xyc xyc[],size_t n,zsl_real_t yp1,zsl_real_t ypn)254 zsl_interp_cubic_calc(struct zsl_interp_xyc xyc[], size_t n, zsl_real_t yp1,
255 		  zsl_real_t ypn)
256 {
257 	int rc;
258 	int i;
259 	int k;
260 	zsl_real_t sigma;
261 	zsl_real_t p;
262 	zsl_real_t qn;
263 	zsl_real_t un;
264 	zsl_real_t *u;
265 
266 	/* Make sure we have at least three values. */
267 	if (n < 3) {
268 		rc = -EINVAL;
269 		goto err;
270 	}
271 
272 	u = (zsl_real_t *)k_malloc((n - 1) * sizeof(zsl_real_t));
273 	if (u == NULL) {
274 		rc = -ENOMEM;
275 		goto err;
276 	}
277 
278 	/* TODO: Remove 'f' restrictions? */
279 	if (yp1 > 0.99e30f) {
280 		xyc[0].y2 = u[0] = 0.0f;
281 	} else {
282 		xyc[0].y2 = -0.5f;
283 		u[0] = (3.0f / (xyc[1].x - xyc[0].x)) *
284 		       ((xyc[1].y - xyc[0].y) / (xyc[1].x - xyc[0].x) - yp1);
285 	}
286 
287 	for (i = 1; i < n - 1; i++) {
288 		/* Break out common values. */
289 		zsl_real_t x_i_im1 = xyc[i].x - xyc[i - 1].x;
290 		zsl_real_t x_ip1_im1 = xyc[i + 1].x - xyc[i - 1].x;
291 		sigma = x_i_im1 / x_ip1_im1;
292 		p = sigma * xyc[i - 1].y2 + 2.0f;
293 		xyc[i].y2 = (sigma - 1.0f) / p;
294 		u[i] = (xyc[i + 1].y - xyc[i].y) / (xyc[i + 1].x - xyc[i].x) -
295 		       (xyc[i].y - xyc[i - 1].y) / (x_i_im1);
296 		u[i] = (6.0f * u[i] / (x_ip1_im1) - sigma * u[i - 1]) / p;
297 	}
298 
299 	if (ypn > 0.99e30f) {
300 		qn = un = 0.0f;
301 	} else {
302 		qn = 0.5f;
303 		un = (3.0f / (xyc[n - 1].x - xyc[n - 2].x)) *
304 		     (ypn - (xyc[n - 1].y -
305 			     xyc[n - 2].y) / (xyc[n - 1].x - xyc[n - 2].x));
306 	}
307 
308 	xyc[n - 1].y2 = (un - qn * u[n - 2]) / (qn * xyc[n - 2].y2 + 1.0f);
309 
310 	for (k = n - 2; k >= 0; k--) {
311 		xyc[k].y2 = xyc[k].y2 * xyc[k + 1].y2 + u[k];
312 	}
313 
314 	k_free(u);
315 
316 	return 0;
317 err:
318 	return rc;
319 }
320 
321 int
zsl_interp_cubic_arr(struct zsl_interp_xyc xyc[],size_t n,zsl_real_t x,zsl_real_t * y)322 zsl_interp_cubic_arr(struct zsl_interp_xyc xyc[], size_t n,
323 		 zsl_real_t x, zsl_real_t *y)
324 {
325 	int rc;
326 	int k;                  /* Array index value for mid point. */
327 	int klo;                /* Array index value for low point. */
328 	int khi;                /* Array index value for high point. */
329 	static int pklo;        /* Per. low pnt for repeat bisection search. */
330 	static int pkhi;        /* Per. high pnt for repeat bisection search. */
331 	zsl_real_t h;           /* xyc[j+1].x - xyc[j].x */
332 	zsl_real_t a;           /* (xyc[j+1].x - x) / h */
333 	zsl_real_t b;           /* (x - xyc[j].x) / h */
334 
335 	pklo = 0;
336 	pkhi = 1;
337 
338 	/* Make sure we have at least three values. */
339 	if (n < 3) {
340 		rc = -EINVAL;
341 		goto err;
342 	}
343 
344 	/* First check if pklo and pkhi from the last run are still a valid
345 	 * match. The static values are maintained across calls to this
346 	 * function, allowing us to avoid unnecessarily performing a full array
347 	 * search. */
348 	if (xyc[pklo].x <= x && xyc[pkhi].x > x) {
349 		klo = pklo;
350 		khi = pkhi;
351 	} else {
352 		/* Search the full array for x using bisection. */
353 		klo = 0;
354 		khi = n - 1;
355 		while (khi - klo > 1) {
356 			/* Set the midpoint based on the current high/low
357 			 * points.. */
358 			k = (khi + klo) >> 1;
359 			/* Determine whether we need to search in upper or
360 			 * lower half. */
361 			if (xyc[k].x > x) {
362 				/* If the current midpoint is greater than
363 				 * search value 'x' set the high marker to the
364 				 * current midpoint, which will cause search to
365 				 * continue in the lower half. */
366 				khi = k;
367 			} else {
368 				/* Otherwise set the low marker to the current
369 				 * midpoint, which will cause search to
370 				 * continue in the upper half. */
371 				klo = k;
372 			}
373 			/* Search until results are reduced to neighbouring xyc
374 			 * values. */
375 		}
376 		/* Persist pklo and pkhi for future calls to this function. */
377 		pklo = klo;
378 		pkhi = khi;
379 	}
380 
381 	h = xyc[khi].x - xyc[klo].x;
382 	if (h == 0) {
383 		/* No diff = invalid x input! */
384 		rc = -EINVAL;
385 		goto err;
386 	}
387 
388 	/* Calculate coefficients for hi-x (a) and x-lo (b). */
389 	a = (xyc[khi].x - x) / h;
390 	b = (x - xyc[klo].x) / h;
391 
392 	/* Interpolate for y based on a, b using prev. calculated y2 vals. */
393 	*y = a * xyc[klo].y + b * xyc[khi].y +
394 	     ((a * a * a - a) * xyc[klo].y2 + (b * b * b - b) *
395 	      xyc[khi].y2) * (h * h) / 6.0f;
396 
397 	return 0;
398 err:
399 	return rc;
400 }
401