This example shows the BER performance improvement for QPSK modulation when using log-likelihood ratio (LLR) instead of hard-decision demodulation in a convolutionally coded communication link. With LLR demodulation, one can use the Viterbi decoder either in the unquantized decoding mode or the soft-decision decoding mode. Unquantized decoding, where the decoder inputs are real values, though better in terms of BER, is not practically viable. In the more practical soft-decision decoding, the demodulator output is quantized before being fed to the decoder. It is generally observed that this does not incur a significant cost in BER while significantly reducing the decoder complexity. We validate this experimentally through this example.
The first step is to set up system parameters for simulation.
bitsPerIter : Number of bits to simulate
EbNo : Information bit Eb/No in dB
codeRate : Code rate of convolutional encoder
constlen : Constraint length of encoder
codegenpoly : Code generator polynomial of encoder
tblen : Traceback depth of Viterbi decoder
M = 4; % Modulation alphabet size for QPSK k = log2(M); bitsPerIter = 1.2e4; EbNo = 3; % Code properties codeRate = 1/2; constlen = 7; codegenpoly = [171 133]; tblen = 32; trellis = poly2trellis(constlen, codegenpoly);
hConvEnc = comm.ConvolutionalEncoder(trellis);
Modulator and Channel
hMod = comm.QPSKModulator('BitInput',true); hDemodHD = comm.QPSKDemodulator('BitOutput',true,... 'DecisionMethod', 'Hard decision'); hDemodLLR = comm.QPSKDemodulator('BitOutput',true,... 'DecisionMethod', 'Log-likelihood ratio');
AWGNChannel System object. The signal going into the AWGN channel is the modulated encoded signal. To achieve the required Eb/No, adjust the signal-to-noise ratio for coded bits and multi-bit symbols and set this as the the EbNo of the channel object.
hChan = comm.AWGNChannel('NoiseMethod', 'Signal to noise ratio (Eb/No)',... 'SignalPower', 1, 'SamplesPerSymbol', 1); adjSNR = EbNo - 10*log10(1/codeRate) + 10*log10(k); hChan.EbNo = adjSNR;
hVitDecHD = comm.ViterbiDecoder(trellis, 'InputFormat', 'Hard',... 'TracebackDepth', tblen); hVitDecUNQANT = comm.ViterbiDecoder(trellis, ... 'InputFormat', 'Unquantized', 'TracebackDepth', tblen); hVitDecSD = comm.ViterbiDecoder(trellis, 'InputFormat', 'Soft',... 'SoftInputWordLength',3, 'TracebackDepth', tblen);
Quantization for soft-decoding
Before using a comm.ViterbiDecoder object in the soft-decision mode, the output of the comm.QPSKDemodulator object needs to be quantized. This example uses a comm.ViterbiDecoder object with a 'SoftInputWordLength' value of 3. This value is a good compromise between short wordlengths and a small BER penalty. Create a ScalarQuantizerEncoderScalarQuantizerEncoder System object with 3-bit quantization.
hScalQuant = dsp.ScalarQuantizerEncoder; NoiseVariance = 10.^(-adjSNR/10); hScalQuant.Partitioning = 'Unbounded'; hScalQuant.BoundaryPoints = (-1.5:0.5:1.5)/NoiseVariance;
Calculating the Error Rate
ErrorRate System objects to compare the decoded bits to the original transmitted bits. The Viterbi decoder creates a delay in the output decoded bit stream equal to the traceback length. To account for this delay set the 'ReceiveDelay' property of the comm.ErrorRate objects to 'tblen'.
hErrorCalcHD = comm.ErrorRate('ReceiveDelay', tblen); hErrorCalcUNQUANT = comm.ErrorRate('ReceiveDelay', tblen); hErrorCalcSD = comm.ErrorRate('ReceiveDelay', tblen);
Initialize variables for storing BER results and simulate the system.
ber_HD = zeros(3,1); ber_UNQUANT = zeros(3,1); ber_SD = zeros(3,1); % Generate 'bitsPerIter' message bits. data = randi([0 1], bitsPerIter, 1); % Convolutionally encode the data encData = step(hConvEnc, data); % Modulate the encoded data modData = step(hMod, encData); % Pass the modulated signal through an AWGN channel channelOutput = step(hChan, modData); % Hard-decision Demodulation demodDataHD = step(hDemodHD, channelOutput); % LLR Demodulation demodDataLLR = step(hDemodLLR, channelOutput); % Hard-decision decoding: Pass the demodulated data through the Viterbi % decoder; and compute and accumulate errors decDataHD = step(hVitDecHD, demodDataHD); ber_HD = step(hErrorCalcHD, data, decDataHD); % Unquantized decoding: Pass the demodulated data through the Viterbi % decoder; and compute and accumulate errors decDataUNQUANT = step(hVitDecUNQANT, demodDataLLR); ber_UNQUANT = step(hErrorCalcUNQUANT, data, decDataUNQUANT); % Soft-decision decoding: The demodulated data must pass through a % quantizer before being fed to the Viterbi decoder. However the output % from the demodulator must be sign-reversed before being fed to the % quantizer. This is because in the soft-decision decoding mode, the % comm.ViterbiDecoder object assumes that positive numbers correspond to 1s % and negative numbers to 0s. Thus the decoding operation consists of % feeding in the sign reversed data from the comm.PSKDemodulator object to % the dsp.scalarQuantizerEncoder object and feeding in the output from this % dsp.scalarQuantizerEncoder object to the comm.ViterbiDecoder. Compute and % accumulate errors. quantizedValue = step(hScalQuant, -demodDataLLR); decDataSD = step(hVitDecSD, double(quantizedValue)); ber_SD = step(hErrorCalcSD, data, decDataSD);
Running Simulation Example
The next step simulates the above designed communications system over a range of Eb/No values by executing the driver file SIMLLRVSHD. It plots BER results as they are generated. BER results for hard-decision demodulation and LLR demodulation with unquantized and soft-decision decoding are plotted in red, blue and black respectively. A comparison of simulation results with theoretical results is also shown. We see that the BER is only slightly degraded by using soft-decision decoding instead of unquantized decoding. The gap between the BER curves for soft-decision decoding and the theoretical bound can be narrowed by increasing the number of quantizer levels. This example may take some time to compute BER results, so we have set it to run in parallel if you have the Parallel Computing Toolbox™ (PCT) installed. To obtain results over a larger range of Eb/No values, modify this example file accordingly. More statistically reliable results can be obtained by collecting more errors which can be realized faster with the parallelized simulation.
[licensePCT,~] = license( 'checkout' , 'Distrib_Computing_Toolbox'); if ( licensePCT && ~isempty(ver('distcomp'))) LLRvsHDwithPCT(1.5:0.5:5.5,5); else simLLRvsHD(1.5:0.5:5.5,5); end
Starting parallel pool (parpool) using the 'local' profile ... connected to 12 workers.
The following functions are used in this example: