GNU Radio Manual and C++ API Reference  3.7.5
The Free & Open Software Radio Ecosystem
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
Forward Error Correction

Introduction

This is the gr-fec package. It contains all of the forward error correction (FEC) blocks, utilities, and examples. To use the FEC blocks, the Python namespaces is in gnuradio.fec, which would be normally imported as:

from gnuradio import fec

See the Doxygen documentation for details about the blocks available in this package.

A quick listing of the details can be found in Python after importing by using:

help(fec)

Using the FEC API

FEC is a complex issue to implement in a generic, generally usable way. To help with these issues, the FEC API operates on two levels: the coder variables and the coder deployments. The variables implement the encoding and decoding methods whereas the deployments set up the variables, make sure the input data is formatted properly, run the coder variable, and then pass on the data for follow-on processing.

In a GNU Radio flowgraph, the deployments are GNU Radio blocks that we insert into the flowgraph. The deployments use the API of the coder variables to interact with the scheduler and set up the input/output item buffers that move data between blocks. The intent of the API is to be able to build the coding variables that are general enough for easy use in multiple situations. We then construct deployments to control the interaction between the data and the variable. GNU Radio provides deployments for a number of situations, but these should not be considered the only ways to deploy the decoders.

Deployments

Generally speaking, encoder deployments take in bits and produce bits (i.e., unpacked bytes with 1 bit per byte). Decoder deployments take in floats and produce bits. The floats are generally meant to represent soft decisions. If the demodulator does not produce soft decisions, an easy alternative is to convert the hard decision 0's and 1's to -1 and +1 floats. The main departure from this model is when using a PDU-based modulator or demodulator, for which we can look at using the asynchronous message passing system. In this instance, passing bits is not natural, so we need to create a deployment that can handle packed bytes. GNU Radio has the gr::fec::asycn_encoder and gr::fec::async_decoder deployments that work in this mode.

Some coding variables handle inputs and outputs differently than the described deployments. Using the FEC API provides concepts of input and output conversion properties that help us create deployments to convert the data streams to the required format of the variable.

Streaming Deployments

For the encoder deployments, the gr::fec::encoder block is a relatively simple deployment for the encoding variables. It uses the encoding object information about the input/output sizes and input/output item sizes to set up the interaction with the scheduler. Typically, a coder will add redundancy to the stream making the output longer by some amount than the input stream. This block simply takes in an encoder object, specifically an object that derives from gr::fec::generic_encoder. It also takes in the input and output items sizes that the encoder will expect, which we can just ask the encoder for. Typically, the encodes expect unpacked bytes in and unpacked bytes out.

The gr::fec::decoder block is a similarly simple deployment for the decoding variables. It uses the decoding variable information about the input/output sizes and input/output item sizes to set up the interaction with the scheduler. Since a decoder typically uses the redundancy from the input stream to correct for errors, the input stream will be longer than the output stream by the rate of the code. This block simply takes in an decoder object, specifically an object that derives from gr::fec::generic_decoder. It also takes in the input and output items sizes that the decoder will expect, which we can just ask the encoder for. The deployment expects a floating point stream input, though the decoder variables may take a float input or a byte. If using a byte format, it could be a hard decision or a quantized soft decision, depending on how the decoder object behaves.

Normally, though, we don't work directly with these simple encoder() or decoder() deployments but a wrapper around those blocks. GNU Radio's gr-fec package comes with two Python deployments to make things easier: fec.extended_encoder and fec.extended_decoder. For one thing, these extended hier_block2 blocks take care of the puncturing and depuncturing operations often found in FEC codes. The other thing that these blocks do for us is read the API of the encoder/decoder variables to properly convert the data in or out, depending on how the coding object works.

For instance, a generic_decoder takes in floating point values (which should be soft decisions). However, a decoder might instead want to work on 8-bit quantized soft decisions and so expects unsigned chars. Specifying 'uchar' as the gr::fec::generic_decoder::get_input_conversion() of the decoder block tells the fec.extended_decoder to convert the float to a byte.

In GRC, we would add an "FEC Extended Encoder" to our transmitter or an "FEC Extended Decoder" to the receiver. We would then use one of the encoder or decoder FEC variable blocks to define the parameters of the particular code we want to use. We can find these codes under the [Error Coding] category in GRC. The encoders are found under [Encoders] and similarly the decoders under the [Decoders] categories. Putting these onto the canvas creates a variable that we can then pass to the extended encoder or decoder deployment blocks.

