;; ;; ;; 1-dimensional interpolation routines. ;; ;; Copyright 2007-2009 Ivan Raikov and the Okinawa Institute of Science and Technology. ;; ;; This program is free software: you can redistribute it and/or ;; modify it under the terms of the GNU General Public License as ;; published by the Free Software Foundation, either version 3 of the ;; License, or (at your option) any later version. ;; ;; This program is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; ;; A full copy of the GPL license can be found at ;; . ;; (module interp1d (interp1d:nearest interp1d:lbound interp1d:ubound interp1d:linear interp1d:piecewise-quadratic interp1d:from-sequence ) (import scheme chicken foreign easyffi srfi-1 srfi-4 ) (define (interp1d:error x . rest) (let ((port (open-output-string))) (if (port? x) (begin (display "[" port) (display (port-name x) port) (display "] " port))) (let loop ((objs (if (port? x) rest (cons x rest)))) (if (null? objs) (begin (newline port) (error 'interp1d (get-output-string port))) (begin (display (car objs) port) (display " " port) (loop (cdr objs))))))) ;; A sliding window over a sequence of data (list or stream) (define (interp1d:from-sequence method step src . rest) (let-optionals rest ((start 0) (data-car car) (data-cdr cdr) (data-null? null?) (delta-window-len 4) (max-window-len 128)) (let ((tlo start) (thi start) (xdata (list start)) (ydata (list (data-car src))) (strm (data-cdr src))) (lambda (t) ; (print "t = " t) ; (print "xdata = " xdata) ; (print "ydata = " ydata) (cond ((= t tlo) (exact->inexact (car ydata))) ((= t thi) (exact->inexact (last ydata))) ((and (<= t thi) (<= tlo t)) (method xdata ydata t)) ((< thi t) (let-values (((thi1 xs1 ys1 strm1) (let loop ((delta (fx+ delta-window-len (inexact->exact (floor (/ (- t thi) step))))) (ti (+ step thi)) (xs (list)) (ys (list)) (strm strm)) (if (and (positive? delta)) (begin (if (data-null? strm) (interp1d:error 'interp1d:from-sequence ": interpolation table is too short;" "boundary exceeded at x = " ti)) (loop (fx- delta 1) (+ step ti) (cons ti xs) (cons (data-car strm) ys) (data-cdr strm))) (values (- ti step) (reverse xs) (reverse ys) strm))))) (set! ydata (append ydata ys1)) (set! xdata (append xdata xs1)) (set! thi thi1) (set! strm strm1) (let ((n (length ydata))) (if (> n max-window-len) (let ((delta (fx- n max-window-len))) (set! tlo (+ tlo (* step delta))) (set! ydata (drop ydata delta)) (set! xdata (drop xdata delta))))) (method xdata ydata t))) ((> tlo t) (interp1d:error 'interp1d-from-sequence ": insufficient sliding window length for past data stream values: " " tlo = " tlo " t = " t))))))) (define (bounds x xi . rest) (let-optionals rest ((delta2 1)) (let loop ((a 0) (b (fx- (length x) 1))) (let ((delta (fx- b a))) (if (positive? (fx- delta delta2)) (let ((midpoint (fx+ a (fx/ delta 2)))) (if (<= xi (list-ref x midpoint)) (loop a midpoint) (loop midpoint b))) (if (or (< xi (list-ref x a)) (< (list-ref x b) xi)) (values #f #f) (values a b))))))) (define (interp1d:lbound xp yp xi) (let ((n (length xp))) (if (not (positive? (- n 2))) (interp1d:error 'interp1d:lbound ": interpolation table is too short")) (let-values (((i1 i2) (bounds xp xi))) (if (not (and i1 i2)) (interp1d:error 'interp1d:lbound ": value outside specified range")) (let ((x1 (list-ref xp i1)) (x2 (list-ref xp i2))) (let ((y1 (list-ref yp i1))) y1))))) (define (interp1d:ubound xp yp xi) (let ((n (length xp))) (if (not (positive? (- n 2))) (interp1d:error 'interp1d:ubound ": interpolation table is too short")) (let-values (((i1 i2) (bounds xp xi))) (if (not (and i1 i2)) (interp1d:error 'interp1d:ubound ": value outside specified range")) (let ((x1 (list-ref xp i1)) (x2 (list-ref xp i2))) (let ((y2 (list-ref yp i2))) y2))))) (define (interp1d:nearest xp yp xi) (let ((n (length xp))) (if (not (positive? (- n 2))) (interp1d:error 'interp1d:nearest ": interpolation table is too short")) (let-values (((i1 i2) (bounds xp xi))) (if (not (and i1 i2)) (interp1d:error 'interp1d:nearest ": value outside specified range")) (let ((x1 (list-ref xp i1)) (x2 (list-ref xp i2))) (let ((y1 (list-ref yp i1)) (y2 (list-ref yp i2))) (if (< (abs (- xi x1)) (abs (- xi x2))) y1 y2)))))) ;; ;; Interpolate function y=f(x) at the point xi using the linear ;; interpolation method. ;; (define (interp1d:linear xp yp xi) (let ((n (length xp))) (if (not (positive? (- n 2))) (interp1d:error 'interp1d:linear ": interpolation table is too short")) ; (print "linear: xi = " xi) ; (print "linear: n = " n) (let-values (((i1 i2) (bounds xp xi))) ; (print "linear: i1 = " i1) ; (print "linear: i2 = " i2) (if (not (and i1 i2)) (interp1d:error 'interp1d:linear ": value outside specified range")) (let ((x1 (list-ref xp i1)) (x2 (list-ref xp i2))) (let ((y1 (list-ref yp i1)) (y2 (list-ref yp i2))) (let ((A (/ (- y2 y1) (- x2 x1))) (B (- (/ (- (* x1 y2) (* y1 x2)) (- x2 x1))))) (let ((yi (+ (* A xi) B))) ; (print "linear: x1 = " x1) ; (print "linear: x2 = " x2) ; (print "linear: y1 = " y1) ; (print "linear: y2 = " y2) ; (print "linear: p = " p) ; (print "linear: yi = " yi) yi))))))) ; Parse & embed #>! void pq_coeffs (double x1, double x2, double x3, double y1, double y2, double y3, double *ABC); <# ;; ;; Interpolate function y=f(x) at the point xi using the piecewise ;; quadratic interpolation method. ;; (define (interp1d:piecewise-quadratic xp yp xi) (let ((n (length xp))) (if (not (positive? (- n 3))) (interp1d:error 'piecewise-quadratic "interpolation table is too short")) (let-values (((i1 i2) (bounds xp xi 2))) ; (print "pq: i1 = " i1) ; (print "pq: i2 = " i2) (if (not (and i1 i2)) (interp1d:error 'interp1d:piecewise-quadratic ": value outside specified range")) (let-values (((i1 i2 i3) (let ((i3 (+ 1 i2))) (if (<= n i3) (values (- i1 1) (- i2 1) i2) (values i1 i2 i3))))) ; (print "pq: i1 = " i1) ; (print "pq: i2 = " i2) ; (print "pq: i3 = " i3) (let ((x1 (list-ref xp i1)) (x2 (list-ref xp i2)) (x3 (list-ref xp i3))) (let ((y1 (list-ref yp i1)) (y2 (list-ref yp i2)) (y3 (list-ref yp i3))) (let-values (((A B C) (let ((ABC (make-f64vector 3 0.0))) (pq_coeffs x1 x2 x3 y1 y2 y3 ABC) (values (f64vector-ref ABC 0) (f64vector-ref ABC 1) (f64vector-ref ABC 2))))) (+ (* A (* xi xi)) (* B xi) C)))))))) ; Include into generated code, but don't parse: #> #include double sqr (double x) { return x*x; } void pq_coeffs (double x1, double x2, double x3, double y1, double y2, double y3, double *ABC) { ABC[0] = ((x2-x1)*y3+(y1-y2)*x3+x1*y2-y1*x2) / ((x2-x1)*sqr(x3)+(sqr(x1)-sqr(x2))*x3+x1*sqr(x2)-sqr(x1)*x2); ABC[1] = -((sqr(x2)-sqr(x1))*y3+(y1-y2)*sqr(x3)+sqr(x1)*y2-y1*sqr(x2)) / ((x2-x1)*sqr(x3)+(sqr(x1)-sqr(x2))*x3+x1*sqr(x2)-sqr(x1)*x2); ABC[2] = ((x1*sqr(x2)-sqr(x1)*x2)*y3+(y1*x2-x1*y2)*sqr(x3)+(sqr(x1)*y2-y1*sqr(x2))*x3) / ((x2-x1)*sqr(x3)+(sqr(x1)-sqr(x2))*x3+x1*sqr(x2)-sqr(x1)*x2); } <# )