%% HDL OFDM MATLAB References
% This example shows how to model OFDM transmitter, additive white Gaussian
% noise (AWGN), and OFDM receiver hardware algorithms in MATLAB as a step
% towards developing a Simulink(R) HDL implementation. The HDL OFDM
% MATLAB(R) References example bridges the gap between a mathematical
% algorithm and its hardware implementation. This example provides MATLAB
% references of the HDL OFDM Transmitter, HDL AWGN, and HDL OFDM Receiver
% algorithms that you can implement on hardware. You can use these MATLAB
% references to generate test vectors for verifying the HDL implementation
% of these Simulink models,
% <docid:whdl_ug#mw_db7bad5f-6c70-4959-bae5-32040f46f063 HDL OFDM
% Transmitter>, <docid:whdl_ug#mw_b5412050-23e7-4680-b6e1-8b108069a167 HDL
% Implementation of AWGN Generator> and
% <docid:whdl_ug#mw_081fbaf8-7329-465c-b90c-d8456e7c6637 HDL OFDM
% Receiver>.
% 
% 
% <<../matSimVerfBlkDiag.png>>
%%

% Copyright 2020 The MathWorks, Inc.

%% HDL OFDM Transmitter MATLAB Reference
% This section describes the MATLAB reference of HDL OFDM Transmitter.
%
% This MATLAB reference accepts a modulation order, code
% rate index, number of frames, and data bits to be transmitted as a
% |txParam| structure or array of structures. |txParam| has these fields.
%
% * |modOrder| &mdash; Specify 2, 4, 16, or 64 for 'BPSK', 'QPSK', '16QAM',
%    and '64QAM', respectively. The default value is 4 ('QPSK').
% * |codeRateIndex| &mdash; Specify 0, 1, 2, or 3 for the rates '1/2',
% '2/3', '3/4',
%   and '5/6' respectively. The default value is 0 ('1/2').
% * |numFrames| &mdash; Specify a positive integer. The default value is 5.
% * |txDataBits| &mdash; Specify binary values in a row or column vector of
%   length |trBlkSize| * |txParam.numFrames|. The default is a column
%   vector containing randomly generated binary values of length
%   |trBlkSize| * |txParam.numFrames|.
% 
% Calculate the transport block size (|trBlkSize|) by using these
% parameters.
%
% * |numSubCar| &mdash; Number of subcarriers per symbol
% * |pilotsPerSym| &mdash; Number of pilots per symbol
% * |numDataOFDMSymbols| &mdash; Number of data OFDM symbols
% * |bitsPerModSym| &mdash; Number of bits per modulated symbol
% * |codeRate| &mdash; Punctured code rate
% * |dataConvK| &mdash; Constraint length of the convolutional encoder
% * |dataCRCLen| &mdash; CRC length
%
%  trBlkSize =
% ((numSubCar-pilotsPerSym)*numDataOFDMSymbols*bitsPerModSym*codeRate)
% -(dataConvK-1)-dataCRCLen
% 
% 
%%
% For example, to generate a time-domain OFDM transmitter waveform of 5
% frames with a modulation scheme of 16QAM and code rate of 1/2 using random
% data bits in the transport block, format the inputs as structure.
%%
txParam.modOrder = 16; % Modulation order corresponding to 16-QAM
txParam.codeRateIndex = 0; % Code rate index corresponding to 1/2 
txParam.numFrames = 5; % Number of frames to be generated

% Calculate transport block size (trBlkSize) using parameters
numSubCar = 72; % Number of subcarriers per symbol
pilotsPerSym = 12; % Number of pilots per symbol 
numDataOFDMSymbols = 32; % Number of data OFDM symbols
bitsPerModSym = log2(txParam.modOrder); % Bits per modulated symbol
codeRate = 1/2; % Punctured code rate 
dataConvK = 7; % Constraint length of convolutional code polynomial
dataCRCLen = 32; % Data CRC length
trBlkSize = ((numSubCar-pilotsPerSym)*numDataOFDMSymbols* ...
    bitsPerModSym*codeRate) - (dataConvK-1) - dataCRCLen;
txParam.txDataBits = randi([0 1],txParam.numFrames*trBlkSize,1);

