labrador_ldpc/decoder.rs
1// Copyright 2017 Adam Greig
2// Licensed under the MIT license, see LICENSE for details.
3
4//! This module provides decoding functions for turning codewords into data.
5//!
6//! Please refer to the `decode_ms` and `decode_bf` methods on
7//! [`LDPCCode`](../codes/enum.LDPCCode.html) for more details.
8
9use core::i8;
10use core::i16;
11use core::i32;
12use core::f32;
13use core::f64;
14
15use core::ops::{Add,AddAssign,Neg,Sub};
16
17use crate::codes::LDPCCode;
18
19/// Trait for types that the min-sum decoder can operate with.
20///
21/// Implemented for `i8`, `i16`, `i32`, `f32`, and `f64`.
22pub trait DecodeFrom:
23 Sized + Clone + Copy + PartialEq + PartialOrd
24 + Add + AddAssign + Neg<Output=Self> + Sub<Output=Self>
25{
26 /// 1 in T
27 fn one() -> Self;
28 /// 0 in T
29 fn zero() -> Self;
30 /// Maximum value T can represent
31 fn maxval() -> Self;
32 /// Absolute value of self
33 fn abs(&self) -> Self;
34 /// Saturating add
35 fn saturating_add(&self, other: Self) -> Self;
36 /// Saturating sub
37 fn saturating_sub(&self, other: Self) -> Self;
38 /// Hard-information decoding for T, treating negative values as `true`
39 fn hard_bit(&self) -> bool;
40}
41
42impl DecodeFrom for i8 {
43 #[inline] fn one() -> i8 { 1 }
44 #[inline] fn zero() -> i8 { 0 }
45 #[inline] fn maxval() -> i8 { i8::MAX }
46 #[inline] fn abs(&self) -> i8 { i8::saturating_abs(*self) }
47 #[inline] fn saturating_add(&self, other: Self) -> Self { i8::saturating_add(*self, other) }
48 #[inline] fn saturating_sub(&self, other: Self) -> Self { i8::saturating_sub(*self, other) }
49 #[inline] fn hard_bit(&self) -> bool { *self < 0 }
50}
51impl DecodeFrom for i16 {
52 #[inline] fn one() -> i16 { 1 }
53 #[inline] fn zero() -> i16 { 0 }
54 #[inline] fn maxval() -> i16 { i16::MAX }
55 #[inline] fn abs(&self) -> i16 { i16::saturating_abs(*self) }
56 #[inline] fn saturating_add(&self, other: Self) -> Self { i16::saturating_add(*self, other) }
57 #[inline] fn saturating_sub(&self, other: Self) -> Self { i16::saturating_sub(*self, other) }
58 #[inline] fn hard_bit(&self) -> bool { *self < 0 }
59}
60impl DecodeFrom for i32 {
61 #[inline] fn one() -> i32 { 1 }
62 #[inline] fn zero() -> i32 { 0 }
63 #[inline] fn maxval() -> i32 { i32::MAX }
64 #[inline] fn abs(&self) -> i32 { i32::saturating_abs(*self) }
65 #[inline] fn saturating_add(&self, other: Self) -> Self { i32::saturating_add(*self, other) }
66 #[inline] fn saturating_sub(&self, other: Self) -> Self { i32::saturating_sub(*self, other) }
67 #[inline] fn hard_bit(&self) -> bool { *self < 0 }
68}
69impl DecodeFrom for f32 {
70 #[inline] fn one() -> f32 { 1.0 }
71 #[inline] fn zero() -> f32 { 0.0 }
72 #[inline] fn maxval() -> f32 { f32::MAX }
73 #[inline] fn abs(&self) -> f32 { f32::from_bits(self.to_bits() & 0x7FFF_FFFF) }
74 #[inline] fn saturating_add(&self, other: Self) -> Self { *self + other }
75 #[inline] fn saturating_sub(&self, other: Self) -> Self { *self - other }
76 #[inline] fn hard_bit(&self) -> bool { *self < 0.0 }
77}
78impl DecodeFrom for f64 {
79 #[inline] fn one() -> f64 { 1.0 }
80 #[inline] fn zero() -> f64 { 0.0 }
81 #[inline] fn maxval() -> f64 { f64::MAX }
82 #[inline] fn abs(&self) -> f64 { f64::from_bits(self.to_bits() & 0x7FFF_FFFF_FFFF_FFFF) }
83 #[inline] fn saturating_add(&self, other: Self) -> Self { *self + other }
84 #[inline] fn saturating_sub(&self, other: Self) -> Self { *self - other }
85 #[inline] fn hard_bit(&self) -> bool { *self < 0.0 }
86}
87
88impl LDPCCode {
89
90 /// Get the length of [u8] required for the working area of `decode_bf`.
91 ///
92 /// Equal to n + punctured_bits.
93 pub const fn decode_bf_working_len(self) -> usize {
94 self.n() + self.punctured_bits()
95 }
96
97 /// Get the length of [T] required for the working area of `decode_ms`.
98 ///
99 /// Equal to 2 * paritycheck_sum + 3*n + 3*punctured_bits - 2*k.
100 pub const fn decode_ms_working_len(self) -> usize {
101 2 * self.paritycheck_sum() as usize + 3*self.n() + 3*self.punctured_bits() - 2*self.k()
102 }
103
104 /// Get the length of [u8] required for the working_u8 area of `decode_ms`.
105 ///
106 /// Equal to (n + punctured_bits - k)/8.
107 pub const fn decode_ms_working_u8_len(self) -> usize {
108 (self.n() + self.punctured_bits() - self.k()) / 8
109 }
110
111 /// Get the length of [u8] required for the output of any decoder.
112 ///
113 /// Equal to (n+punctured_bits)/8.
114 pub const fn output_len(self) -> usize {
115 (self.n() + self.punctured_bits()) / 8
116 }
117
118 /// Hard erasure decoding algorithm.
119 ///
120 /// Used to preprocess punctured codes before attempting bit-flipping decoding,
121 /// as the bit-flipping algorithm cannot handle erasures.
122 ///
123 /// The algorithm is:
124 /// * We compute the parity of each check over all non-erased bits
125 /// * We count how many erased bits are connected to each check (0, 1, or "more than 1")
126 /// * Then each parity check with exactly one erased variable casts a vote for
127 /// that variable, +1 if check parity is 1, otherwise -1
128 /// * Each variable that receives a majority vote (i.e. not equal 0) is set to that
129 /// vote and marked decoded
130 /// * Iterate until all variables are decoded or we reach the iteration limit
131 ///
132 /// This is based on the paper:
133 /// Novel multi-Gbps bit-flipping decoders for punctured LDPC codes,
134 /// by Archonta, Kanistras, and Paliouras, MOCAST 2016.
135 ///
136 /// * `codeword` must be (n+p)/8 long (`self.output_len()`), with the first n/8 bytes already
137 /// set to the received hard information, and the punctured bits at the end will be updated.
138 /// * `working` must be (n+p) bytes long (`self.decode_bf_working_len()`).
139 ///
140 /// Returns `(success, number of iterations run)`. Success only indicates that every punctured
141 /// bit got a majority vote; but they might still be wrong; likewise failure means not every
142 /// bit got a vote but many may still have been determined correctly.
143 #[allow(clippy::many_single_char_names)]
144 fn decode_erasures(self, codeword: &mut [u8], working: &mut [u8], maxiters: usize)
145 -> (bool, usize)
146 {
147 assert_eq!(codeword.len(), self.output_len());
148 assert_eq!(working.len(), self.decode_bf_working_len());
149
150 let n = self.n();
151 let p = self.punctured_bits();
152
153 // Working area:
154 // * The top bit 0x80 for byte 'i' is the parity bit for check 'i'.
155 // * The second and third top bits 0x60 for byte 'i' indicate the number of erased
156 // variables connected to check 'i':
157 // 00 for no erasures, 01 for a single erasure, 11 for more than one erasure
158 // * The fourth top bit 0x10 for byte 'a' indicates whether variable 'a' is erased
159 // * The lowest four bits 0x0F for byte 'a' indicate the votes received for variable 'a',
160 // starting at 8 for 0 votes and being incremented and decremented from there.
161
162 // Initialse working area: mark all punctured bits as erased
163 for w in &mut working[..n] { *w = 0x00 }
164 for w in &mut working[n..] { *w = 0x10 }
165
166 // Also write all the punctured bits in the codeword to zero
167 for c in &mut codeword[n/8..] { *c = 0x00 }
168
169 // Keep track of how many bits we've fixed
170 let mut bits_fixed = 0;
171
172 for iter in 0..maxiters {
173 // Initialise parity and erasure counts to zero, reset votes, preserve erasure bit
174 for w in &mut working[..] { *w = (*w & 0x10) | 0x08 }
175
176 // Compute check parity and erasure count
177 for (check, var) in self.iter_paritychecks() {
178 if working[var] & 0x10 == 0x10 {
179 // If var is erased, update check erasure count
180 match working[check] & 0x60 {
181 0x00 => working[check] |= 0x20,
182 0x20 => working[check] |= 0x40,
183 _ => (),
184 }
185 } else if codeword[var/8] >> (7-(var%8)) & 1 == 1 {
186 // If var is not erased and this codeword bit is set, update check parity
187 working[check] ^= 0x80;
188 }
189 }
190
191 // Now accumulate votes for each erased variable
192 for (check, var) in self.iter_paritychecks() {
193 // If this variable is erased and this check has only one vote
194 if working[var] & 0x10 == 0x10 && working[check] & 0x60 == 0x20 {
195 // Vote +1 if our parity is currently 1, -1 otherwise
196 if working[check] & 0x80 == 0x80 {
197 working[var] += 1;
198 } else {
199 working[var] -= 1;
200 }
201 }
202 }
203
204 // Finally fix all bits that are erased and have a majority vote
205 for (var, working) in working[0..(n+p)].iter_mut().enumerate() {
206 if *working & 0x10 == 0x10 {
207 if *working & 0x0F > 0x08 {
208 codeword[var/8] |= 1<<(7-(var%8));
209 *working &= !0x10;
210 }
211 bits_fixed += 1;
212 }
213 }
214
215 if bits_fixed == p {
216 // Hurray we're done
217 return (true, iter)
218 }
219 }
220
221 // If we finished the iteration loop then we did not succeed.
222 (false, maxiters)
223 }
224
225 /// Bit flipping decoder.
226 ///
227 /// This algorithm is quick but only operates on hard information and consequently leaves a
228 /// lot of error-correcting capability behind. It is around 1-2dB worse than the min-sum
229 /// decoder. However, it requires much less memory and is a lot quicker.
230 ///
231 /// Requires:
232 ///
233 /// * `input` must be `n/8` long, where each bit is the received hard information
234 /// * `output` must be `(n+punctured_bits)/8` (=`self.output_len()`) bytes long and is written
235 /// with the decoded codeword, so the user data is present in the first `k/8` bytes.
236 /// * `working` must be `n+punctured_bits` (=`self.decode_bf_working_len()`) bytes long.
237 ///
238 /// Runs for at most `maxiters` iterations, both when attempting to fix punctured erasures on
239 /// applicable codes, and in the main bit flipping decoder.
240 ///
241 /// Returns `(decoding success, iters)`. For punctured codes, `iters` includes iterations
242 /// of the erasure decoding algorithm which is run first.
243 pub fn decode_bf(self, input: &[u8], output: &mut [u8],
244 working: &mut [u8], maxiters: usize)
245 -> (bool, usize)
246 {
247 assert_eq!(input.len(), self.n()/8, "input.len() != n/8");
248 assert_eq!(output.len(), self.output_len(), "output.len != (n+p)/8");
249 assert_eq!(working.len(), self.decode_bf_working_len(), "working.len() incorrect");
250
251 output[..self.n()/8].copy_from_slice(input);
252
253 // For punctured codes we must first try and fix all the punctured bits.
254 // We run them through an erasure decoding algorithm and record how many iterations
255 // it took (so we can return the total).
256 let erasure_iters = if self.punctured_bits() > 0 {
257 let (_, iters) = self.decode_erasures(output, working, maxiters);
258 iters
259 } else { 0 };
260
261 // Working area: we use the top bit of the first k bytes to store that parity check,
262 // and the remaining 7 bits of the first n+p bytes to store violation count for that var.
263
264 for iter in 0..maxiters {
265 // Zero out violation counts
266 for v in &mut working[..] { *v = 0 }
267
268 // Calculate the parity of each parity check
269 for (check, var) in self.iter_paritychecks() {
270 if output[var/8] >> (7-(var%8)) & 1 == 1 {
271 working[check] ^= 0x80;
272 }
273 }
274
275 // Count how many parity violations each variable is associated with
276 let mut max_violations = 0;
277 for (check, var) in self.iter_paritychecks() {
278 if working[check] & 0x80 == 0x80 {
279 // Unless we have more than 127 checks for a single variable, this
280 // can't overflow into the parity bit. And we don't have that.
281 working[var] += 1;
282 if working[var] & 0x7F > max_violations {
283 max_violations = working[var] & 0x7F;
284 }
285 }
286 }
287
288 if max_violations == 0 {
289 return (true, iter + erasure_iters);
290 } else {
291 // Flip all the bits that have the maximum number of violations
292 for (var, violations) in working.iter().enumerate() {
293 if *violations & 0x7F == max_violations {
294 output[var/8] ^= 1<<(7-(var%8));
295 }
296 }
297 }
298 }
299
300 (false, maxiters + erasure_iters)
301 }
302
303 /// Message passing based min-sum decoder.
304 ///
305 /// This algorithm is slower and requires more memory than the bit-flipping decode, but
306 /// operates on soft information and provides very close to optimal decoding. If you don't have
307 /// soft information, you can use `hard_to_llrs` to go from hard information (bytes from a
308 /// receiver) to soft information (LLRs).
309 ///
310 /// Requires:
311 ///
312 /// * `llrs` must be `n` long, with positive numbers more likely to be a 0 bit.
313 /// * `output` must be allocated to (n+punctured_bits)/8 bytes, aka `output_len()`, of which
314 /// the first k/8 bytes will be set to the decoded message (and the rest to the parity bits
315 /// of the complete codeword)
316 /// * `working` is the main working area which must be provided and must have
317 /// `decode_ms_working_len()` elements, equal to
318 /// 2*paritycheck_sum + 3*n + 3*punctured_bits - 2*k
319 /// * `working_u8` is the secondary working area which must be provided and must have
320 /// `decode_ms_working_u8_len()` elements, equal to (n + punctured_bits - k)/8.
321 ///
322 /// Will run for at most `maxiters` iterations.
323 ///
324 /// Returns decoding success and the number of iterations run for.
325 ///
326 /// ## Log Likelihood Ratios and choice of `T`
327 ///
328 /// The `llrs` input is a list of signed numbers, one per bit, where positive numbers mean
329 /// a bit is more likely to be 0, and larger magnitude numbers indicate increased confidence
330 /// on a logarithmic scale (so every step increase is a multiplication of the confidence).
331 ///
332 /// This decoder is invariant to a linear scaling of all the LLRs (in other words, it is
333 /// invariant to the channel noise level), so you can choose any quantisation level and
334 /// fixed-point interpretation you desire. This means you can view `i8` as representing
335 /// the 256 numbers between -1 and +0.9921875, or as just representing -128 to +127.
336 ///
337 /// Internally, variables of type `T` are used to accumulate messages, so it is useful to leave
338 /// some headroom in `T` after the range of your LLRs. For `T=i8` you might assign -32 to 31
339 /// for LLR inputs, so that several full-scale messages can be accumulated before saturation
340 /// occurs. On floating point types this is less of a concern.
341 ///
342 /// This also means if you only have hard information it makes no practical difference what
343 /// exact value you give the LLRs, but in the interests of avoiding saturation you may as
344 /// well pick +-1 in any unit (and you may as well use i8 since the additional range will
345 /// not be of benefit).
346 #[allow(clippy::cognitive_complexity,clippy::many_single_char_names)]
347 pub fn decode_ms<T: DecodeFrom>(self, llrs: &[T], output: &mut [u8],
348 working: &mut [T], working_u8: &mut [u8],
349 maxiters: usize)
350 -> (bool, usize)
351 {
352 let n = self.n();
353 let k = self.k();
354 let p = self.punctured_bits();
355
356 assert_eq!(llrs.len(), n, "llrs.len() != n");
357 assert_eq!(output.len(), self.output_len(), "output.len() != (n+p)/8");
358 assert_eq!(working.len(), self.decode_ms_working_len(), "working.len() incorrect");
359 assert_eq!(working_u8.len(), self.decode_ms_working_u8_len(), "working_u8 != (n+p-k)/8");
360
361 // Rename output to parities as we'll use it to keep track of the status of each check's
362 // parity equations, until using it to write the decoded output on completion.
363 let parities = output;
364
365 // Rename working_u8 to ui_sgns.
366 // It will be used to accumulate products of signs of each check's incoming messages.
367 let ui_sgns = working_u8;
368 for x in &mut ui_sgns[..] { *x = 0 }
369
370 // Split up the working area.
371 // `u` contains check-to-variable messages, `v` contains variable-to-check messages,
372 // `va` contains the marginal a posteriori LLRs for each variable, `ui_min*` tracks the
373 // smallest and second-smallest variable-to-check message for each check.
374 for w in &mut working[..] { *w = T::zero() }
375 let (u, working) = working.split_at_mut(self.paritycheck_sum() as usize);
376 let (v, working) = working.split_at_mut(self.paritycheck_sum() as usize);
377 let (va, working) = working.split_at_mut(n + p);
378 let (ui_min1, ui_min2) = working.split_at_mut(n + p - k);
379
380 for iter in 0..maxiters {
381 // Initialise the marginals to the input LLRs (and to 0 for punctured bits).
382 va[..llrs.len()].copy_from_slice(llrs);
383 for x in &mut va[llrs.len()..] { *x = T::zero() }
384
385 // You'd think .enumerate() would be sensible, but actually it prevents
386 // inlining the iterator's next() method, which leads to a big performance hit.
387 let mut idx = 0;
388 for (check, var) in self.iter_paritychecks() {
389 // If this variable-to-check message equals the minimum of all messages to this
390 // check, use the second-minimum to obtain the minimum-excluding-this-variable.
391 if v[idx].abs() == ui_min1[check] {
392 u[idx] = ui_min2[check];
393 } else {
394 u[idx] = ui_min1[check];
395 }
396
397 // When the product of all incoming signs was -1, negate `u`.
398 if ui_sgns[check/8] >> (check%8) & 1 == 1 {
399 u[idx] = -u[idx];
400 }
401
402 // Remove the effect of the sign of this variable's message to this check node.
403 if v[idx].hard_bit() {
404 u[idx] = -u[idx];
405 }
406
407 // Accumulate with all incoming messages to this variable.
408 va[var] = va[var].saturating_add(u[idx]);
409
410 idx += 1;
411 }
412
413 // Compute variable-to-check messages `v`.
414 for x in &mut ui_min1[..] { *x = T::maxval() }
415 for x in &mut ui_min2[..] { *x = T::maxval() }
416 for x in &mut ui_sgns[..] { *x = 0 }
417 for x in &mut parities[..] { *x = 0 }
418 idx = 0;
419 for (check, var) in self.iter_paritychecks() {
420 // Compute new `v`, with self-correcting behaviour.
421 let new_v_ai = va[var].saturating_sub(u[idx]);
422 if new_v_ai.hard_bit() == v[idx].hard_bit() || v[idx] == T::zero() {
423 v[idx] = new_v_ai;
424 } else {
425 v[idx] = T::zero();
426 }
427
428 // Track the smallest and second-smallest abs(variable-to-check) messages.
429 // These may be the same if two messages have identical magnitude.
430 if v[idx].abs() < ui_min1[check] {
431 ui_min2[check] = ui_min1[check];
432 ui_min1[check] = v[idx].abs();
433 } else if v[idx].abs() < ui_min2[check] {
434 ui_min2[check] = v[idx].abs();
435 }
436
437 // Accumulate product of signs of variable-to-check messages, with a set
438 // bit meaning the product is negative.
439 if v[idx].hard_bit() {
440 ui_sgns[check/8] ^= 1<<(check%8);
441 }
442
443 // Accumulate output of each check's parity equation, used to detect
444 // when all parity checks are satisfied and thus decoding is complete.
445 if va[var].hard_bit() {
446 parities[check/8] ^= 1<<(check%8);
447 }
448
449 idx += 1;
450 }
451
452 // Check parity equations. If none are 1 then we have a valid codeword.
453 if *parities.iter().max().unwrap() == 0 {
454 // Hard decode marginals into the output.
455 let output = parities;
456 for o in &mut output[..] { *o = 0 }
457 for (var, &va) in va[0..(n+p)].iter().enumerate() {
458 if va.hard_bit() {
459 output[var/8] |= 1 << (7 - (var%8));
460 }
461 }
462 return (true, iter);
463 }
464 }
465
466 // If we failed to find a codeword, at least hard decode the marginals into the output.
467 let output = parities;
468 for o in &mut output[..] { *o = 0 }
469 for (var, &va) in va[0..(n+p)].iter().enumerate() {
470 if va.hard_bit() {
471 output[var/8] |= 1 << (7 - (var%8));
472 }
473 }
474 (false, maxiters)
475 }
476
477 /// Convert hard information into LLRs.
478 ///
479 /// The min-sum decoding used in `decode_ms` is invariant to linear scaling
480 /// in LLR, so it doesn't matter which value is picked so long as the sign
481 /// is correct. This function just assigns -/+ 1 for 1/0 bits.
482 ///
483 /// `input` must be n/8 long, `llrs` must be n long.
484 pub fn hard_to_llrs<T: DecodeFrom>(self, input: &[u8], llrs: &mut [T]) {
485 assert_eq!(input.len(), self.n()/8, "input.len() != n/8");
486 assert_eq!(llrs.len(), self.n(), "llrs.len() != n");
487 let llr = -T::one();
488 for (idx, byte) in input.iter().enumerate() {
489 for i in 0..8 {
490 llrs[idx*8 + i] = if (byte >> (7-i)) & 1 == 1 { llr } else { -llr };
491 }
492 }
493 }
494
495 /// Convert LLRs into hard information.
496 ///
497 /// `llrs` must be n long, `output` must be n/8 long.
498 pub fn llrs_to_hard<T: DecodeFrom>(self, llrs: &[T], output: &mut [u8]) {
499 assert_eq!(llrs.len(), self.n(), "llrs.len() != n");
500 assert_eq!(output.len(), self.n()/8, "output.len() != n/8");
501
502 for o in &mut output[..] { *o = 0 }
503
504 for (i, llr) in llrs.iter().enumerate() {
505 if llr.hard_bit() {
506 output[i/8] |= 1 << (7 - (i%8));
507 }
508 }
509 }
510}
511
512#[cfg(test)]
513mod tests {
514 use std::prelude::v1::*;
515
516 use crate::codes::{LDPCCode, CodeParams,
517 TC128_PARAMS, TC256_PARAMS, TC512_PARAMS,
518 TM1280_PARAMS, TM1536_PARAMS, TM2048_PARAMS,
519 TM5120_PARAMS, TM6144_PARAMS, TM8192_PARAMS};
520
521 const CODES: [LDPCCode; 9] = [LDPCCode::TC128, LDPCCode::TC256, LDPCCode::TC512,
522 LDPCCode::TM1280, LDPCCode::TM1536, LDPCCode::TM2048,
523 LDPCCode::TM5120, LDPCCode::TM6144, LDPCCode::TM8192,
524 ];
525
526 const PARAMS: [CodeParams; 9] = [TC128_PARAMS, TC256_PARAMS, TC512_PARAMS,
527 TM1280_PARAMS, TM1536_PARAMS, TM2048_PARAMS,
528 TM5120_PARAMS, TM6144_PARAMS, TM8192_PARAMS,
529 ];
530
531 #[test]
532 fn test_decode_ms_working_len() {
533 for (code, param) in CODES.iter().zip(PARAMS.iter()) {
534 assert_eq!(code.decode_ms_working_len(), param.decode_ms_working_len);
535 assert_eq!(code.decode_ms_working_u8_len(), param.decode_ms_working_u8_len);
536 }
537 }
538
539 #[test]
540 fn test_decode_bf_working_len() {
541 for (code, param) in CODES.iter().zip(PARAMS.iter()) {
542 assert_eq!(code.decode_bf_working_len(), param.decode_bf_working_len);
543 }
544 }
545
546 #[test]
547 fn test_output_len() {
548 for (code, param) in CODES.iter().zip(PARAMS.iter()) {
549 assert_eq!(code.output_len(), param.output_len);
550 }
551 }
552
553 #[test]
554 fn test_hard_to_llrs() {
555 let code = LDPCCode::TC128;
556 let hard = vec![255, 254, 253, 252, 251, 250, 249, 248,
557 203, 102, 103, 120, 107, 30, 157, 169];
558 let mut llrs = vec![0f32; code.n()];
559 let llr = -1.0;
560 code.hard_to_llrs(&hard, &mut llrs);
561 assert_eq!(llrs, vec![
562 llr, llr, llr, llr, llr, llr, llr, llr,
563 llr, llr, llr, llr, llr, llr, llr, -llr,
564 llr, llr, llr, llr, llr, llr, -llr, llr,
565 llr, llr, llr, llr, llr, llr, -llr, -llr,
566 llr, llr, llr, llr, llr, -llr, llr, llr,
567 llr, llr, llr, llr, llr, -llr, llr, -llr,
568 llr, llr, llr, llr, llr, -llr, -llr, llr,
569 llr, llr, llr, llr, llr, -llr, -llr, -llr,
570 llr, llr, -llr, -llr, llr, -llr, llr, llr,
571 -llr, llr, llr, -llr, -llr, llr, llr, -llr,
572 -llr, llr, llr, -llr, -llr, llr, llr, llr,
573 -llr, llr, llr, llr, llr, -llr, -llr, -llr,
574 -llr, llr, llr, -llr, llr, -llr, llr, llr,
575 -llr, -llr, -llr, llr, llr, llr, llr, -llr,
576 llr, -llr, -llr, llr, llr, llr, -llr, llr,
577 llr, -llr, llr, -llr, llr, -llr, -llr, llr]);
578 }
579
580 #[test]
581 fn test_llrs_to_hard() {
582 let code = LDPCCode::TC128;
583 let llr = -1.0;
584 let llrs = vec![
585 llr, llr, llr, llr, llr, llr, llr, llr,
586 llr, llr, llr, llr, llr, llr, llr, -llr,
587 llr, llr, llr, llr, llr, llr, -llr, llr,
588 llr, llr, llr, llr, llr, llr, -llr, -llr,
589 llr, llr, llr, llr, llr, -llr, llr, llr,
590 llr, llr, llr, llr, llr, -llr, llr, -llr,
591 llr, llr, llr, llr, llr, -llr, -llr, llr,
592 llr, llr, llr, llr, llr, -llr, -llr, -llr,
593 llr, llr, -llr, -llr, llr, -llr, llr, llr,
594 -llr, llr, llr, -llr, -llr, llr, llr, -llr,
595 -llr, llr, llr, -llr, -llr, llr, llr, llr,
596 -llr, llr, llr, llr, llr, -llr, -llr, -llr,
597 -llr, llr, llr, -llr, llr, -llr, llr, llr,
598 -llr, -llr, -llr, llr, llr, llr, llr, -llr,
599 llr, -llr, -llr, llr, llr, llr, -llr, llr,
600 llr, -llr, llr, -llr, llr, -llr, -llr, llr];
601 let mut hard = vec![0u8; code.n()/8];
602 code.llrs_to_hard(&llrs, &mut hard);
603 assert_eq!(hard, vec![255, 254, 253, 252, 251, 250, 249, 248,
604 203, 102, 103, 120, 107, 30, 157, 169]);
605 }
606
607 #[test]
608 fn test_decode_erasures() {
609 for code in &CODES {
610 // Only bother testing codes that actually have punctured bits
611 if code.punctured_bits() == 0 {
612 continue;
613 }
614
615 // Encode a codeword
616 let txdata: Vec<u8> = (0..code.k()/8).map(|x| x as u8).collect();
617 let mut txcode = vec![0u8; code.n()/8];
618 code.copy_encode(&txdata, &mut txcode);
619
620 // Allocate working area
621 let mut working = vec![0u8; code.decode_bf_working_len()];
622 let mut output = vec![0u8; code.output_len()];
623
624 // Copy TX codeword into output manually (normally done by `decode_bf()`).
625 output[..txcode.len()].copy_from_slice(&txcode);
626
627 // Run erasure decoder
628 let (success, _) = code.decode_erasures(&mut output, &mut working, 50);
629
630 assert!(success);
631
632 // Now compare the result against the min-sum decoder which should
633 // also correctly decode the punctured parity bits
634 let mut llrs = vec![0i8; code.n()];
635 let mut working_ms = vec![0i8; code.decode_ms_working_len()];
636 let mut working_u8_ms = vec![0u8; code.decode_ms_working_u8_len()];
637 let mut output_ms = vec![0u8; code.output_len()];
638 code.hard_to_llrs(&txcode, &mut llrs);
639 let (success, _) = code.decode_ms(&llrs, &mut output_ms, &mut working_ms,
640 &mut working_u8_ms, 50);
641
642 assert!(success);
643 assert_eq!(output, output_ms);
644 }
645 }
646
647 #[test]
648 fn test_decode_bf() {
649 for code in &CODES {
650 // Make up some TX data
651 let txdata: Vec<u8> = (0..code.k()/8).map(|x| x as u8).collect();
652 let mut txcode = vec![0u8; code.n()/8];
653 code.copy_encode(&txdata, &mut txcode);
654
655 // Copy it and corrupt some bits
656 let mut rxcode = txcode.clone();
657 rxcode[0] ^= 1<<7 | 1<<5 | 1<<3;
658
659 // Allocate working area and output area
660 let mut working = vec![0u8; code.decode_bf_working_len()];
661 let mut output = vec![0u8; code.output_len()];
662
663 // Run decoder
664 let (success, _) = code.decode_bf(&rxcode, &mut output, &mut working, 50);
665
666 assert!(success);
667 assert_eq!(&txcode[..], &output[..txcode.len()]);
668 }
669
670 }
671 #[test]
672 fn test_decode_ms() {
673 for code in &CODES {
674 // Make up a TX codeword
675 let txdata: Vec<u8> = (0..code.k()/8).map(|x| x as u8).collect();
676 let mut txcode = vec![0u8; code.n()/8];
677 code.copy_encode(&txdata, &mut txcode);
678
679 // Copy it and corrupt some bits
680 let mut rxcode = txcode.clone();
681 rxcode[0] ^= 1<<7 | 1<<5 | 1<<3;
682
683 // Convert the hard data to LLRs
684 let mut llrs = vec![0i8; code.n()];
685 code.hard_to_llrs(&rxcode, &mut llrs);
686
687 // Allocate working area and output area
688 let mut working = vec![0i8; code.decode_ms_working_len()];
689 let mut working_u8 = vec![0u8; code.output_len() - code.k()/8];
690 let mut output = vec![0u8; code.output_len()];
691
692 // Run decoder
693 let (success, _) = code.decode_ms(&llrs, &mut output, &mut working,
694 &mut working_u8, 50);
695
696 assert!(success);
697 assert_eq!(&txcode[..], &output[..txcode.len()]);
698 }
699 }
700}