
CONVOLUTIONAL CODING
In telecommunications, convolutional codes are a type of error-correcting code that generates parity symbols via the sliding application of a boolean polynomial function to a data stream. The sliding application represents the convolution of the encoder over the data, which gives rise to the term itself. As better explained below, the ability to perform economical maximum likelihood soft decision decoding is one of the major benefits of convolutional codes, besides being especially fit for AWGN channels (unlike, for instance, Reed-Solomon codes suited mainly for burst-error codes). Their main parameters are the base code rate k/n (i.e. the ratio between encoder input and output data rate) and the encoder depth K. (often called constraint length). The output of the encoder at a certain time is a function of the current input as well as the previous K-1 inputs, aspect that characterize them as coding technique with memory (unlike block coding ones that are defined as memoryless). Besides K, the actual code generates depends on on the specific connection made inside the encoder block, and typically summarized in polynomial form in the connection vector. The code rate of a convolutional code is commonly modified via puncturing. For example, a code with base code rate 0.5 can be punctured to a higher rate simply by not transmitting a portion of redundancy symbols [2].
In this project the error-correction capability of convolutional codes is discussed and analyzed. First, a MATLAB script (without employing any of its built-in functions) for the convolutional encoding and Viterbi decoding is proposed. Then, the same algorithm is adapted for a much more efficient C/C++ implementation.
The developed encoder / decoder pair can handle constrain lengths (K) from 3 up to 8. The optimal connection vectors [1] as a function of K have been chosen as
-
G1 = [1 1 1] and G2 = [1 0 1] for K = 3
-
G1 = [1 1 1 1] and G2 = [1 0 1 1] for K = 4
-
G1 = [1 0 1 1 1] and G2 = [1 1 0 0 1] for K = 5
-
G1 = [1 0 1 1 1 1] and G2 = [1 1 0 1 0 1] for K = 6
-
G1 = [1 0 0 1 1 1 1] and G2 = [1 1 0 1 1 0 1] for K = 7
-
G1 = [1 0 0 1 1 1 1 1] and G2 = [1 1 1 0 0 1 0 1] for K = 8
The base code-rate (Rc) is 1/2 (i.e. for each information bit a redundancy bit is added), but can be raised to 2/3, 3/4 or 5/6 by means of puncturing. The puncturing vectors employed for this purpose (in accordance with NASA standards) are
-
P = [1 1 0 1] for Rc = 1/2
-
P = [1 1 0 1 1 0] for Rc = 2/3
-
P = [1 1 0 1 1 0 0 1 1 0] for Rc = 5/6
Moreover, the decoder is designed to perform both hard and soft decoding and, for the latter case, allow to specify the number of quantization levels. However, keep in mind the implemented soft decoding algorithm only works for binary modulations.
The encoder schemes (applying the information of the aforementioned connection vectors) and the trellis diagrams for K = 3 and 4 (for Rc = 1/2) are depicted below in Fig.1 and Fig.2.


Fig.1 - Encoder scheme and trellis diagram for K = 3


Fig.2 - Encoder scheme and trellis diagram for K = 4
The encoder is realized by means of a K-cell shift register filled with one source bit at a time (i.e. k = 1) and the output bits computed as the modulo-2 bitwise sum between the elements of the latter and those specified by the connection vectors.
A trellis structure required for the decoder operation is created as an array of Nst elements, where

