You are on page 1of 19

ELL319 Digital Signal Processing

Lab Report | Experiment 4

T Pratham - 2020EE10559
Afreen Haider - 2020EE10463

March 29, 2022

DFT, IDFT and FFT


1 Objectives
Learning to compute N point Discrete Fourier Transform, and Inverse Discrete Fourier Trans-
form for a sequence for a sequence and to plot magnitude and phase spectrum. Using DFT
and IDFT, perform linear and circular convolution of two sequences. Also learning about Fast
Fourier Transform and its usefulness in finding the frequency spectrum of signals.

Part A: To write a C program to compute the N point complex valued DFT of an array x[n].

Part B: To write a C program to compute IDFT of a complex valued sequence X(k)

Part C: To implement the convolution operation in time domain.

Part D: To write a C function to compute the FFT of a sample domain signal x[n].

Part E: To compare the running time of FFT in Part D with the DFT function in Part A.

2 Requirements:
1. CCS

3 Theory
3.1 Part A: Discrete Fourier Transform
The discrete Fourier Transform is used to find the amplitude and phase spectrum of a discrete
time sequence x[n] of length N as
N
X −1
X(k) = DF T (x[n]) = x[n]e−j2πkn/N ∀k ∈ {0, 1, · · · , N − 1} (1)
n=0

The real part of X(k) can be seen as the correlation between x and cosine function of frequency k
and the imaginary part as that between x and sine function of frequency k. Correlation between
two signals, by its very definition, can be seen as a metric of the similarity between the two
signals. Closer the frequencies of the two signals, higher the magnitude of correlation X(k) will

1
be. To get the frequencies in the signal, we require sampling frequency fs used to sample x[n].
The frequencies in the signal are then given by
fs · k
f= (2)
N

3.2 Part B: Inverse Discrete Fourier Transform


Given a frequency spectrum X(k), Inverse Discrete Fourier Transform is the inverse of the
operation given in equation (1). It yields the domain signal x[n] back. IDFT is given by
N −1
1 X
x[n] = X(k) · ej2πkn/N ∀k ∈ {0, 1, · · · , N − 1} (3)
N
k=0

3.3 Part C: Convolution of two signals using DFT and IDFT


Convolution of two signals x[n] and h[n] in time(or sample) domain is given by
N
X −1
y[n] = x[k]h[n − k] (4)
k=0

In frequency domain the convolution of the two signals corresponds to multiplication of the
Fourier transforms of the two signals, given by

Y (k) = X(k)H(k) (5)

where X(k) and H(k) are the Fourier transforms of x[n] and h[n] respectively.

3.4 Part D: Fast Fourier Transform


The time complexity of the DFT computing algorithm is of quadratic in nature, i.e., O(n2 )
which gets very inefficient in respect of large signals with a huge number of samples. Therefore
we use Fast Fourier Transforms, which are a class of algorithms that use divide and conquer
strategy to compute Fourier Transforms of smaller segments of the signal, and combine them
to compute the overall Fourier transform of the complete signal.

The most common type of algorithm used for Fast Fourier Transforms is the Radix-2 Dec-
imation In Time FFT. It assumes the signal size N to be a power of 2. Radix-2 DIT first
computes the DFTs of the even-indexed inputs (x2m = x0 , x2 , · · · xN −2 ) and of the odd indexed
inputs (x2m+1 = x1 , x3 , · · · xN −1 ) and then combines the two results to produce the DFT of the
whole sequence.
N/2−1 N/2−1
X −2πj X −2πj
(2m)k (2m+1)k
X(k) = x2m e N + x2m+1 e N (6)
m=0 m=0

If we denote the DFT of the even indexed inputs x2m by Ek and the DFT of the odd indexed
inputs x2m+1 by Ok , equation (6) above can be re-written as

X(k) = E(k) + e−2πjk/N O(k) (7)

Due to periodicity of the exponential, we can also simplify the expression for Xk+N/2 as

Xk+N/2 = E(k) − e−2πjk/N O(k) (8)

2
4 Procedure
4.1 Part A: Discrete Fourier Transform
• Equation (1) was modified using Euler’s theorem as:
N
X −1 N
X −1
X(k) = x[n] cos(−j2πkn/N ) − j x[n] sin(−j2πkn/N ) (9)
n=0 n=0