Most of the parameters of the encoder and decoder definitions should be fairly obvious based on the type of code. See the documentation for each coding object for more details. In the following section Parallelism, we explain the Parallelism and Dimension properties.

See fec/fecapi_encoders.grc and fec/fecapi_decoders.grc in the installed examples for an example of how to work with these deployments given the three initial FEC coders available.

Tagged Stream Deployments

GNU Radio's gr-fec also comes with simple deployments for Tagged Stream Blocks blocks. These deployments work similarly to the normal streaming deployments but fit into a tagged stream system by setting a tagged stream tag to control the frame size. Like all tagged stream blocks, they expect the tag to be located in the stream in order to properly function.

The simplest form of the tagged stream deployments are just the C++ blocks gr::fec::tagged_encoder and gr::fec::tagged_decoder. These do not handle any input or output conversion. They expect the inputs to be already properly formatted for the encoding/decoding variables, and the outputs will be whatever the variable naturally produce.

In the tagged stream deployments, the frame size set for a variable is no longer the static frame size like we expected in the streaming data implementations. Instead, we look at the frame size of the encoder/decoder variable during construction of the deployment as the maximum frame size, or a maximum transmission unit (MTU). This allows us to set up some internal memory to handle up to the required maximum length. When a tagged stream comes into this block, the frame size is then set based on that tagged stream information. If the frame is larger than the established MTU, a warning is sent out and the deployment only handles up to the MTU of the given frame.

The extended Python tagged deployments, fec.extended_tagged_encoder and fec.extended_tagged_decoder, offer additional handling of the FEC API like we saw with the fec.extended_encoder and fec.extended_decoder. These extended deployments wrap up the puncturing and depuncturing as well as use the FEC API to do any input and output translation for the formatting of data streams. The fec.extended_tagged_encoder expects unpacked bits in and produces unpacked bits out. The fec.extended_tagged_decoder takes in floats (generally soft decisions from -1 to 1) and produces unpacked bits.

See fec/fecapi_tagged_encoders.grc and fec/fecapi_tagged_decoders.grc in the installed examples for an example of how to work with these deployments given the three initial FEC coders available.

Asynchronous Deployments

The final standard deployment shipped with GNU Radio is for asynchronous Message Passing and handling PDUs: gr::fec::async_encoder and gr::fec::async_decoder.

Unlike the other deployments, these C++ deployments do not also have an extended Python deployment. Because this deployment uses message passing, we cannot easily build up a hierarchical block of message passing blocks to satisfy the input/output translations like we've done with the other forms of deployment. Instead, the input/output formatting is taken care of inside this deployment itself. Further, because this form of moving data anticipates data being moved in packets, these deployments cannot work with any FEC code that requires a history (see generic_decoder::get_history). Right now, this means that the async message passing deployments cannot work with convolutional codes (gr::fec::code::cc_encoder and gr::fec::code::cc_decoder) in streaming mode because it would require data from the next frame to finish off decoding the current frame.

These deployments also work in two distinct modes. They can work in unpacked mode where inputs are messages of bits, or they may work in packed mode where messages are PDUs. The distinction is that the packed mode PDU's are the standard protocol data units (PDUs) that encompass full packets of data. This allows these async deployments to be used easily within PDU-based applications, such as encoding a packet with a CRC attached.

When in packed or PDU mode, the encoder deployment has the option of reversing the bits during unpacking and packing. Like the extended deployments for the other data modes, these deployments manipulate the input data to the format expected by the encoding or decoding variables using calls to the FEC API. Because most of the coders work off unpacked bits, the incoming PDUs must first be unpacked into bits and the repacked again into the original PDU. The gr::blocks::kernel::pack_k_bits and gr::blocks::kernel::unpack_k_bits kernels are used here, and they can change the direction on how to pack and unpack. Because different data processing blocks, framing, deframing, and other operations may arbitrarily set the format of the bits and the ordering, we provide the options of unpacking and packing directions in the deployments. However, the gr::fec::async_decoder still expects the input to be soft decisions with one decision per item, so we only say whether this deployment outputs packed PDUs or not and the packing direction.

For an example of using the asynchronous in PDU mode, see fec/fecapi_async_packed_decoders.grc. See fec/fecapi_async_to_stream.grc for an example of mixing the packed PDU mode encoder with a tagged stream decoder. This example shows the PDU input having a CRC32 appended to the uncoded stream that is then checked after the packet is decoded.

