Merge branch 'master' into bug-4403-remove-polyfill
[maxima.git] / share / fftpack5 / fftpack5.lisp
blob1c648c94b96064450441bd98b93af0faedfc0b45
1 (defpackage :fftpack5
2 (:use :common-lisp)
3 (:export "RFFT"
4 "INVERSE-RFFT"
5 "CFFT"
6 "INVERSE-CFFT"))
8 (in-package :fftpack5)
10 (defvar *wsave-cache*
11 (make-hash-table)
12 "Cache for different wsave tables for single-precision. The key is
13 the FFT size; the value is a the wsave table needed by the FFT
14 routines.")
16 (defvar *wsave-cache-double*
17 (make-hash-table)
18 "Cache for different wsave tables for double-precision FFTs. The
19 key is the FFT size; the value is a the wsave table needed by the
20 FFT routines.")
22 (defun get-wsave-entry (n)
23 "Get the wsave array and it's length that is needed for computing
24 the (forward and inverse) FFTs. The value is cached, so if it's the
25 cache, return it. Otherwise compute a new value and save it in the
26 cache."
27 (let ((entry (gethash n *wsave-cache*)))
28 (if entry
29 entry
30 (let* ((lensav (+ n 4 (floor (log n 2))))
31 (wsave (make-array lensav :element-type 'double-float)))
32 (multiple-value-bind (ignore-0 ignore-1 ignore-2 ier)
33 (rfft1i n wsave lensav 0)
34 (declare (ignore ignore-0 ignore-1 ignore-2))
35 (unless (zerop ier)
36 ;; This shouldn't really ever happen.
37 (error "lensav is not big enough"))
38 (setf (gethash n *wsave-cache*) wsave)
39 wsave)))))
41 (defun convert-rfft (x)
42 "Convert the output of FFTPACK RFFT1F (forward real FFT) into a more
43 user-friendly format with complex values"
44 (declare (type (simple-array double-float (*)) x))
45 (let* ((n (length x))
46 (nhalf (floor (/ n 2)))
47 (out (make-array (+ 1 nhalf) :element-type '(complex double-float))))
48 ;; If X is the transformed value, then the output from rfftf is:
49 ;; 0: X(0) / N
50 ;; 1: 2*realpart(X(1))/N
51 ;; 2: 2*imagpart(X(1))/N
52 ;; 3: 2*realpart(X(2))/N
53 ;; 4: 2*realpart(X(2))/N
54 ;; ...
55 ;; N-1: X(N-1)/N
56 ;; The last term exists only if N is even.
57 (setf (aref out 0) (complex (aref x 0) 0.0))
58 (loop for j from 1 to (if (evenp n) (1- nhalf) nhalf)
59 for k from 1 by 2
61 ;; Need to remove the factor of 2 that rfftf added.
62 (setf (aref out j) (complex (* 0.5 (aref x k))
63 (* 0.5 (aref x (1+ k))))))
64 (when (evenp n)
65 (setf (aref out nhalf)
66 (complex (aref x (1- n)) 0.0)))
67 out))
69 (defun convert-inverse-rfft (x n)
70 "Convert the complex-valued input, X, (that was produced by rfft)
71 into the form needed by rfft1b. The length of the transform, N, is
72 needed because it cannot be uniquely determined from the length of
73 X."
74 (declare (type (simple-array (complex double-float) (*)) x))
75 (let ((res (make-array n :element-type 'double-float)))
76 (setf (aref res 0) (realpart (aref x 0)))
77 (loop for j from 1 below (if (evenp n) (1- (length x)) (length x))
78 for k from 1 by 2
80 (let ((z (aref x j)))
81 ;; Put back the factor of 2 that we removed in rfft but
82 ;; is needed by rfft1b.
83 (setf (aref res k) (* 2 (realpart z)))
84 (setf (aref res (1+ k)) (* 2 (imagpart z)))))
85 (when (evenp n)
86 (setf (aref res (1- n)) (realpart (aref x (1- (length x))))))
87 res))
89 (defun rfft (x)
90 "Compute the real FFT of X.
92 Let N be the length of X. The FFT is:
94 Y[n] = 1/N*sum(x[k] * exp(2*%i*%pi*k*n/N), k, 0, N-1)
96 for n = 0, 1,...,floor(N/2)+1
98 WARNING: This definition differs from the formula used for CFFT
99 which uses exp(-2*%pi*%i*k*n/N)!"
101 (declare (type (simple-array double-float (*)) x))
102 (let* ((n (length x))
103 (work (make-array n :element-type 'double-float))
104 (wsave (get-wsave-entry n))
105 (lensav (length wsave)))
106 (let ((ier
107 (nth-value 8
108 (rfft1f n 1 x n wsave lensav work n 0))))
109 (unless (zerop ier)
110 ;; This should never happen because we always allocate enough
111 ;; space for x, wsave, and the work array.
112 (error "rfft1f failed with code ~A" ier))
113 (convert-rfft x))))
115 (defun inverse-rfft (x n)
116 "Compute the inverse real FFT of X. N is the length of the transform
117 (and not the length of X!).
119 See RFFT for the definition of the FFT used in these routines."
120 (declare (type (simple-array (complex double-float) (*)) x))
122 ;; Check to see if N is consistent with the length of X. However,
123 ;; this isn't foolproof. For a real FFT of length 5, the FFT array
124 ;; has length 3. But a real FFT of length 4 also has an array
125 ;; length of 3.
126 (unless (= (length x) (1+ (floor n 2)))
127 (error "Length of X (~A) is not compatible with N (~D)"
128 (length x) n))
130 (let* ((inv (convert-inverse-rfft x n))
131 (work (make-array n :element-type 'double-float))
132 (wsave (get-wsave-entry n))
133 (lensav (length wsave)))
134 (let* ((ier
135 (progn
136 (nth-value 8
137 (rfft1b n 1 inv n wsave lensav work n 0)))))
138 (unless (zerop ier)
139 ;; This should never happen because we should have always
140 ;; allocated the correct size of the arrays.
141 (error "rfft1b failed with code ~A" ier))
142 inv)))
144 (defun test-rfft (n &key verbose)
145 "Test the rfft and inverse-rfft routines using a simple ramp of
146 length n"
147 ;; For testing, use a simple ramp: 1, 2, 3, ..., N.
148 ;; With some care, we can derive the FFT for this is
150 ;; X[0] = (N + 1) / 2
151 ;; X[n] = -1/2 - cot(%pi*n/N)/2, n = 1, N
153 (let* ((x (make-array n :element-type 'double-float)))
154 (loop for k from 0 below n
156 (setf (aref x k) (float (+ k 1) 1d0)))
157 (let* ((xfrm (rfft x))
158 (xfrm-len (length xfrm))
159 (expected (make-array xfrm-len :element-type '(complex double-float)))
160 (noise-pwr 0d0)
161 (signal-pwr 0d0))
162 (declare (double-float noise-pwr signal-pwr))
164 (setf (aref expected 0) (complex (* 0.5d0 (+ n 1)) 0d0))
165 (loop for k from 1 below xfrm-len
166 with omega = (coerce (/ pi n) 'double-float)
168 (setf (aref expected k)
169 (complex -0.5d0 (/ -0.5d0 (tan (* omega k))))))
170 (when verbose
171 (format t "Forward transform; actual vs expected~%")
172 (loop for k from 0 below xfrm-len
174 (format t "~4d: ~A ~A~%" k (aref xfrm k) (aref expected k))))
175 (incf noise-pwr (expt (abs (- (aref xfrm 0) (* 0.5 (+ n 1)))) 2))
176 (incf signal-pwr (expt (* 0.5 (+ n 1)) 2))
177 (loop for k from 1 below (length xfrm)
179 (incf noise-pwr (expt (abs (- (aref xfrm k)
180 (aref expected k)))
182 (incf signal-pwr (expt (abs (aref expected k)) 2)))
183 (let ((inv (inverse-rfft xfrm n))
184 (inv-noise-pwr 0.0)
185 (inv-signal-pwr 0.0))
186 (when verbose
187 (format t "Inverse transform; actual vs expected~%")
188 (loop for k from 0 below n
190 (format t "~4d: ~A ~A~%" k (aref inv k) (+ k 1))))
191 (loop for k from 0 below n
193 (incf inv-noise-pwr (expt (- (aref inv k) (+ k 1)) 2))
194 (incf inv-signal-pwr (expt (coerce (+ k 1) 'double-float) 2)))
195 (flet ((db (s n)
196 (if (zerop n)
197 1000d0
198 (* 10 (log (/ s n) 10)))))
199 (values (db signal-pwr noise-pwr) (db inv-signal-pwr inv-noise-pwr) noise-pwr signal-pwr inv-noise-pwr inv-signal-pwr))))))
202 (defun get-double-wsave-entry (n)
203 "Get the wsave array and it's length that is needed for computing
204 the (forward and inverse) FFTs. The value is cached, so if it's the
205 cache, return it. Otherwise compute a new value and save it in the
206 cache."
207 (let ((entry (gethash n *wsave-cache-double*)))
208 (if entry
209 entry
210 (let* ((lensav (+ (* 2 n) 4 (floor (log n 2))))
211 (wsave (make-array lensav :element-type 'double-float)))
212 (multiple-value-bind (ignore-0 ignore-1 ignore-2 ier)
213 (cfft1i n wsave lensav 0)
214 (declare (ignore ignore-0 ignore-1 ignore-2))
215 (unless (zerop ier)
216 ;; This shouldn't really ever happen.
217 (error "lensav is not big enough"))
218 (setf (gethash n *wsave-cache-double*) wsave)
219 wsave)))))
221 (defun cfft (x)
222 "Compute the FFT of a complex array X
224 Let N be the length of X. The FFT is:
226 Y[n] = 1/N*sum(x[k] * exp(-2*%pi*%i*n*k/N), k = 0, N-1)
228 for n = 0, 1,...,N -1
230 WARNING: This definition differs from RFFT which has
231 exp(+2*%pi*%i*n*k/N)"
233 (declare (type (simple-array (complex double-float) (*)) x))
234 (let* ((n (length x))
235 (lenwrk (* 2 n))
236 (work (make-array lenwrk :element-type 'double-float))
237 (wsave (get-double-wsave-entry n))
238 (lensav (length wsave)))
239 (let ((ier
240 (nth-value 8
241 (cfft1f n 1 x n wsave lensav work lenwrk 0))))
242 (unless (zerop ier)
243 ;; This should never happen because we always allocate enough
244 ;; space for x, wsave, and the work array.
245 (error "rfft1f failed with code ~A" ier))
246 x)))
248 (defun inverse-cfft (x)
249 (declare (type (simple-array (complex double-float) (*)) x))
251 (let* ((n (length x))
252 (lenwrk (* 2 n))
253 (work (make-array lenwrk :element-type 'double-float))
254 (wsave (get-double-wsave-entry n))
255 (lensav (length wsave)))
256 (let* ((ier
257 (progn
258 (nth-value 8
259 (cfft1b n 1 x n wsave lensav work lenwrk 0)))))
260 (unless (zerop ier)
261 ;; This should never happen because we should have always
262 ;; allocated the correct size of the arrays.
263 (error "rfft1b failed with code ~A" ier))
264 x)))
266 (defun test-cfft (n &key verbose)
267 (let* ((x (make-array n :element-type '(complex double-float))))
268 ;; The test signal is a simple ramp: 1, 2, 3,..., N.
270 ;; The analytical FFT for this is:
272 ;; X[0] = (N+1)/2
273 ;; X[n] = -1/2 + %i*cot(%pi*n/N)/2
274 (loop for k from 0 below n
276 (setf (aref x k) (complex (+ k 1) 0d0)))
277 (let* ((xfrm (cfft x))
278 (xfrm-len (length xfrm))
279 (expected (make-array xfrm-len :element-type '(complex double-float)))
280 (noise-pwr 0d0)
281 (signal-pwr 0d0))
282 (declare (double-float noise-pwr signal-pwr))
284 (setf (aref expected 0) (complex (* 0.5d0 (+ n 1)) 0d0))
285 (loop for k from 1 below xfrm-len
286 with omega = (coerce (/ pi n) 'double-float)
288 (setf (aref expected k)
289 (complex -0.5d0 (/ 0.5d0 (tan (* omega k))))))
290 (when verbose
291 (format t "Forward transform; actual vs expected~%")
292 (loop for k from 0 below xfrm-len
294 (format t "~4d: ~A ~A~%" k (aref xfrm k) (aref expected k))))
295 (incf noise-pwr (expt (abs (- (aref xfrm 0) (* 0.5 (+ n 1)))) 2))
296 (incf signal-pwr (expt (* 0.5 (+ n 1)) 2))
297 (loop for k from 1 below (length xfrm)
299 (incf noise-pwr (expt (abs (- (aref xfrm k)
300 (aref expected k)))
302 (incf signal-pwr (expt (abs (aref expected k)) 2)))
303 (let ((inv (inverse-cfft xfrm))
304 (inv-noise-pwr 0.0)
305 (inv-signal-pwr 0.0))
306 (when verbose
307 (format t "Inverse transform; actual vs expected~%")
308 (loop for k from 0 below n
310 (format t "~4d: ~A ~A~%" k (aref inv k) (+ k 1))))
311 (loop for k from 0 below n
313 (incf inv-noise-pwr (expt (abs (- (aref inv k) (+ k 1))) 2))
314 (incf inv-signal-pwr (expt (coerce (+ k 1) 'double-float) 2)))
315 (flet ((db (s n)
316 (if (zerop n)
317 1000d0
318 (* 10 (log (/ s n) 10)))))
319 (values (db signal-pwr noise-pwr) (db inv-signal-pwr inv-noise-pwr) noise-pwr signal-pwr inv-noise-pwr inv-signal-pwr))))))