• x[n] was taken in as an array and its complex valued Discrete Fourier Transform was
calculated using the above equation (9)

4.2 Part B: Inverse Discrete Fourier Transform


• Inverse Discrete Fourier Transform of the signal X(k) obtained in Part A is calculated
using the formula given in equation (3).
• IDFT yields sample domain signal x[n] back.

4.3 Part C: Convolution


• In order to calculate the convolution of two signals x[n] and h[n], we first calculate the
N -point DFT of each of the signals in frequency domain, multiply the two as Y (k) =
X(k)H(k) and calculate the inverse DFT of Y(k) to obtain y[n].
• Both the signals x[n] of length L and h[n] of length K are made of length N = L + K − 1
and their N -point DFT Y (k) is calculated.
• N -point IDFT of Y (k) is calculated to get back the convoluted signal y[n]

4.4 Part D: Fast Fourier Transform


• Fast Fourier Transform is calculated using a recursive algorithm based on the equations
(6), (7), and (8).

4.5 Part E: Execution Time for Part A and Part D


• The running times for each of the algorithms are analytically calculated with exceptions
of constants and henceforth compared.

5 Code
# ifndef COMPLEX_H_
# define COMPLEX_H_

typedef struct Complex {


double re , im ;
} Complex ;

Complex add ( Complex x , Complex y ) ;


Complex sub ( Complex x , Complex y ) ;
Complex mul ( Complex x , Complex y ) ;

void mag ( int n , Complex x [] , double A []) ;


void phase ( int n , Complex x [] , double P []) ;

# endif /* COMPLEX_H_ */

Listing 1: Custom struct defined for handling complex numbers in header file complex.h

3
# include < math .h >

# include " complex . h "

# define pi 3.1415926535

Complex add ( Complex x , Complex y ) {


Complex z ;
z . re = x . re + y . re ;
z . im = x . im + y . im ;
return z ;
}

Complex sub ( Complex x , Complex y ) {


Complex z ;
z . re = x . re - y . re ;
z . im = x . im - y . im ;
return z ;
}

Complex mul ( Complex x , Complex y ) {


Complex z ;
z . re = x . re * y . re - x . im * y . im ;
z . im = x . im * y . re + x . re * y . im ;
return z ;
}

void mag ( int n , Complex x [] , double A []) {


int i =0;
for ( i =0; i < n ; ++ i ) {
A [ i ] = sqrt ( x [ i ]. re * x [ i ]. re + x [ i ]. im * x [ i ]. im ) ;
}
}

void phase ( int n , Complex x [] , double P []) {


int i =0;
for ( i =0; i < n ; ++ i ) {
if ( x [ i ]. re >= 0) {
P [ i ] = atan ( x [ i ]. im / x [ i ]. re ) ;
}
else if ( x [ i ]. im >= 0) {
P [ i ] = pi + atan ( x [ i ]. im / x [ i ]. re ) ;
}
else {
P [ i ] = atan ( x [ i ]. im / x [ i ]. re ) - pi ;
}
}
}

Listing 2: Implementation of complex.h

# include < math .h >

# include " complex . h "

# define pi 3.14159265

void dft ( int n , Complex x [] , Complex X []) {


int k =0 , i =0;

for ( k =0; k < n ; ++ k ) {


double t = (2.0* pi * k ) / n ;
X [ k ]. re = 0;
X [ k ]. im = 0;
for ( i =0; i < n ; ++ i ) {
X [ k ]. re += x [ i ]. re * cos ( t * i ) + x [ i ]. im * sin ( t * i ) ;
X [ k ]. im += x [ i ]. im * cos ( t * i ) - x [ i ]. re * sin ( t * i ) ;
}
}
}

Listing 3: Code for Part A

4
# include < math .h >

# include " complex . h "

# define pi 3.14159265

void idft ( int n , Complex X [] , Complex x []) {


int k =0 , i =0;

for ( k =0; k < n ; ++ k ) {


double t = (2.0* pi * k ) / n ;
x [ k ]. re = 0;
x [ k ]. im = 0;
for ( i =0; i < n ; ++ i ) {
x [ k ]. re += ( X [ i ]. re * cos ( t * i ) - X [ i ]. im * sin ( t * i ) ) / n ;
x [ k ]. im += ( X [ i ]. im * cos ( t * i ) + X [ i ]. re * sin ( t * i ) ) / n ;
}
}
}