For an example of the async deployment using unpacked bits, see fec/fecapi_async_encoders.grc and fec/fecapi_async_decoders.grc.

Encoding/Decoding Variables

GNU Radio currently has a minor subset of coders available:

Coders:

Decoders:

When building a new FECAPI encoder or decoder variable, the dummy encoder/decoder block would be a good place to start. This coding set does no processing on the data. For the encoder, each bit is simply passed through directly. For the dummy decoder, the input data are floats, so -1's become 0 and 1's stay as 1, but nothing else is done to the data. Mainly, these blocks are used for references and to make it easy to compare implementations with and without codes by easily dropping in these objects instead of restructuring the entire flowgraph. The ber_curve_gen.grc example file uses the dummy codes to show the curve to compare against the actual codes.

Although mentioned in the convolutional coder and decoder classes, it is worth another mention. The cc_encoder is a generic convolutional encoder that can take any value of K, rate, and polynomials to encode a data stream. However, the cc_decoder is not as general, even though it is technically parameterized as such. The cc_decoder block currently only uses K=7, rate=2, and two polynomials (because the rate is two). We can, in fact, alter the polynomials, but a default of [109, 79] is typically. Eventually, we will make this block more generic for different rates and constraint lengths and take this particular code implementation as the set CCSDS decoder, much like we have the ccsds_encoder class.

Parallelism

The code variables in GNU Radio Companion have the ability to create multiple encoder/decoder variables by selecting the level of parallelism. It is up the encoder to understand how to handle the parallelism. The following discussion explains the difference between the two levels and how and when to use. Generally, normal applications will just use a single level of parallelism.

The GRC variable declarations for the different coders has a setting for Parallelism, which can be either 1 or 2. If set to 1, then the resulting variable is a list of coder blocks with the same settings. If set to 2, then the resulting variable is a list of lists of coder blocks. The code that accepts these variables must understand how to handle the parallelism. Most applications would set this to 1.

The standard fec.extended_encoder ("FEC Extended Encoder" in GRC) and fec.extended_decoder ("FEC Extended Decoder" in GRC) can handle a Parallelism of 1. They accept a list of coder variables as defined by Dimension 1 and can multithread the application based on the "Threading Type" setting:

  • None: does no parallel threading of the coders. Even if Dimension 1 is > 1, the encoder/decoder will ignore this setting and only use the first object in the list.
  • Ordinary: all "Dimension 1" number (N) of encoder/decoder blocks will be used in parallel. The hier_block2 will block deinterleave the packets into N streams (using gr::blocks::deinterleave with a value of blocksize as the frame length and no relative rate changes) and pass these to each of the N coders to process the frames in parallel. The output of each coder is then interleaved back together to make a single output stream.
  • Capillary: all "Dimension 1" number (N) of encoder/decoder blocks will be used in parallel, much like in the Ordinary mode. In this mode, however, the frames get split up in a tree-like fashion, where each branch launches 2 more branches. This means that N must be a factor of 2 for this mode to work. It tends to handle the load of the encoders/decoders better than the Ordinary mode.

Note that the threading modes only work when using constant-length frames. If using the coders in tagged stream mode where the frame lengths may change, the Ordinary and Capillary modes are not available.

The GRC example "ber_curve_gen.grc" uses a Parallelism of 2. This creates a list of lists of coders. The first dimension of the list corresponds to the number of Es/N0 values being used in the BER simulation. This allows the application to process all values of Es/N0 simultaneously. Dimension 2 in this case allows the same concept of parallelism discussed above with the None, Ordinary, and Capillary models of threading.

The API of the FECAPI

The FECAPI defined by the parent generic_encoder and generic_decoder classes defines a set of virtual functions, some pure virtual, to allow the encoders/decoders to interact with the GNU Radio blocks. See the associated documentation of the generic_encoder and generic_decoder classes to know more about each of the API functions, some of which a child class is required to implement.

The functions of the encoder and decoder are:

Note: there is no get_input_item_size (or output) as the encoders always expect to work on bits.

Whenever an FECAPI object refers to the frame size, it always means the number of bits in the uncoded frame. This means the number of bits going into an encoder and the number of bits coming out of a decoder.

FEC Examples

  • ber_curve_gen.grc
  • ber_test.grc
  • fecapi_decoders.grc
  • fecapi_encoders.grc
  • fecapi_tagged_decoders.grc
  • fecapi_tagged_encoders.grc
  • fecapi_async_decoders.grc
  • fecapi_async_encoders.grc
  • fecapi_async_to_stream.grc