clear all
clc
close   %   closes any open figures
%
%   OFDM System Version 0.12
%   Created on September 12, 2004 by Surujlal Dasrath
%
%   ** New in Version 0.12 **
%   Transmitter
%   1.  Function (do_padding) to do padding if the serial data cannot be
%       segmented into an integer number of blocks
%   2.  Create blocks of data points from the serial stream, in matrix
%       form where after IFFT & Cyclic Prefixing (CP), each block
%       will become an OFDM block
%   3.  Add CP to exisiting blocks of data
%   4.  Convert martix of blocks of data & CP into an OFDM signal for TX
%
%   Receiver
%   5.  On the RX end, add a multipath channel via "filer" command
%   6.  Remove CP, do FFT & demodulate

%   ---------------
%   A:  Definitions
%   ---------------
M = 4;                          %   QPSK signal constellation
no_of_data_points = 16;         %   have 128 data points
block_size = 8;                 %   size of each ofdm block
cp_len = ceil(0.1*block_size);  %   length of cyclic prefix
no_of_ifft_points = block_size; %   128 points for the FFT/IFFT
no_of_fft_points = block_size;

fsymb = 1;    %% Symbolfrequenz
N = 1;        %%Überabtastfaktor
fa = N*fsymb; %% die Abtastfrequenz
K=2;          %% Wertigkeit der Modulation K=1 PSK-2, K=2: PSK-4
%   ---------------------------------------------
%   B:  %   +++++   TRANSMITTER HEAD END    +++++
%   ---------------------------------------------
%   1.  Generate 1 x 128 vector of random data points
data_source = randsrc(1, no_of_data_points, 0:M-1);
figure(1)
stem(data_source); grid on; xlabel('Data Points'); ylabel('Amplitude')
title('Transmitted Data "O"')

%   2.  Call 'do_padding' function to check if an integer number of blocks
%       can be generated from block_size as specified by the user. If not,
%       zero-pad the stream to make this true
%[padded_data no_of_blocks]=do_padding(data_source, block_size);
padded_data = data_source;

%   3.  Perform QPSK modulation
%qpsk_modulated_data = pskmod(padded_data, M);
qpsk_modulated_data = dmodce(data_source,fsymb,fa,'psk',2^K);
%scatterplot(qpsk_modulated_data);title('QPSK Modulation')

%   4.  Do IFFT on each block
%   Make the serial stream a matrix where each column represents a pre-OFDM
%   block (w/o cyclic prefixing)
%   First: Find out the number of colums that will exist after reshaping
num_cols=length(qpsk_modulated_data)/block_size;
padded_data_matrix = reshape(qpsk_modulated_data, block_size, num_cols);

%   Second: Create empty matix to put the IFFT'd data
cp_start = block_size-cp_len;
cp_end = block_size;

%   Third: Operate columnwise & do CP
for i=1:num_cols,
    ifft_data_matrix(:,i) = ifft((padded_data_matrix(:,i)),no_of_ifft_points);
    %   Compute and append Cyclic Prefix
    for j=1:cp_len,
       actual_cp(j,i) = ifft_data_matrix(j+cp_start,i);
    end
    %   Append the CP to the existing block to create the actual OFDM block
    ifft_data(:,i) = vertcat(actual_cp(:,i),ifft_data_matrix(:,i));
end

%   5.  Convert to serial stream for transmission
[rows_ifft_data cols_ifft_data]=size(ifft_data);
len_ofdm_data = rows_ifft_data*cols_ifft_data;

%   Actual OFDM signal to be transmitted
ofdm_signal = reshape(ifft_data, 1, len_ofdm_data);
figure(3)
plot(real(ofdm_signal)); xlabel('Time'); ylabel('Amplitude');
title('OFDM Signal');grid on;

%   --------------------------------
%   C:  %   +++++   CHANNEL    +++++
%   --------------------------------
%   Create a complex multipath channel
%+++channel = randn(1,block_size) + sqrt(-1)*randn(1,block_size);
%channel = randn(1,block_size) + sqrt(-1)*randn(1,block_size);

%   ------------------------------------------
%   D:  %   +++++   RECEIVER TAIL END    +++++
%   ------------------------------------------
%
%   1.  Generate random complex noise
%+++noise = randn(1,len_ofdm_data) +  sqrt(-1)*randn(1,len_ofdm_data);
%noise = randn(1,len_ofdm_data) +  sqrt(-1)*randn(1,len_ofdm_data);

%   2.  Pass the ofdm signal through the channel
%++after_channel = filter(channel, 1, ofdm_signal);
%after_channel = filter(channel, 1, ofdm_signal);

%   3.  Add noise to signal...
%++recvd_signal = after_channel + noise;
recvd_signal = ofdm_signal;
%recvd_signal = after_channel + noise;

%   4.  Convert Data back to "parallel" form to perform FFT
recvd_signal_matrix = reshape(recvd_signal,rows_ifft_data, cols_ifft_data);

%   5.  Remove CP
recvd_signal_matrix(1:cp_len,:)=[];

%   6.  Perform FFT
for i=1:cols_ifft_data,
    %   FFT
    fft_data_matrix(:,i) = fft(recvd_signal_matrix(:,i),no_of_fft_points);
end

%   7.  Convert to serial stream
recvd_serial_data = reshape(fft_data_matrix, 1,(block_size*num_cols));

%   8.  Demodulate the data
%qpsk_demodulated_data = pskdemod(recvd_serial_data,M);
qpsk_demodulated_data = ddemodce(recvd_serial_data,fsymb,fa,'psk',2^K);

figure(4)
stem(data_source)
hold on
stem(qpsk_demodulated_data,'rx');
grid on;xlabel('Data Points');ylabel('Amplitude');title('Received Data "X"')   