Listing 4: Code for Part B

# include < math .h >

# include " complex . h "

# define pi 3.14159265

extern void dft ( int , Complex [] , Complex []) ;


extern void idft ( int , Complex [] , Complex []) ;

void conv ( int n , Complex x [] , Complex h [] , Complex y []) {


// Convolution using DFT and IDFT
Complex x_ [100] , h_ [100];
int i =0;
for ( i =0; i < n ; ++ i ) {
x_ [ i ] = x [ i ];
h_ [ i ] = h [ i ];
}
for ( i = n ; i <2* n -1; ++ i ) {
x_ [ i ]. re = 0;
h_ [ i ]. re = 0;
x_ [ i ]. im = 0;
h_ [ i ]. im = 0;
}

Complex X [100] , H [100];


dft (2* n -1 , x_ , X ) ;
dft (2* n -1 , h_ , H ) ;

Complex x22 [100] , h22 [100];


idft (2* n -1 , X , x22 ) ;
idft (2* n -1 , H , h22 ) ;

Complex Y [100];
for ( i =0; i <2* n -1; ++ i ) {
Y [ i ]. re = X [ i ]. re * H [ i ]. re - X [ i ]. im * H [ i ]. im ;
Y [ i ]. im = X [ i ]. im * H [ i ]. re + X [ i ]. re * H [ i ]. im ;
}

idft (2* n -1 , Y , y ) ;
}

void convVerify ( int n , Complex x [] , Complex h [] , Complex y []) {


// directly implementing convolution in time domain assuming
// real valued signal , for testing purpose only .
int i =0 , k =0;
for ( i =0; i <2* n -1; ++ i ) {
y [ i ]. re = 0;
for ( k =0; k <= i && k < n ; ++ k ) {
if (i -k < n )
y [ i ]. re += x [ k ]. re * h [i - k ]. re ;
}

5
}
}

Listing 5: Code for Part C

# include < math .h >

# include " complex . h "

# define pi 3.14159265

extern void dft ( int , Complex [] , Complex []) ;

void fft ( int n , Complex x [] , Complex X []) {


if ( n ==1) {
X [0] = x [0];
}
else {
Complex x1 [100] , x2 [100];
Complex X1 [100] , X2 [100];
int i =0;
for ( i =0; i < n /2; i +=1) {
x1 [ i ] = x [2* i ];
x2 [ i ] = x [2* i +1];
}
fft ( n /2 , x1 , X1 ) ;
fft ( n /2 , x2 , X2 ) ;
for ( i =0; i < n /2; ++ i ) {
X [ i ] = X1 [ i ];
}
for ( i = n /2; i < n ; ++ i ) {
X [ i ] = X2 [i - n /2];
}

double t = 2.0* pi / n ;
Complex temp ;
for ( i =0; i < n /2; ++ i ) {
temp . re = X [ i ]. re ;
temp . im = X [ i ]. im ;
X [ i ]. re = temp . re + X [ i + n /2]. re * cos ( t * i ) + X [ i + n /2]. im * sin ( t * i ) ;
X [ i ]. im = temp . im + X [ i + n /2]. im * cos ( t * i ) - X [ i + n /2]. re * sin ( t * i ) ;
X [ i + n /2]. re = temp . re - X [ i + n /2]. re * cos ( t * i ) - X [ i + n /2]. im * sin ( t * i ) ;
X [ i + n /2]. im = temp . im - X [ i + n /2]. im * cos ( t * i ) + X [ i + n /2]. re * sin ( t * i ) ;
}
}
}

Listing 6: Code for Part D

# include " complex . h "

extern void fft ( int , Complex [] , Complex []) ;


extern void dft ( int , Complex [] , Complex []) ;
extern void idft ( int , Complex [] , Complex []) ;
extern void conv ( int , Complex [] , Complex [] , Complex []) ;
extern void convVerify ( int , Complex [] , Complex [] , Complex []) ;

