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
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
//! Reed-Solomon error correction codes.
//!
//! The error correction in a Data Matrix is done using Reed-Solomon codes.
//!
//! Assuming you have never heard of coding theory: By putting some redundancy
//! into the Data Matrix one can recover from, say, detection or printing errors
//! when trying to read a Data Matrix. A clever way to add redundancy
//! is the Reed-Solomon code. The details are relatively
//! math heavy and involve, for example, "higher" algebra (Galois fields).
//! Any book about coding theory should cover it, for example
//! "Error Correction Coding: Mathematical Methods and Algorithms" by Moon.
//!
//! While there is only way to compute the error code (called _encoding_),
//! there are several algorithms for error correction (called _decoding_).
//!
//! The decoder implemented in this module is _syndrome_ based. Such a decoder
//! is classically made of four main steps:
//!
//! 1. Compute the syndrome values.
//! 2. Compute the error locator polynomial
//! 3. Compute the error locations.
//! 4. Compute the error values.
//!   
//! We use a Levinson-Durbin algorithm in the second step. See
//! the article ["Levinson-Durbin Algorithm Used For Fast BCH Decoding"](https://doi.org/10.1007/978-1-4615-6119-4_1)
//! by Michael Schmidt and Gerhard P. Fettweis. This approach was empiricially
//! verified to be better than a LU decomposition and it did also beat
//! the Berlekamp-Massey algorithm (about 10% faster).
//!
//! Furthermore, in step four the [Björck-Pereyra algorithm](https://doi.org/10.1090/S0025-5718-1970-0290541-1)
//! is used to determine the error values. It was faster than Forney's algorithm
//! and also faster than a naive LU decomposition in our tests.
//!
//! The other possibilites mentionend for step 2 and 4
//! are still in the source code in case someone is interested in them.
mod decoding;
mod galois;

use super::symbol_size::{Size, SymbolSize};
use galois::GF;

pub use decoding::decode as decode_error;
pub use decoding::ErrorDecodingError;

/// The coefficients of the generator polynomicals used
/// by the Reed-Solomon code specified for Data Matrix.
///
/// The coefficients are given in the standard, but can also
/// be computed with the Python script "gf.py" in this repository.
const GENERATOR_POLYNOMIALS: [&[u8]; 16] = [
    // 5
    &[1, 62, 111, 15, 48, 228],
    // 7
    &[1, 254, 92, 240, 134, 144, 68, 23],
    // 10
    &[1, 61, 110, 255, 116, 248, 223, 166, 185, 24, 28],
    // 11
    &[1, 120, 97, 60, 245, 39, 168, 194, 12, 205, 138, 175],
    // 12
    &[1, 242, 100, 178, 97, 213, 142, 42, 61, 91, 158, 153, 41],
    // 14
    &[
        1, 185, 83, 186, 18, 45, 138, 119, 157, 9, 95, 252, 192, 97, 156,
    ],
    // 18
    &[
        1, 188, 90, 48, 225, 254, 94, 129, 109, 213, 241, 61, 66, 75, 188, 39, 100, 195, 83,
    ],
    // 20
    &[
        1, 172, 186, 174, 27, 82, 108, 79, 253, 145, 153, 160, 188, 2, 168, 71, 233, 9, 244, 195,
        15,
    ],
    // 24
    &[
        1, 193, 50, 96, 184, 181, 12, 124, 254, 172, 5, 21, 155, 223, 251, 197, 155, 21, 176, 39,
        109, 205, 88, 190, 52,
    ],
    // 28
    &[
        1, 255, 93, 168, 233, 151, 120, 136, 141, 213, 110, 138, 17, 121, 249, 34, 75, 53, 170,
        151, 37, 174, 103, 96, 71, 97, 43, 231, 211,
    ],
    // 36
    &[
        1, 112, 81, 98, 225, 25, 59, 184, 175, 44, 115, 119, 95, 137, 101, 33, 68, 4, 2, 18, 229,
        182, 80, 251, 220, 179, 84, 120, 102, 181, 162, 250, 130, 218, 242, 127, 245,
    ],
    // 42
    &[
        1, 5, 9, 5, 226, 177, 150, 50, 69, 202, 248, 101, 54, 57, 253, 1, 21, 121, 57, 111, 214,
        105, 167, 9, 100, 95, 175, 8, 242, 133, 245, 2, 122, 105, 247, 153, 22, 38, 19, 31, 137,
        193, 77,
    ],
    // 48
    &[
        1, 19, 225, 253, 92, 213, 69, 175, 160, 147, 187, 87, 176, 44, 82, 240, 186, 138, 66, 100,
        120, 88, 131, 205, 170, 90, 37, 23, 118, 147, 16, 106, 191, 87, 237, 188, 205, 231, 238,
        133, 238, 22, 117, 32, 96, 223, 172, 132, 245,
    ],
    // 56
    &[
        1, 46, 143, 53, 233, 107, 203, 43, 155, 28, 247, 67, 127, 245, 137, 13, 164, 207, 62, 117,
        201, 150, 22, 238, 144, 232, 29, 203, 117, 234, 218, 146, 228, 54, 132, 200, 38, 223, 36,
        159, 150, 235, 215, 192, 230, 170, 175, 29, 100, 208, 220, 17, 12, 238, 223, 9, 175,
    ],
    // 62
    &[
        1, 204, 11, 47, 86, 124, 224, 166, 94, 7, 232, 107, 4, 170, 176, 31, 163, 17, 188, 130, 40,
        10, 87, 63, 51, 218, 27, 6, 147, 44, 161, 71, 114, 64, 175, 221, 185, 106, 250, 190, 197,
        63, 245, 230, 134, 112, 185, 37, 196, 108, 143, 189, 201, 188, 202, 118, 39, 210, 144, 50,
        169, 93, 242,
    ],
    // 68
    &[
        1, 186, 82, 103, 96, 63, 132, 153, 108, 54, 64, 189, 211, 232, 49, 25, 172, 52, 59, 241,
        181, 239, 223, 136, 231, 210, 96, 232, 220, 25, 179, 167, 202, 185, 153, 139, 66, 236, 227,
        160, 15, 213, 93, 122, 68, 177, 158, 197, 234, 180, 248, 136, 213, 127, 73, 36, 154, 244,
        147, 33, 89, 56, 159, 149, 251, 89, 173, 228, 220,
    ],
];

