Note

This static document was automatically created from the output of a jupyter notebook.

Execute and modify the notebook online here.

Implementing FEC codings

This Jupyter notebook will outline the step-by-step process of implementing a new coding scheme for the forward error correction in communication modems. The selected algorithm is a repetition encoder, since it is arguably the most basic error correction coding.

As an initial step, we will import all required modules from HermesPy:

[1]:
%%capture
pip install hermespy
[2]:
import numpy as np
from hermespy.fec import Encoder

Error correcting coding steps are represented by the abstract Encoder interface. Each coding algorithm is represented by a class inheriting from the interface, which requires them to implement the abstract functions and properties

Let’s assume our repetition coding takes blocks of \(K = 4\) data bits and repeats them \(3\) times. The resulting code block size would be \(N = 3K = 12\), which results in a rate of \begin{equation} \mathbf{R} = \frac{K}{N} = \frac{1}{3} \end{equation} for the full encoding. During decoding, the repetition coding decides by majority voting which bit has been transmitted. So, our coding implementation is

[3]:
class RepetitionCoding(Encoder):

    @property
    def bit_block_size(self) -> int:
        return 4

    @property
    def code_block_size(self) -> int:
        return 12

    def encode(self, data: np.ndarray) -> np.ndarray:
        return np.tile(data, 3)

    def decode(self, code: np.ndarray) -> np.ndarray:
        return (np.mean(np.reshape(code, (3, self.bit_block_size)), axis=0, keepdims=False) > .5).astype(int)

Now we can inspect our coding implementation:

[4]:
coding = RepetitionCoding()
print(f"Our coding rate is {coding.rate} with an input block size of {coding.bit_block_size} and an output block size of {coding.code_block_size}")

data = np.random.randint(0, 2, coding.bit_block_size)
print(f"Let's assume we transmit the following data block: {data}")

code = coding.encode(data)
print(f"After encoding the respective code block is: {code}")

error_code = code.copy()
error_code[0] = not error_code[0]
print(f"After channel propagation the first bit has flipped: {error_code}")

corrected_data = coding.decode(error_code)
print(f"But the coding can correct a single bit flip to: {corrected_data}")

Our coding rate is 0.3333333333333333 with an input block size of 4 and an output block size of 12
Let's assume we transmit the following data block: [0 0 0 0]
After encoding the respective code block is: [0 0 0 0 0 0 0 0 0 0 0 0]
After channel propagation the first bit has flipped: [1 0 0 0 0 0 0 0 0 0 0 0]
But the coding can correct a single bit flip to: [0 0 0 0]

We may now investigate our newly created forward error correction coding within a full Hermes simulation capaign.

[6]:
from hermespy.simulation import Simulation
from hermespy.modem import BitErrorEvaluator, DuplexModem, RootRaisedCosineWaveform

# Create a new simulation featuring a single device transmitting at 10GHz
simulation = Simulation()
device = simulation.scenario.new_device()
device.carrier_frequency = 10e9

# Configure a communication operation on the device, using our coding
modem = DuplexModem()
modem.waveform_generator = RootRaisedCosineWaveform(modulation_order=4, oversampling_factor=2, num_preamble_symbols=0, num_data_symbols=10, symbol_rate=1e6)
modem.encoder_manager.add_encoder(coding)
modem.device = device

# Run a very low-demanding simulation for demonstration purposes
simulation.new_dimension('snr', np.linspace(2, .1, 20))
simulation.add_evaluator(BitErrorEvaluator(modem, modem))
simulation.min_num_samples, simulation.num_samples = 1000, 1000
simulation.num_actors = 1
result = simulation.run()
──────────────────────────────────── Simulation Campaign ────────────────────────────────────
[16:30:28] Launched simulation campaign with 1 dedicated actors           monte_carlo.py:1672
           Generating a maximum of 20000 = 1000 x 20 samples inspected by monte_carlo.py:1692
           1 evaluators                                                                      
                                                                                             
Simulation Grid                                                                              
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Dimension  Sections                                                                      ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ snr        2.00 1.90 1.80 1.70 1.60 1.50 1.40 1.30 1.20 1.10 1.00 0.90 0.80 0.70 0.60    │
│            0.50 0.40 0.30 0.20 0.10                                                      │
└───────────┴───────────────────────────────────────────────────────────────────────────────┘
[16:30:49] Simulation finished after 21.17 seconds                        monte_carlo.py:1832

This allows us to visualize the achieved bit error rates for given linear signal to noise ratios:

[ ]:
_ = result.plot()
../_images/notebooks_fec_coding_10_0.svg