int main ( void ) {


Complex x [16] , X1 [16] , X2 [16];
int i =0;

// Taking input sequence as {0 , 1 , 2 , ... , 14 , 15}


for ( i =0; i <16; ++ i ) {
x [ i ]. re = i ;
x [ i ]. im = 0;
}
fft (16 , x , X1 ) ; // FFT
dft (16 , x , X2 ) ; // DFT

// Magnitude response of FFT and DFT


double A1 [16] , A2 [16];

6
mag (16 , X1 , A1 ) ;
mag (16 , X2 , A2 ) ;

// Phase response of DFT and DFT


double P1 [16] , P2 [16];
phase (16 , X1 , P1 ) ;
phase (16 , X2 , P2 ) ;

// IDFT of DFT of the input sequence


Complex x2 [16];
idft (16 , X1 , x2 ) ;

// Convolution by taking DFT and


// multiplying and then taking IDFT
Complex h [16] , y [31]; // y2 [31];
// Taking second sequence as {1 , 1 , 1 , ... , 1}
for ( i =0; i <16; ++ i ) {
h [ i ]. re = 1;
h [ i ]. im = 0;
}

conv (16 , x , h , y ) ;
// convVerify (16 , x , h , y2 ) ;
return 0;
}

Listing 7: main function calling the other functions

7
6 Graphs
6.1 Part A

The graphs in Figure 1a show the Magnitude Response and Figure 1b shows the Phase Response
of the DFT X[k] of the signal input signal x[n].

(a) Magnitude Response

(b) Phase Response

Figure 1: DFT X[k] of input signal x[n]

8
6.2 Part D

The graph in Figure 2a shows the Magnitude Response and Figure 2b shows the Phase Response
of the FFT X2[k] of the signal input signal x[n].

(a) Magnitude Response

(b) Phase Response

Figure 2: FFT X2[k] of input signal x[n]

9
7 Observations
1. The result of IDFT x2[n] matches with the input signal x[n] within domain of floating-
point error.

Figure 3: Input signal x[n]

10
Figure 4: IDFT x2[n] of X[k]

11
2. The discrete Fourier transform calculated using DFT formula and FFT match within
floating-point error bounds. Their magnitude and phase responses have been plotted
above in Figure 1 and Figure 2. (PS: The phase responses don’t match at k=8 due to
floating point error around 0. The value is a very small positive float in one and negative
in other. Hence, the phases come as −π and π.)

3. For Part C, the first signal taken is x[n] (same as shown above, used in Part A). The
second signal h[n] taken is shown below (Figure 8). The value of convolution of x[n] and
h[n] is computed and as shown below in Figure 11:

12
Figure 5: DFT X[k] of x[n]

13
Figure 6: FFT X2[k] of x[n]

14
Figure 8: Signal h[n]

15
16
17
PN −1
Figure 11: y[n] = i=0 x[i]h[n − i] = IDFT(X[k]H[k])[n]

4. DFT algorithm requires a nested loop with two stages, each of length n. In each iteration
of the outer loop, there are n iterations of the inner loop. If we consider the time taken
for each innermost loop’s iteration to be c, then the total time taken for the nested loop
to run is cn2 . Other operations in the algorithm are of constant running time.
Therefore the running time of the algorithm is of the order of n2

5. For the FFT algorithm, of the time taken for the algorithm to run for a signal of length
n is given by T (n), then the recursive equation for the running time is given by

T (n) = c1 (n/2) + 2T (n/2) + 2c2 n/2 + c3 (n/2)


(10)
= 2T (n/2) + C(n)

From the equation we obtain T (n) to be of the form pnlogn where p is a constant.

18
8 Result
1. DFT has been calculated for a sequence x[n] and its magnitude and phase response has
been plotted in the Graphs section above (Figure 1).

2. IDFT has been calculated and result shown above in observations in Figure 4. The IDFT
matches the original input signal within error bounds.

3. Time-domain convolution has been calculated correctly using DFT and IDFT and has
been demonstrated above in the observations in Figure 11.

4. FFT algorithm has been implemented correctly and has been demonstrated in previous
sections. The Discrete Fourier Transform calculated using FFT matches the one calculated
using DFT formula in Part A. The magnitude and phase response has been shown for
FFT as well in Graphs section above (Figure 2).

5. From the comparison between the running times of DFT and FFT we obtain that FFT
is a more efficient algorithm with a running time that is less than that of DFT.

19

You might also like