% Generate complex baseband transmitter waveform
fprintf('\n-------------------------\n');
fprintf('\n Transmitting %d frames ...\n',sum(txParam.numFrames));
[txWaveform,txGrid,txDiagnostics] = whdlexamples.OFDMTx(txParam);
fprintf('\n Transmission successful\n');
fprintf('\n-------------------------\n');
%%
% 
% 
% |whdlexamples.OFDMTx| function returns |txWaveform|, |txGrid|, and
% |txDiagnostics| are described below.
% 
% * |txWaveform| is the generated time-domain waveform returned as a column
% vector of length (((|fftLen| + |cpLen|) x |txParam.numFrames| x
% |numSymPerFrame|) + (|txFilterLen| - 1)), where
%
    % * |fftLen| is the FFT length.
    % * |cpLen| is the cyclic prefix length.
    % * |numSymPerFrame| is the number of OFDM symbols per frame.
    % * |txFilterLen| is the transmitter filter length.
%
% If |txParam| is an array of structures, then |txParam.numFrames| is
% replaced with the sum of all |numFrames| attributes present in the array.
% The frame structure of the generated time-domain waveform |txWaveform| is
% similar to the Simulink HDL OFDM Transmitter output waveform. For the
% detailed explanation of the frame structure, see
% <docid:whdl_ug#mw_db7bad5f-6c70-4959-bae5-32040f46f063 HDL OFDM
% Transmitter>.
% 
% 2. |txGrid| is the transmitter grid output and is returned as a matrix of
% dimension |numSubCar|-by-(|txParam.numFrames| x |numSymPerFrame|).
%
% 3. |txDiagnostics| is a structure or array of structures and consists of
% these three fields.
%
    % * |headerBits| &mdash; This field represents the header bits as a column
    % vector of size 22,
    % which includes 3 bits for the FFT length index, 2 bits for the symbol modulation
    % type, 2 bits for the code rate index, and 15 spare bits.
    % * |dataBits| &mdash; This field represents actual data bits transmitted in the given
    % number of frames (|txParam.numFrames|). This field is a binary row or
    % column vector of length (|txParam.numFrames| x |trbBlkSize|). The row or
    % column vector depends on the dimension of |txparam.dataBits|. The default
    % size is a column vector of length |txParam.numFrames| x |trbBlkSize|.
    % * |ofdmModOut| &mdash; This field epresents the OFDM modulator output as a column vector
    % of length
    %  (|fftLen| + |cpLen|) x |txParam.numFrames| x |numSymPerFrame|.
%%
% *|OFDMTx|*
%
% |whdlexamples.ofdmTx| function is used to generate OFDM transmitter
% waveform with synchronization, reference, header, pilots, and data
% signals. This function returns |txWaveform|, |txGrid|, and diagnostics using
% transmitter parameters |txParam|. This function internally calls
% these individual functions.
% 
% 
% * |generateOFDMSyncSignal| &mdash; This function generates the synchronization signal
% |SyncSignal|. This function uses Zadoff-Chu sequence with a root index of 25
% and length of 62.
%
% 
% * |generateOFDMRefSignal| &mdash; This function generates the reference signal |refSignal| for
% the given FFT length |fftLen|. This function uses a BPSK-modulated pseudo
% random binary sequence.
%
% 
% * |generateOFDMPilotSignal| &mdash; This function generates the pilot signal |pilot|. This
% function uses a BPSK-modulated pseudo random binary sequence.
% 
% 
% * |OFDMSymbolModulate| &mdash; This function modulates input bits to complex modulation
% symbols based on the specified modulation scheme
% BPSK, QPSK, 16QAM, and 64QAM.
%%
% 
% Plot the resource grid of the transmitter waveform. The plot indicates
% the magnitude variations of each resource grid elements.

%%
plotResourceGrid(txGrid);

 
%% HDL AWGN MATLAB Reference
% This section describes the MATLAB reference of HDL AWGN. 
%
% This MATLAB reference is used for performance evaluation of the HDL OFDM
% Transmitter and Receiver algorithms. The HDL AWGN MATLAB reference generates AWGN by
% accepting the signal-to-noise ratio (SNR) in decibel (dB) and sets of seeds.
% For more details, see <docid:whdl_ug#mw_b5412050-23e7-4680-b6e1-8b108069a167 HDL
% Implementation of AWGN Generator>. The generated AWGN is
% added to the HDL OFDM Transmitter output.

