Turn Off Systematic CodingΒΆ

A simple, yet clever technique called systematic encoding can be used to improve the performance of network coding. The way it works is to initially send everything uncoded, and then start the encoding. As the receivers initially have no data, all data will be useful for them. So if the symbols are safely received by the decoder, it can get the data “for free” without the need for decoding. The Kodo library has built-in support for this approach. The sample code is also based on the basic example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// Copyright Steinwurf ApS 2016.
// Distributed under the "STEINWURF RESEARCH LICENSE 1.0".
// See accompanying file LICENSE.rst or
// http://www.steinwurf.com/licensing

#include <cstdint>
#include <cstdlib>
#include <ctime>
#include <algorithm>
#include <iostream>
#include <vector>

#include <kodocpp/kodocpp.hpp>

int main()
{
    // Seed the random number generator to produce different data every time
    srand((uint32_t)time(0));

    // Set the number of symbols (i.e. the generation size in RLNC
    // terminology) and the size of a symbol in bytes
    uint32_t max_symbols = 16;
    uint32_t max_symbol_size = 1400;

    // In the following we will make an encoder/decoder factory.
    // The factories are used to build actual encoders/decoders
    kodocpp::encoder_factory encoder_factory(
        kodocpp::codec::full_vector,
        kodocpp::field::binary8,
        max_symbols,
        max_symbol_size);

    kodocpp::encoder encoder = encoder_factory.build();

    kodocpp::decoder_factory decoder_factory(
        kodocpp::codec::full_vector,
        kodocpp::field::binary8,
        max_symbols,
        max_symbol_size);

    kodocpp::decoder decoder = decoder_factory.build();

    std::vector<uint8_t> payload(encoder.payload_size());
    std::vector<uint8_t> data_in(encoder.block_size());
    // Just for fun - fill the data with random data
    std::generate(data_in.begin(), data_in.end(), rand);
    // Assign the data buffer to the encoder so that we may start
    // to produce encoded symbols from it
    encoder.set_const_symbols(data_in.data(), encoder.block_size());
    // Create a buffer which will contain the decoded data, and we assign
    // that buffer to the decoder
    std::vector<uint8_t> data_out(decoder.block_size());
    decoder.set_mutable_symbols(data_out.data(), decoder.block_size());

    uint32_t encoded_count = 0;
    uint32_t dropped_count = 0;

    //! [0]
    // We switch any systematic operations off, so the encoder produces
    // coded symbols from the beginning.
    // Note that some codecs might not have a systematic mode, so it is a
    // good idea to check this capability with has_systematic_interface()
    // before calling is_systematic_on() and set_systematic_off()
    if (encoder.has_systematic_interface() && encoder.is_systematic_on())
    {
        encoder.set_systematic_off();
    }
    //! [1]

    while (!decoder.is_complete())
    {
        // Encode a packet into the payload buffer
        uint32_t bytes_used = encoder.write_payload(payload.data());
        std::cout << "Bytes used = " << bytes_used << std::endl;

        ++encoded_count;

        if (rand() % 2)
        {
            ++dropped_count;
            continue;
        }

        // Pass that packet to the decoder
        decoder.read_payload(payload.data());
    }

    std::cout << "Encoded count = " << encoded_count << std::endl;
    std::cout << "Dropped count = " << dropped_count << std::endl;

    // Check if we properly decoded the data
    if (data_in == data_out)
    {
        std::cout << "Data decoded correctly" << std::endl;
    }

    return 0;
}

What’s added in this example is the use of is_systematic_on and set_systematic_off.

1
2
3
4
5
6
7
8
9
    // We switch any systematic operations off, so the encoder produces
    // coded symbols from the beginning.
    // Note that some codecs might not have a systematic mode, so it is a
    // good idea to check this capability with has_systematic_interface()
    // before calling is_systematic_on() and set_systematic_off()
    if (encoder.has_systematic_interface() && encoder.is_systematic_on())
    {
        encoder.set_systematic_off();
    }

Initially Kodo’s Full RLNC encoder has the systematic phase enabled per default. As seen in the previous example, this is automatically turned off when all symbols are sent once. In this example we turn off the systematic phase before entering the coding loop. This can be easily seen from the output when running the example:

Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Bytes used = 1417
Encoded count = 27
Dropped count = 11

Here the bytes used is always the same as all the symbols are encoded by the encoder. For most use cases the systematic phase is beneficial. However it should be avoided if:

  • The state of the receivers is unknown. If that’s the case, using the systematic approach might result in sending redundant data, as the receivers might already have some of the data.
  • The setup has multiple sources. If this is the case, the sources should not send the same data, as this can be redundant for the receivers.