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
203
204
205
206
207
208
209
210
//! Encoding and decoding of the base (17, 9, 5) cyclic code used by
//! [DMR](https://en.wikipedia.org/wiki/Digital_mobile_radio) and
//! [P25](https://en.wikipedia.org/wiki/Project_25).
//!
//! The generator polynomial for the base code is given by
//!
//! > g(x) = x<sup>8</sup> + x<sup>5</sup> + x<sup>4</sup> + x<sup>3</sup> + 1
//!
//! It can detect up to 4 errors or correct up to 2 errors.
//!
//! ## DMR "quadrature residue" code
//!
//! The DMR air interface extends this code to (18, 9, 6) with an extra parity check bit
//! in the LSB, then shortens it to (16, 7, 6) by deleting two MSB data bits. The extra
//! parity bit is computed over the data bits using the mask `1010111`.
//!
//! ## P25 "shortened cyclic" code
//!
//! The P25 air interface shortens this code to (16, 8, 5) by deleting the MSB data bit.
//!
//! ## References
//!
//! The decoding algorithm is based on the algorithm described in Lin and Costello's
//! *Error Control Coding* (1983) and Roman's *Coding and Information Theory* (1992),
//! p345.

extern crate binfield_matrix;

use binfield_matrix::{matrix_mul, matrix_mul_systematic};

/// Encode the given 9 data bits into a 17-bit codeword.
pub fn encode(data: u16) -> u32 {
    assert_eq!(data >> 9, 0);
    matrix_mul_systematic(data, GEN)
}

/// Try to decode the given 17-bit word to the nearest codeword, correcting up to 2
/// errors.
///
/// If decoding was successful, return `Some((data, err))`, where `data` is the 9 data
/// bits and `err` is the number of corrected bits. Otherwise, return `None` to indicate
/// an unrecoverable error.
pub fn decode(word: u32) -> Option<(u16, usize)> {
    assert_eq!(word >> 17, 0);

    // Go through a full cycle of the codeword, so the data bits end up in their original
    // position.
    let (fixed, word) = (0..17).fold((Some(0), word), |(fixed, word), _| {
        let syndrome: u8 = matrix_mul(word, PAR);

        if syndrome == 0 {
            return (fixed, rotate_17(word));
        }

        match PATTERNS[syndrome as usize] {
            0 => (None, rotate_17(word)),
            pat => (Some(pat.count_ones() as usize), rotate_17(word ^ pat)),
        }
    });

    fixed.map(|err| ((word >> 8) as u16, err))
}

/// Transpose of the generator matrix, without the identity part.
const GEN: &[u16] = &[
    0b100111100,
    0b010011110,
    0b001001111,
    0b100011011,
    0b110110001,
    0b111100100,
    0b011110010,
    0b001111001,
];

/// Transpose of parity-check matrix.
///
/// This was derived in the standard way from the generator matrix.
const PAR: &[u32] = &[
    0b10011110010000000,
    0b01001111001000000,
    0b00100111100100000,
    0b10001101100010000,
    0b11011000100001000,
    0b11110010000000100,
    0b01111001000000010,
    0b00111100100000001,
];

/// Maps each 8-bit syndrome to an error pattern.
///
/// If a syndrome is invalid, the pattern is zero. Because the code is cyclic, we only
/// need to store patterns for syndromes where the LSB is set.
const PATTERNS: [u32; 256] = [
    0,
    0b00000000000000001,
    0,
    0b00000000000000011,
    0,
    0b00000000000000101,
    0, 0, 0,
    0b00000000000001001,
    0, 0, 0, 0, 0, 0, 0,
    0b00000000000010001,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0b00000000000100001,
    0, 0, 0, 0,
    0b00100000000000001,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0b00000000100000001,
    0, 0, 0, 0, 0, 0, 0, 0,
    0b00000000001000001,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0b01000000000000001,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0,
    0b00000001000000001,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0b00000000010000001,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0b00010000000000001,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0b10000000000000001,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0,
    0b00001000000000001,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0b00000010000000001,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0b00000100000000001,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];

/// Cyclically rotate the word right as if it was 17 bits long.
fn rotate_17(word: u32) -> u32 {
    let lsb = word & 1;
    word >> 1 | lsb << 16
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_encode() {
        assert_eq!(encode(0b000000000), 0b000000000_00000000);
        assert_eq!(encode(0b111111111), 0b111111111_11111111);
        assert_eq!(encode(0b100000001), 0b100000001_10100101);
        assert_eq!(encode(0b000001001), 0b000001001_11001000);
        assert_eq!(encode(0b000001011), 0b000001011_10111010);
        assert_eq!(encode(0b000001010), 0b000001010_10000011);
        assert_eq!(encode(0b000001000), 0b000001000_11110001);
    }

    #[test]
    fn test_decode() {
        // Exhaustively test loopback of all possible input words.
        for w in 0..1<<9 {
            assert_eq!(decode(encode(w)), Some((w, 0)));
        }

        let w = encode(0b1010101);
        assert_eq!(w, 0b1010101_00100001);
        assert_eq!(decode(w), Some((0b1010101, 0)));

        // Exhaustively test one-bit errors.
        for i in 0..17 {
            assert_eq!(decode(w ^ 1 << i), Some((0b1010101, 1)));
        }

        // Exhaustively test two-bit errors.
        for (i, j) in (0..17).zip(0..17) {
            if i != j {
                assert_eq!(decode(w ^ (1 << i) ^ (1 << j)), Some((0b1010101, 2)));
            }
        }
    }

    #[test]
    fn test_rotate_17() {
        assert_eq!(rotate_17(0b00000000000000000), 0b00000000000000000);
        assert_eq!(rotate_17(0b10000000000000000), 0b01000000000000000);
        assert_eq!(rotate_17(0b01000000000000000), 0b00100000000000000);
        assert_eq!(rotate_17(0b00100000000000000), 0b00010000000000000);
        assert_eq!(rotate_17(0b00010000000000000), 0b00001000000000000);
        assert_eq!(rotate_17(0b00001000000000000), 0b00000100000000000);
        assert_eq!(rotate_17(0b00000100000000000), 0b00000010000000000);
        assert_eq!(rotate_17(0b00000010000000000), 0b00000001000000000);
        assert_eq!(rotate_17(0b00000001000000000), 0b00000000100000000);
        assert_eq!(rotate_17(0b00000000100000000), 0b00000000010000000);
        assert_eq!(rotate_17(0b00000000010000000), 0b00000000001000000);
        assert_eq!(rotate_17(0b00000000001000000), 0b00000000000100000);
        assert_eq!(rotate_17(0b00000000000100000), 0b00000000000010000);
        assert_eq!(rotate_17(0b00000000000010000), 0b00000000000001000);
        assert_eq!(rotate_17(0b00000000000001000), 0b00000000000000100);
        assert_eq!(rotate_17(0b00000000000000100), 0b00000000000000010);
        assert_eq!(rotate_17(0b00000000000000010), 0b00000000000000001);
        assert_eq!(rotate_17(0b00000000000000001), 0b10000000000000000);
        assert_eq!(rotate_17(0b01111111111111111), 0b10111111111111111);

        let mut word = 0b11100011001010101;

        for _ in 0..17 {
            word = rotate_17(word);
        }

        assert_eq!(word, 0b11100011001010101);
    }
}