Efficient FFT Computation of Real Input
Contents
Introduction
Algorithms to perform forward and inverse Fast Fourier Transforms (FFT and IFFT) typically assume complex input and output data. However, many applications use only real-valued data in the time domain. A simple but inefficient solution to this problem is to pad N-length real input signals to N-length complex input signals with zero-valued imaginary components.
x_{real} = { 1, 2, 3, ... } x_{cplx} = { 1, 0, 2, 0, 3, 0, ... }
The complex FFT can then be applied to the double-length sequence. However, this method is obviously inefficient. This topic explains how to use the complex-valued FFT and IFFT algorithms to efficiently process real-valued sequences without padding those sequences. There are two key advantages to this approach:
- Lower memory footprint - M bytes per sequence instead of 2M bytes
- Fewer cycles - length N/2 FFT and IFFT computation instead of length N
We will jump straight to the algorithm and how to use it. For detailed information on the derivation of this method, please refer to the full application report on ti.com.
Example C code for this method is also available for download.
Prerequisites
- Install the C674x DSPLIB
- Familiarize yourself with the FFT and IFFT kernels
- Look at demo code (*_d.c) for usage examples
Computing a Length N/2 Complex FFT from a Length N Real Input Sequence
Let g(n) be an N-point real sequence (N must be even). We want to compute the N-point complex FFT, but we only want to use an N/2-point FFT computation. We can accomplish this using the following steps.
- Form the the N/2-point complex valued sequence, x(n) = x_{1}(n) + jx_{2}(n), where x_{1}(n) = g(2n) and x_{2}(n) = g(2n + 1).
- Compute the N/2-point complex FFT on the complex valued sequence x(n) to obtain X(k) = FFT{x(n)}. Note that the FFT should be performed with bit reversal.
- Perform an additional computation to get G(k) from X(k)
G_{r}(k) = X_{r}(k)A_{r}(k) – X_{i}(k)A_{i}(k) + X_{r}(N/2–k)B_{r}(k) + X_{i}(N/2–k)B_{i}(k), for k = 0, 1, ..., N/2–1 and X(N/2) = X(0)
G_{i}(k) = X_{i}(k)A_{r}(k) + X_{r}(k)A_{i}(k) + X_{r}(N/2–k)B_{i}(k) – X_{i}(N/2–k)B_{r}(k)
Note that only N/2 points of the N-point sequence of G(k) are computed in the above equations. Because the FFT of a real-sequence has symmetric properties, we can easily compute the remaining N/2 points of G(k) with the following equations.
G_{r}(N/2) = X_{r}(0) – X_{i}(0) G_{i}(N/2) = 0 G_{r}(N–k) = G_{r}(k), for k = 1, 2, ..., N/2–1 G_{i}(N–k) = –G_{i}(k)
As you can see, the above equations require A(k) and B(k), which are sine and cosine coefficients. These values can be precomputed with the following C code, where even indices contains the real part and odd indices contain the imaginary part.
for (i = 0; i < N/2; i++) { A[2 * i] = 0.5 * (1.0 - sin (2 * PI / (double) (2 * N) * (double) i)); A[2 * i + 1] = 0.5 * (-1.0 * cos (2 * PI / (double) (2 * N) * (double) i)); B[2 * i] = 0.5 * (1.0 + sin (2 * PI / (double) (2 * N) * (double) i)); B[2 * i + 1] = 0.5 * (1.0 * cos (2 * PI / (double) (2 * N) * (double) i)); }
Returning to a Length N Real Sequence Using a Length N/2 Complex IFFT
Let G(k) be a N-point complex valued sequence derived from a real-valued sequence g(n). We want to get back g(n) = IFFT{G(k)}. However, we want to do this with an N/2-point IFFT. This can be accomplished using the following procedure.
X_{r}(k) = G_{r}(k)IA_{r}(k) – G_{i}(k)IA_{i}(k) + G_{r}(N/2–k)IB_{r}(k) + Gi(N/2–k)IB_{i}(k), for k = 0, 1, ..., N/2–1 and G(N/2) = G(0) X_{i}(k) = G_{i}(k)IA_{r}(k) + G_{r}(k)IA_{i}(k) + G_{r}(N/2–k)IB_{i}(k) – G_{i}(N/2–k)IB_{r}(k)
- Find X(k) using the above equations.
- Compute the N/2-point inverse FFT of X(k) to get x(n) = x_{1}(n) + jx_{2}(n). Note that the IFFT should be performed with bit reversal.
- Get g(n) from x(n) using the following equations.
g(2n) = x_{1}(n), for n = 0, 1, ..., N/2–1 g(2n+1) = x_{2}(n)
The above equations look similar to those used in our forward FFT computation. However, the pre-computed coefficients are slightly different. The following C code can be used to initialize IA(k) and IB(k), again, even indices contains the real part and odd indices contain the imaginary part.
for (i = 0; i < N/2; i++) { IA[2 * i] = 0.5 * (1.0 - sin (2 * PI / (double) (2 * N) * (double) i)); IA[2 * i + 1] = 0.5 * (1.0 * cos (2 * PI / (double) (2 * N) * (double) i)); IB[2 * i] = 0.5 * (1.0 + sin (2 * PI / (double) (2 * N) * (double) i)); IB[2 * i + 1] = 0.5 * (-1.0 * cos (2 * PI / (double) (2 * N) * (double) i)); }
Note that IA(k) is the complex conjugate of A(k) and IB(k) is the complex conjugate of B(k).
BenchMark
This table compares the performance of two methods of calculating the FFT of an N-point real sequence. Complex FFT refers to using an N-point complex FFT in the standard way, while Real FFT refers to using an N/2-point complex FFT as described in this topic. As expected, the Real FFT method yields superior performance.
Cycle Count | |||
---|---|---|---|
N | Complex FFT | Real FFT | Improvement |
128 | 1134 | 827 | 27.07% |
256 | 2094 | 1709 | 18.38% |
512 | 4944 | 3181 | 35.65% |
1024 | 9680 | 7055 | 27.11% |
2048 | 22770 | 13839 | 39.22% |
4096 | 45298 | 31025 | 31.50% |
8192 | 104724 | 61745 | 41.04% |