FFTLen = 128;
CPLen = 32;
usedSubCarr = 72; % Out of 128 subcarriers, 72 subcarriers are loaded with data 

SNRdB = 30;
SNRdBSimInput = SNRdB*ones(length(txWaveform)+633,1);
seedsURNG1 = [121 719 511]; % Seeds for TausURNG1
seedsURNG2 = [2343 323 833]; % Seeds for TausURNG2
txScaleFactor = FFTLen/sqrt(usedSubCarr);

awgnNoise = whdlexamples.hdlawgn(SNRdBSimInput,seedsURNG1,seedsURNG2);

rxWaveform = txWaveform + (1/txScaleFactor)*awgnNoise(634:end);
fprintf('\n Applying the AWGN channel at %d dB\n', SNRdB);



%% HDL OFDM Receiver MATLAB Reference
% This section describes MATLAB reference of HDL OFDM Receiver.
% 
% This MATLAB reference includes time synchronization, CFO
% estimation and correction, OFDM demodulation, header recovery, CPE
% estimation and correction, and data recovery.
% 
% The |whdlexamples.OFDMRx| function accepts |rxWaveform|, a transmitted waveform
% passed through an AWGN channel.
% 
%
% The |whdlexamples.OFDMRx| function returns decoded bits |rxBits| and an array
% of structures, |rxDiagnostics|, consisting of these eight fields. 
%
    % * |estCFO| &mdash; Estimated carrier frequency offset
    % * |rxConstellationHeader| &mdash; Demodulated header constellation symbols
    % * |rxConstellationData| &mdash; Demodulated data constellation symbols
    % * |softLLR| &mdash; Demodulated soft LLR bits
    % * |decodedCodeRateIndex| &mdash; Decoded code rate index from header
    % * |decodedModOrder| &mdash; Decoded modulation order from header
    % * |headerCRCErrorFlag| &mdash; Status of header CRC 
    % * |dataCRCErrorFlag| &mdash; Status of data CRC 
%
% *|OFDMRx|*
%
% The |whdlexamples.OFDMRx| function is used to demodulate and decode the
% received |rxWaveform|. This function internally calls these individual
% functions.
%
% * |OFDMFrequencyOffset| &mdash; This function estimates the carrier frequency
% offset based on cyclic prefix (CP) technique. The cyclic prefix portion
% of the received time-domain waveform is correlated to estimate frequency
% offset.
%
% * |OFDMFrequencyCorrect| &mdash; This function corrects the carrier frequency
% offset on the received waveform using the estimated frequency offset.
%
% * |OFDMFrameSync| &mdash; This function synchronizes the received waveform by
% performing correlation using the reference signal. This step reduces the
% intersymbol interference while demodulating the received waveform.
%
% * |OFDMDemodulation| &mdash; This function converts the time-domain waveform to
% frequency-domain waveform for further decoding. The object |dsp.HDLFFT|
% is used for HDL implementation of the receiver.
%
% * |OFDMChannelEstimation| &mdash; This function performs the estimation of the
% channel using two reference signals. It uses least squares (LS)
% estimation technique. LS estimates are averaged to improve channel
% estimation accuracy.
%
% * |OFDMChannelEqualization| &mdash; This function performs zero forcing (ZF) equalization using
% estimated channel. Then the received waveform that is free of the channel is used for
% header recovery and data recovery.
%
% * |OFDMHeaderRecovery| &mdash; This function recovers header information by
% performing symbol demodulation, descrambling, and decoding. The success or
% failure of header information recovery is indicated by the CRC status. This
% header recovery CRC status is given as an output to the receiver to indicate
% frame loss or recovery. When the CRC check
% fails, the header CRC status is |1|. Otherwise, it is |0|.
%
% * |OFDMDataRecovery| &mdash; This function performs symbol demodulation, Viterbi decoding,
% depuncturing, and descrambling. The data is processed only when the header
% CRC check passes. After descrambling, CRC check on the recovered
% data bits to indicate whether the packet is errored. When the CRC check
% fails, the header CRC status is |1|. Otherwise, it is |0|.
%     