fn generator(len: usize) -> &'static [u8] {
    GENERATOR_POLYNOMIALS
        .iter()
        .find(|p| p.len() - 1 == len)
        .expect("no generator polynomical defined for this symbol size, this is a bug")
}

/// Compute the Reed-Solomon code used by Data Matrix for error correction.
///
/// Depending on the symbol size, the data is first split up into
/// interleaved blocks. For each block an error code is computed.
/// The resulting blocks of error codes are returned interleaved.
pub fn encode_error(data: &[u8], size: SymbolSize) -> Vec<u8> {
    let setup = size.block_setup().unwrap();
    let num_codewords = size.num_data_codewords().unwrap();
    assert!(data.len() == num_codewords);
    let gen = generator(setup.num_ecc_per_block);
    // For bigger symbol sizes the data is split up into interleaved blocks
    // for which an error code is computed individually. we store
    // the error blocks interleaved in the returned result.
    let stride = setup.num_blocks;
    let mut ecc = vec![0; setup.num_ecc_per_block + 1];
    let mut full_ecc = vec![0; setup.num_ecc_per_block * setup.num_blocks];
    for block in 0..setup.num_blocks {
        for item in &mut ecc {
            // reset ecc for new block
            *item = 0;
        }
        let strided_data_input = (block..data.len()).step_by(stride).map(|i| data[i]);
        ecc_block(strided_data_input, gen, &mut ecc);

        // copy block interleaved to result vector
        for (result, ecc_i) in full_ecc
            .iter_mut()
            .skip(block)
            .step_by(stride)
            .zip(&ecc[..setup.num_ecc_per_block])
        {
            debug_assert_eq!(*result, 0);
            *result = *ecc_i;
        }
    }
    full_ecc
}

fn ecc_block<T: Iterator<Item = u8>>(data: T, g: &[u8], ecc: &mut [u8]) {
    // Let d be the data polynomical (n coefficients) and g the generating polynomical
    // with k + 1 coefficients.
    //
    // We use a variant of euclidean polynomical division on the input polynomials
    // d(x) * x^k and g to get a quotient q and remainder r such that
    //
    //     d(x) * x^k = q(x) g(x) + r(x).
    //
    // The error code then is -r(x) = r(x), because
    //
    //     d(x) * x^k - r(x)
    //
    // is then divisible by g. The first n bytes will contain the data, and
    // the last k the error code, i.e., the coefficient of r. The algorithm
    // is modified to not compute q and store r directly in ecc. The ecc
    // array is used to store intermediate results.
    let ecc_len = g.len() - 1;
    for a in data {
        let k = GF(ecc[0]) + GF(a);
        for j in 0..ecc_len {
            ecc[j] = (GF(ecc[j + 1]) + k * GF(g[j + 1])).into();
        }
    }
}

#[test]
fn ecc_block_1() {
    // The test case was computed with the Python script
    let data = [23, 40, 11];
    let g = GENERATOR_POLYNOMIALS[0];
    let mut ecc = vec![0; 5 + 1];
    ecc_block(data.iter().cloned(), g, &mut ecc);
    assert_eq!(ecc[..5], vec![255, 207, 37, 244, 81]);
}