Compute the theoretical BER for AWGN channel and various constellation size
Based on the previous skeleton, we can now compute an iterative testbench to compute the Bit Error Rate for various constellation size, and compare the simulation with the theory. As a gentle reminder, the theoretical bit error rate can be approximated as
$\mathrm{BER} = \frac{ 4 \left( 1 - \frac{1}{\sqrt{M}} \right) }{ \log_2(M)} \times Q( \sqrt{ \frac{6 \log_2(M)}{2(M-1)} \frac{Eb}{N_0}}$
First of all let's call the modules
using DigitalComm
using PGFPlotsX
We define first the main monte-carlo function that compute an elementary Tx-Rx link, and returns the number of error and number of bit computed (to be accumulated)
function monteCarlo(snr,mcs,nbSymb)
# Number of bits
nbBits = nbSymb * Int(log2(mcs));
# --- Binary sequence generation
bitSeq = genBitSequence(nbBits);
# --- QPSK mapping
qamSeq = bitMappingQAM(mcs,bitSeq);
# ----------------------------------------------------
# --- Channel
# ----------------------------------------------------
# --- AWGN
# Theoretical power is 1 (normalized constellation)
qamNoise, = addNoise(qamSeq,snr,1);
# ----------------------------------------------------
# --- Rx Stage: SRRC
# ----------------------------------------------------
# --- Binary demapper
bitDec = bitDemappingQAM(mcs,qamNoise);
# --- Error counter
nbE = sum(xor.(bitDec,bitSeq));
# --- Return Error and bits
return (nbE,nbBits);
end
A function to plot the BER versus the SNR, for different mcs and compare to theory
function doPlot(snrVect,ber,qamVect)
a = 0;
@pgf a = Axis({
ymode = "log",
height ="3in",
width ="4in",
grid,
xlabel = "SNR [dB]",
ylabel = "Bit Error Rate ",
ymax = 1,
ymin = 10.0^(-5),
title = "AWGN BER for QAM",
legend_style="{at={(0,0)},anchor=south west,legend cell align=left,align=left,draw=white!15!black}"
},
Plot({color="red",mark="square*"},Table([snrVect,ber[1,:]])),
LegendEntry("QPSK"),
Plot({color="green",mark="*"},Table([snrVect,ber[2,:]])),
LegendEntry("16-QAM"),
Plot({color="purple",mark="triangle*"},Table([snrVect,ber[3,:]])),
LegendEntry("64-QAM"),
Plot({color="blue",mark="diamond*"},Table([snrVect,ber[4,:]])),
LegendEntry("256-QAM"),
);
# --- Adding theoretical curve
snrLin = (10.0).^(snrVect/10)
for qamScheme = qamVect
ebNo = snrLin / log2(qamScheme);
# This approximation is only valid for high SNR (one symbol error is converted to one bit error with Gray coding).
berTheo = 4 * ( 1 - 1 / sqrt(qamScheme)) / log2(qamScheme) * qFunc.(sqrt.( 2*ebNo * 3 * log2(qamScheme) / (2*(qamScheme-1) )));
@pgf push!(a,Plot({color="black"},Table([snrVect,berTheo])));
end
display(a);
end
Then, the main routine to compute the BER for a given number of iterations and a range of SNR
function main()
# --- Parameters
nbIt = 10000; # Number of iterations
nbSymb = 1024; # Number of symbols per iterations
mcs = [4,16,64,256]; # Constellation size
snrRange = (-1:26); # SNR, expressed in dB
# --- Init performance metrics
nbSNR = length(snrRange);
ber = zeros(Float64,length(mcs),nbSNR);
for iMcs = 1 : 1 : length(mcs)
for iSNR = 1 : 1 : nbSNR
# --- Create BER counters
nbE = 0;
nbB = 0;
for iN = 1 : 1 : nbIt
# --- Elementary MC call
# Corresponds to a given SNR and a given iteration
# As we are ergodic in AWGN, it is only nbSymb*nbIt that matters for BER computation
(a,b) = monteCarlo(snrRange[iSNR],mcs[iMcs],nbSymb);
# --- Update counters
nbE += a; # Increment errors
nbB += b; # Increment bit counters
end
ber[iMcs,iSNR] = nbE / nbB;
end
end
# --- Plotting routine
doPlot(snrRange,ber,mcs);
end
The output plot is the following, showing adequacy between theory and practise for high SNR (the theoretical curve is under the assumption that one symbol error leads to one erroneous bit (gray coding) which is true only with intermediate noise levels).