fprintf('\n Receiving process started...\n');
[rxDataBits,rxDiagnostics] = whdlexamples.OFDMRx(rxWaveform);
fprintf('\n Reception completed\n\n');

% Plot constellation of header and data
scatterplot(rxDiagnostics.rxConstellationHeader(:))
title('Header Constellation')

scatterplot(rxDiagnostics.rxConstellationData(:))
title('Data Constellation');

%% Verify Simulink Model with MATLAB Reference
%
% In this section, Simulink HDL OFDM Transmitter, AWGN generator, and
% Simulink HDL OFDM Receiver algorithms implemented in fixed point are compared with
% the equivalent MATLAB HDL reference models implemented in floating point.
%
% The Simulink model consists of an OFDM Transmitter that generates
% time-domain waveform for a user-defined modulation order and code rate. The
% waveform is then passed through the AWGN channel that introduces AWGN
% noise of the desired SNR in dB. Then, the OFDM Receiver algorithm is used to demodulate
% and decode information bits. The outputs of the Simulink model are
% verified with the MATLAB reference at each stage.
%%

open HDLOFDMTxRx;
sim HDLOFDMTxRx;
%%
% *Verify Simulink HDL OFDM Transmitter with MATLAB HDL OFDM Transmitter*
%
% In this section, plot the real and imaginary parts of the HDL OFDM Transmitter
% MATLAB reference function output |txWaveform| as compared with the output
% of the <docid:whdl_ug#mw_db7bad5f-6c70-4959-bae5-32040f46f063 HDL OFDM
% Transmitter> block. 

%%
matlabTxWaveform = txWaveform;
simulinkTxWaveform = simTxOut;

figure;
plot(real(matlabTxWaveform),'-bo')
hold on
plot(real(simulinkTxWaveform(1:length(matlabTxWaveform))),'-r.')
legend('MATLAB Tx waveform','Simulink Tx waveform');
title('Comparison of MATLAB Tx and Simulink Block Tx (Real Part)');
ylim([-0.2 0.2]);
xlabel('Time-Domain Samples');
ylabel('Amplitude');

figure;
plot(imag(matlabTxWaveform),'-bo')
hold on
plot(imag(simulinkTxWaveform(1:length(matlabTxWaveform))),'-r.')
legend('MATLAB Tx waveform','Simulink Tx waveform');
title('Comparison of MATLAB Tx and Simulink Block Tx (Imaginary Part)');
ylim([-0.2 0.2]);
xlabel('Time-Domain Samples');
ylabel('Amplitude');

%%
% *Verify Simulink HDL AWGN Generator with MATLAB HDL AWGN*
%
% In this section, plot the real and imaginary parts of the MATLAB HDL AWGN is
% compared with the output of the Simulink AWGN Generator block.
matlabChannelOut= rxWaveform;
simulinkChannelOut = simChannelOut;

figure;
plot(real(matlabChannelOut),'-bo');
hold on;
plot(real(simulinkChannelOut(1:length(matlabChannelOut))),'-r.');
legend('MATLAB channel output','Simulink channel output');
title('Comparison of MATLAB Channel and Simulink Channel (Real Part)');
ylim([-0.2 0.2]);
xlabel('Time-Domain Samples');
ylabel('Amplitude');


figure;
plot(imag(matlabChannelOut),'-bo');
hold on;
plot(imag(simulinkChannelOut(1:length(matlabChannelOut))),'-r.');
legend('MATLAB channel output','Simulink channel output');
title('Comparison of MATLAB Channel and Simulink Channel (Imaginary Part)');
ylim([-0.2 0.2]);
xlabel('Time-Domain Samples');
ylabel('Amplitude');

%%
% *Verify Simulink HDL OFDM Receiver with MATLAB HDL OFDM Receiver*
%
% In this section, plot the decoded bits of the MATLAB receiver as compared with the
% decoded bits of the Simulink receiver.
matlabRxOut= rxDataBits;
simulinkRxOut = simRxDataBits;

figure;
plot(rxDataBits,'-bo');
hold on;
plot(simulinkRxOut(1:length(rxDataBits)),'-r.');
legend('MATLAB Rx bits','Simulink Rx bits');
title('MATLAB and Simulink decoded bits');
ylim([-0.25 1.25]);
xlabel('Time-domain samples');
ylabel('Amplitude');