is the total number of states within the trellis diagram. Each element represents one specific state and, in turn, contains a 2x2 matrix with the corresponding output bits (1st column) and next state (2nd column) in case an input 0 (1st row) or 1 (2nd row) bit is received.
The decoder works employing three memory structures to iteratively refresh all the needed decoding information:
-
Iteration array (Nst elements), which contains the iteration number of the last update of each trellis diagram state,
-
Distance array (Nst elements), where the current minimum distance for every state is saved,
-
Path matrix (Nst x MemDim elements), which saves the previous history (limited to the last MemDim past states) of each survivor paths is stored. MemDim is fixed as Nst · MemFct, where MemFct can be set as parameter by the user (keep in mind that a value of MemFct of 4 or 5 times the constrain length is sufficient for near-optimum decoder performance [1]).
At each cycle the minimum distances and the survivor paths are updated by means of the trellis diagram and the memory structures and, in case the Path matrix is full (or the input encoded bit stream is over), the latter is partially (or completely) flushed in order to retrieve the oldest decoded bits from the so far minimum distance survivor path.
In case of puncturing, some of the output bits are discarded by the encoder in accordance with the specific puncturing vector and, then, reintroduced by the decoder before the launch of the Viterbi algorithm (but not used for the distance estimate).
Besides the encoder / decoder implementation, the MATLAB script also make available 4 simulations to test the error correction capability of the code, always assuming binary antipodal modulation and AWGN channel.
-
1st is a single-run simulation where it is possible to specify as parameters the number of bits Nbits to be encoded, the constrain length K, the code-rate Rc, the decoding method (hard or soft) and the energy-per-bit to noise-spectral-density ratio Eb/N0 over channel and to verify how many errors are introduced by the channel and how many of them are recovered by the Viterbi decoder. An example is shown in Fig.3.

Fig.3 - Single-run simulation in MATLAB
-
2nd is a bit-error-rate (BER) vs Eb/N0 simulation as a function of the constrain length K. The result (having fixed Rc = 1/2 and hard as decoding method) is displayed in Fig.4. As can be seen from the plot, the higher K the better the coding performances in terms of BER at the expense of an exponential increase in computational complexity (especially on the decoding side). In fact, Fig.1 and Fig.2 show how each increase by one of the K value doubles the number of states within the trellis diagram which has to be managed by the Viterbi decoder.

Fig.4 - Coding performance as a function of K
-
3rd is a BER vs Eb/N0 simulation as a function of the code-rate Rc (or equivalently, of the puncturing value). The result (having fixed K = 5 and hard as decoding method) is displayed in Fig.5. As expected, the lower Rc the better the coding performances in terms of BER at the expense of a heavier redundancy. From the plot, the values of Eb/N0 at which the punctured codes performances outdo the uncoded case can be estimated (about 4.5, 6 and 8 dB for Rc = 2/3, 3/4 and 5/6 respectively).

Fig.5 - Coding performance as a function of Rc
-
4th is a BER vs Eb/N0 simulation as a function of the decoding method (hard or soft). The result (having fixed K = 5 and Rc = 1/2) is displayed in Fig.6. As expected, the performances of the hard and 2-level soft cases coincide, since they both work in terms of Hamming distance. With an ideally infinite number of quantization levels a coding gain of about 2 dB arises compared to the hard case, as well known from theory [1]. However, for a 4-level soft decoding the performance improvement due to the usage of Euclidean instead of Hamming distance is already significant.

Fig.6 - Coding performance as a function of the decoding method
After the MATLAB implementation and validation, the encoder and hard decoder algorithms have been adapted for C/C++ language, making it much more efficient and compatible, for example, for microcontroller operation. In fact, besides other intrinsic advantages, while the MATLAB version works on bits where each of them is represented by default as a double type (i.e. 64 bits), the C/C++ version works on bytes allowing to minimize the memory allocation.
Again, the validation of the code error-correcting capability is provided by simulating a simple binary symmetric channel (BSC) and allowing to set as parameter the probability of error Peb. An example of this simulation is shown in Fig.7, where a source stream (SRC) of 40 bytes is randomly generated, encoded (ENC) according to the selected K and Rc parameters, corrupted (ERR) with probability Peb and decoded (DEC). The content of these streams are displayed on the left side of the figure in hexadecimal format. Finally, a check of the number of errors before and after the Viterbi hard decoder is provided.

Fig.7 - Single-run simulation in C/C++
Below there the links through which the aforementioned codes can be free downloaded. For further details and explanations about the practical implementation of the convolutional encoder / decoder pair take a look at the comments within these files.
-
Download MATLAB project : ConvCoding.m
-
Download C/C++ project : ConvCoding.cpp
WARNING#1: The proposed MATLAB simulations may take several hours to be executed, depending on PC performance.
WARNING#2: The ConvCoding.m project has been developed in MATLAB R2017a. Therefore, the correct operation of the script may not be assured with different software versions.
Last update : 02/11/2018
References
[1] B. Sklar and P.K. Ray, Digital Communications, Chapter 7, Pearson Education, 2012
[2] Wikipedia - Convolutional code (link/a)