Skip to main content

comnum_fec/
turbo.rs

1//! Turbo codes: two recursive systematic codes in parallel, decoded iteratively.
2//!
3//! The encoder of Berrou, Glavieux & Thitimajshima (1993) in the form of LTE (3GPP TS
4//! 36.212, Sec. 5.1.3.2): the $K$ information bits enter a recursive systematic code
5//! $(13, 15)_{8}$ of constraint length 4, and their interleaved copy a second one. The
6//! codeword is the systematic bits, the two parity sequences and the 12 tail bits that
7//! terminate both encoders, $n = 3K + 12$.
8//!
9//! The decoder exchanges extrinsic information between two BCJR decoders: each
10//! computes the a posteriori LLRs of the information bits with the extrinsic LLRs of
11//! the other as a priori, and passes on the difference
12//! $L_{e} = L_{\mathrm{app}} - L_{a} - L_{s}$. A few iterations reach within about 1 dB of
13//! capacity for long blocks.
14//!
15//! # Examples
16//! ```
17//! use comnum_fec::block::BlockCode;
18//! use comnum_fec::turbo::TurboCode;
19//! let code = TurboCode::lte(40)?;                         // K = 40, QPP (3, 10)
20//! let info: Vec<u8> = (0..40).map(|i| (i % 3 == 1) as u8).collect();
21//! let c = code.encode_block(&info)?;
22//! assert_eq!(c.len(), 3 * 40 + 12);
23//! let llr: Vec<f64> = c.iter().map(|&b| if b == 0 { 2.0 } else { -2.0 }).collect(); // noiseless
24//! assert_eq!(code.decode_block(&llr)?, info);
25//! # Ok::<(), comnum_core::Error>(())
26//! ```
27//!
28//! # References
29//!
30//! 1. C. Berrou, A. Glavieux and P. Thitimajshima, "Near Shannon limit error-correcting
31//!    coding and decoding: turbo-codes," *Proc. IEEE ICC*, 1064–1070, 1993.
32//! 2. 3GPP TS 36.212, *E-UTRA; Multiplexing and channel coding*, Sec. 5.1.3.2.
33//! 3. J. Sun and O. Y. Takeshita, "Interleavers for turbo codes using permutation
34//!    polynomials over integer rings," *IEEE Trans. Inf. Theory*, 51(1), 101–119, 2005.
35
36use crate::block::{BlockCode, SoftDecode, container_traits, expect_len};
37use crate::conv::{BcjrWorkspace, Trellis, bcjr_into};
38use crate::interleaving::Interleaver;
39use comnum_core::signal::hard_decisions;
40use comnum_core::{Result, ensure};
41use comnum_math::Jacobian;
42
43/// The seed of the random interleaver of [`TurboCode::lte`] for a block size outside
44/// the table of LTE.
45pub const RANDOM_INTERLEAVER_SEED: u64 = 7;
46
47/// The settings of the iterative decoder of a [`TurboCode`].
48#[derive(Clone, Copy, Debug, PartialEq, Eq)]
49pub struct TurboDecoder {
50    /// Decoding iterations (0: the a posteriori LLRs are the systematic channel LLRs).
51    pub iterations: usize,
52    /// log-MAP ([`Jacobian::Exact`]) or max-log-MAP ([`Jacobian::MaxLog`]) decoding.
53    pub jacobian: Jacobian,
54}
55
56impl Default for TurboDecoder {
57    /// 8 iterations of log-MAP.
58    fn default() -> TurboDecoder {
59        TurboDecoder { iterations: 8, jacobian: Jacobian::Exact }
60    }
61}
62
63/// A parallel concatenation of two $(13, 15)_{8}$ recursive systematic codes.
64#[derive(Clone, Debug, PartialEq)]
65pub struct TurboCode {
66    /// The constituent trellis (two outputs per step: systematic, parity).
67    trellis: Trellis,
68    /// The interleaver of the second encoder ($K$ positions).
69    interleaver: Interleaver,
70    /// The decoder settings.
71    pub decoder: TurboDecoder,
72}
73
74/// The QPP coefficients $(K, f_{1}, f_{2})$ of the turbo interleaver of LTE, 3GPP TS 36.212,
75/// Table 5.1.3-3 (transcribed from the table of Sionna, `turbo_coeffs.csv`, Apache-2.0,
76/// see `THIRD_PARTY.md`; every entry is checked to be a permutation, and the codewords
77/// of four sizes against the reference vectors of Sionna).
78#[rustfmt::skip]
79const LTE_QPP: [(usize, usize, usize); 188] = [
80    (40, 3, 10), (48, 7, 12), (56, 19, 42), (64, 7, 16), (72, 7, 18), (80, 11, 20), (88, 5, 22),
81    (96, 11, 24), (104, 7, 26), (112, 41, 84), (120, 103, 90), (128, 15, 32), (136, 9, 34),
82    (144, 17, 108), (152, 9, 38), (160, 21, 120), (168, 101, 84), (176, 21, 44), (184, 57, 46),
83    (192, 23, 48), (200, 13, 50), (208, 27, 52), (216, 11, 36), (224, 27, 56), (232, 85, 58),
84    (240, 29, 60), (248, 33, 62), (256, 15, 32), (264, 17, 198), (272, 33, 68), (280, 103, 210),
85    (288, 19, 36), (296, 19, 74), (304, 37, 76), (312, 19, 78), (320, 21, 120), (328, 21, 82),
86    (336, 115, 84), (344, 193, 86), (352, 21, 44), (360, 133, 90), (368, 81, 46), (376, 45, 94),
87    (384, 23, 48), (392, 243, 98), (400, 151, 40), (408, 155, 102), (416, 25, 52), (424, 51, 106),
88    (432, 47, 72), (440, 91, 110), (448, 29, 168), (456, 29, 114), (464, 247, 58), (472, 29, 118),
89    (480, 89, 180), (488, 91, 122), (496, 157, 62), (504, 55, 84), (512, 31, 64), (528, 17, 66),
90    (544, 35, 68), (560, 227, 420), (576, 65, 96), (592, 19, 74), (608, 37, 76), (624, 41, 234),
91    (640, 39, 80), (656, 185, 82), (672, 43, 252), (688, 21, 86), (704, 155, 44), (720, 79, 120),
92    (736, 139, 92), (752, 23, 94), (768, 217, 48), (784, 25, 98), (800, 17, 80), (816, 127, 102),
93    (832, 25, 52), (848, 239, 106), (864, 17, 48), (880, 137, 110), (896, 215, 112), (912, 29, 114),
94    (928, 15, 58), (944, 147, 118), (960, 29, 60), (976, 59, 122), (992, 65, 124), (1008, 55, 84),
95    (1024, 31, 64), (1056, 17, 66), (1088, 171, 204), (1120, 67, 140), (1152, 35, 72),
96    (1184, 19, 74), (1216, 39, 76), (1248, 19, 78), (1280, 199, 240), (1312, 21, 82),
97    (1344, 211, 252), (1376, 21, 86), (1408, 43, 88), (1440, 149, 60), (1472, 45, 92),
98    (1504, 49, 846), (1536, 71, 48), (1568, 13, 28), (1600, 17, 80), (1632, 25, 102),
99    (1664, 183, 104), (1696, 55, 954), (1728, 127, 96), (1760, 27, 110), (1792, 29, 112),
100    (1824, 29, 114), (1856, 57, 116), (1888, 45, 354), (1920, 31, 120), (1952, 59, 610),
101    (1984, 185, 124), (2016, 113, 420), (2048, 31, 64), (2112, 17, 66), (2176, 171, 136),
102    (2240, 209, 420), (2304, 253, 216), (2368, 367, 444), (2432, 265, 456), (2496, 181, 468),
103    (2560, 39, 80), (2624, 27, 164), (2688, 127, 504), (2752, 143, 172), (2816, 43, 88),
104    (2880, 29, 300), (2944, 45, 92), (3008, 157, 188), (3072, 47, 96), (3136, 13, 28),
105    (3200, 111, 240), (3264, 443, 204), (3328, 51, 104), (3392, 51, 212), (3456, 451, 192),
106    (3520, 257, 220), (3584, 57, 336), (3648, 313, 228), (3712, 271, 232), (3776, 179, 236),
107    (3840, 331, 120), (3904, 363, 244), (3968, 375, 248), (4032, 127, 168), (4096, 31, 64),
108    (4160, 33, 130), (4224, 43, 264), (4288, 33, 134), (4352, 477, 408), (4416, 35, 138),
109    (4480, 233, 280), (4544, 357, 142), (4608, 337, 480), (4672, 37, 146), (4736, 71, 444),
110    (4800, 71, 120), (4864, 37, 152), (4928, 39, 462), (4992, 127, 234), (5056, 39, 158),
111    (5120, 39, 80), (5184, 31, 96), (5248, 113, 902), (5312, 41, 166), (5376, 251, 336),
112    (5440, 43, 170), (5504, 21, 86), (5568, 43, 174), (5632, 45, 176), (5696, 45, 178),
113    (5760, 161, 120), (5824, 89, 182), (5888, 323, 184), (5952, 47, 186), (6016, 23, 94),
114    (6080, 47, 190), (6144, 263, 480),
115];
116
117/// The QPP coefficients $(f_{1}, f_{2})$ of the turbo interleaver of LTE for the block
118/// size `k` (TS 36.212, Table 5.1.3-3), `None` for a size outside the table.
119pub fn lte_qpp(k: usize) -> Option<(usize, usize)> {
120    LTE_QPP.binary_search_by_key(&k, |e| e.0).ok().map(|i| (LTE_QPP[i].1, LTE_QPP[i].2))
121}
122
123impl TurboCode {
124    /// The code of LTE: the quadratic permutation polynomial interleaver of [`lte_qpp`]
125    /// for the 188 block sizes of TS 36.212 (40 to 6144), a random interleaver of the
126    /// same length (seeded by [`RANDOM_INTERLEAVER_SEED`]) for any other size.
127    ///
128    /// # Errors
129    /// [`comnum_core::Error::Config`] for $K = 0$.
130    pub fn lte(k: usize) -> Result<TurboCode> {
131        let interleaver = match lte_qpp(k) {
132            Some((f1, f2)) => Interleaver::qpp(k, f1, f2)?,
133            None => Interleaver::random(k, RANDOM_INTERLEAVER_SEED),
134        };
135        TurboCode::with_interleaver(interleaver)
136    }
137
138    /// The code with a given interleaver (its length is $K$), the decoder of
139    /// [`TurboDecoder::default`] (8 iterations of log-MAP).
140    ///
141    /// # Errors
142    /// [`comnum_core::Error::Config`] for an empty interleaver.
143    pub fn with_interleaver(interleaver: Interleaver) -> Result<TurboCode> {
144        ensure(!interleaver.is_empty(), || "a turbo code needs at least one information bit".into())?;
145        let trellis = Trellis::recursive(4, 0o13, 0o15).unwrap_or_else(|_| unreachable!("a valid code"));
146        Ok(TurboCode { trellis, interleaver, decoder: TurboDecoder::default() })
147    }
148
149    /// The constituent trellis, the $(13, 15)_{8}$ recursive systematic code.
150    pub fn trellis(&self) -> &Trellis {
151        &self.trellis
152    }
153
154    /// The interleaver of the second encoder.
155    pub fn interleaver(&self) -> &Interleaver {
156        &self.interleaver
157    }
158
159    fn tail_len(&self) -> usize {
160        self.trellis.constraint as usize - 1
161    }
162
163    /// Split a coded block into its systematic, parity and tail parts, as the LLR
164    /// sequences of the two constituent decoders (systematic and parity interleaved
165    /// per step, the tail appended).
166    fn constituents(&self, llr: &[f64]) -> (Vec<f64>, Vec<f64>) {
167        let k = self.k();
168        let m = self.tail_len();
169        let (sys, rest) = llr.split_at(k);
170        let (p1, rest) = rest.split_at(k);
171        let (p2, tail) = rest.split_at(k);
172        let sys2 = self.interleaver.interleave(sys);
173        let mut a = Vec::with_capacity(2 * (k + m));
174        let mut b = Vec::with_capacity(2 * (k + m));
175        for i in 0..k {
176            a.extend([sys[i], p1[i]]);
177            b.extend([sys2[i], p2[i]]);
178        }
179        a.extend(&tail[..2 * m]);
180        b.extend(&tail[2 * m..]);
181        (a, b)
182    }
183}
184
185impl BlockCode for TurboCode {
186    fn k(&self) -> usize {
187        self.interleaver.len()
188    }
189
190    fn n(&self) -> usize {
191        3 * self.k() + 4 * self.tail_len()
192    }
193
194    fn encode_block(&self, info: &[u8]) -> Result<Vec<u8>> {
195        let k = self.k();
196        expect_len(info.len(), k, "bits")?;
197        let c1 = self.trellis.encode(info, true);
198        let c2 = self.trellis.encode(&self.interleaver.interleave(info), true);
199        let mut out = info.to_vec();
200        out.extend((0..k).map(|i| c1[2 * i + 1]));
201        out.extend((0..k).map(|i| c2[2 * i + 1]));
202        out.extend(&c1[2 * k..]);
203        out.extend(&c2[2 * k..]);
204        Ok(out)
205    }
206
207    fn decode_block(&self, llr: &[f64]) -> Result<Vec<u8>> {
208        Ok(hard_decisions(&self.app(llr, None)?))
209    }
210}
211
212impl SoftDecode for TurboCode {
213    /// The a posteriori LLRs of the second decoder after the last iteration,
214    /// deinterleaved; the a priori LLRs add to the systematic channel LLRs. With 0
215    /// iterations, the systematic LLRs (and the a priori ones).
216    fn app_into(&self, llr: &[f64], apriori: Option<&[f64]>, out: &mut [f64]) -> Result<usize> {
217        expect_len(llr.len(), self.n(), "LLRs")?;
218        let k = self.k();
219        expect_len(out.len(), k, "a posteriori LLRs")?;
220        let with_prior;
221        let llr = match apriori {
222            None => llr,
223            Some(a) => {
224                expect_len(a.len(), k, "a priori LLRs")?;
225                let mut v = llr.to_vec();
226                for (l, a) in v.iter_mut().zip(a) {
227                    *l += a;
228                }
229                with_prior = v;
230                &with_prior
231            }
232        };
233        let sys = &llr[..k];
234        if self.decoder.iterations == 0 {
235            out.copy_from_slice(sys);
236            return Ok(0);
237        }
238        let m = self.tail_len();
239        let (a, b) = self.constituents(llr);
240        let sys2 = self.interleaver.interleave(sys);
241        iterate(&self.trellis, &self.trellis, &a, &b, sys, &sys2, self.interleaver.perm(), k + m, self.decoder, out)
242    }
243}
244
245container_traits!(TurboCode);
246
247/// The iterations of a parallel turbo decoder: the BCJR of the first constituent code on
248/// its channel LLRs `la` with the a priori LLRs of the second, deinterleaved, then the
249/// BCJR of the second on `lb` with the extrinsic LLRs of the first, interleaved by
250/// `perm`; `sys` are the systematic LLRs ($k$ values, `sys2` interleaved), `steps` the
251/// trellis steps of both codes (the tails included). The a posteriori LLRs of the second
252/// decoder after the last iteration, deinterleaved, into `out`; returns the iterations.
253/// Shared by [`TurboCode`] and [`crate::ccsds::CcsdsTurbo`].
254#[allow(clippy::too_many_arguments, reason = "the two codes, their LLRs and the permutation")]
255pub(crate) fn iterate(
256    first: &Trellis,
257    second: &Trellis,
258    la: &[f64],
259    lb: &[f64],
260    sys: &[f64],
261    sys2: &[f64],
262    perm: &[usize],
263    steps: usize,
264    decoder: TurboDecoder,
265    out: &mut [f64],
266) -> Result<usize> {
267    let TurboDecoder { iterations, jacobian } = decoder;
268    // the a priori LLRs of the two decoders, zero on their tails; la1 holds the
269    // extrinsic LLRs of decoder 2, deinterleaved
270    let (mut la1, mut la2) = (vec![0.0; steps], vec![0.0; steps]);
271    let (mut app1, mut app2) = (Vec::with_capacity(steps), Vec::with_capacity(steps));
272    let mut ws = BcjrWorkspace::default();
273    for _ in 0..iterations {
274        bcjr_into(first, la, &la1, true, jacobian, &mut ws, &mut app1)?;
275        for (i, &p) in perm.iter().enumerate() {
276            la2[i] = app1[p] - la1[p] - sys[p];
277        }
278        bcjr_into(second, lb, &la2, true, jacobian, &mut ws, &mut app2)?;
279        for (i, &p) in perm.iter().enumerate() {
280            la1[p] = app2[i] - la2[i] - sys2[i];
281        }
282    }
283    for (i, &p) in perm.iter().enumerate() {
284        out[p] = app2[i];
285    }
286    Ok(iterations)
287}
288
289#[cfg(test)]
290mod tests {
291    use super::*;
292
293    #[test]
294    fn the_lte_qpp_table_gives_permutations() {
295        assert_eq!((LTE_QPP[0], LTE_QPP[187]), ((40, 3, 10), (6144, 263, 480)));
296        assert!(LTE_QPP.windows(2).all(|w| w[0].0 < w[1].0));
297        for &(k, f1, f2) in &LTE_QPP {
298            assert!(Interleaver::qpp(k, f1, f2).is_ok(), "K = {k}");
299        }
300        assert_eq!(lte_qpp(432), Some((47, 72)));
301        assert_eq!(lte_qpp(41), None);
302    }
303    use crate::block::testing::awgn_llr;
304    use crate::bounds::bpsk_noise_variance;
305    use comnum_core::rng::Rng;
306
307    /// The decoder as it was written before the soft output: the hard decisions of
308    /// the deinterleaved a posteriori LLRs of the second decoder.
309    fn former_decode(code: &TurboCode, llr: &[f64]) -> Vec<u8> {
310        let k = code.k();
311        let m = code.tail_len();
312        let (a, b) = code.constituents(llr);
313        let sys = &llr[..k];
314        let sys2 = code.interleaver.interleave(sys);
315        let perm = code.interleaver.perm();
316        let (mut la1, mut la2) = (vec![0.0; k + m], vec![0.0; k + m]);
317        let (mut app1, mut app2) = (Vec::new(), Vec::new());
318        let mut ws = BcjrWorkspace::default();
319        for _ in 0..code.decoder.iterations.max(1) {
320            bcjr_into(&code.trellis, &a, &la1, true, code.decoder.jacobian, &mut ws, &mut app1).unwrap();
321            for (i, &p) in perm.iter().enumerate() {
322                la2[i] = app1[p] - la1[p] - sys[p];
323            }
324            bcjr_into(&code.trellis, &b, &la2, true, code.decoder.jacobian, &mut ws, &mut app2).unwrap();
325            for (i, &p) in perm.iter().enumerate() {
326                la1[p] = app2[i] - la2[i] - sys2[i];
327            }
328        }
329        let mut app = vec![0.0; k];
330        for (i, &p) in perm.iter().enumerate() {
331            app[p] = app2[i];
332        }
333        hard_decisions(&app)
334    }
335
336    #[test]
337    fn the_soft_output_gives_the_decisions_of_the_former_decoder() {
338        let mut r = Rng::new(4);
339        for jacobian in [Jacobian::Exact, Jacobian::MaxLog] {
340            let mut code = TurboCode::lte(104).unwrap();
341            code.decoder.jacobian = jacobian;
342            for _ in 0..10 {
343                let info: Vec<u8> = (0..104).map(|_| r.bit()).collect();
344                let s2 = bpsk_noise_variance(0.5, code.code_rate());
345                let y = awgn_llr(&code.encode_block(&info).unwrap(), s2, &mut r);
346                assert_eq!(code.decode_block(&y).unwrap(), former_decode(&code, &y));
347            }
348        }
349        // the a priori LLRs are those of the systematic bits
350        let code = TurboCode::lte(40).unwrap();
351        let y: Vec<f64> = (0..code.n()).map(|_| r.gaussian()).collect();
352        let la: Vec<f64> = (0..40).map(|_| r.gaussian()).collect();
353        let mut shifted = y.clone();
354        shifted.iter_mut().zip(&la).for_each(|(v, a)| *v += a);
355        let (with, moved) = (code.app(&y, Some(&la)).unwrap(), code.app(&shifted, None).unwrap());
356        assert!(with.iter().zip(&moved).all(|(a, b)| a.to_bits() == b.to_bits()));
357        // no iteration: the systematic LLRs
358        let none = TurboCode { decoder: TurboDecoder { iterations: 0, jacobian: Jacobian::Exact }, ..code.clone() };
359        assert_eq!(none.app(&y, None).unwrap(), y[..40]);
360        assert!(TurboCode::lte(0).is_err() && code.app(&y[1..], None).is_err());
361    }
362
363    #[test]
364    fn iterations_correct_more_errors() {
365        let mut code = TurboCode::with_interleaver(Interleaver::random(512, 9)).unwrap();
366        let mut r = Rng::new(2);
367        let (mut e1, mut e8) = (0usize, 0usize);
368        for _ in 0..20 {
369            let info: Vec<u8> = (0..512).map(|_| r.bit()).collect();
370            let c = code.encode_block(&info).unwrap();
371            // Eb/N0 = 1 dB at rate ≈ 1/3
372            let s2 = bpsk_noise_variance(1.0, code.code_rate());
373            let y = awgn_llr(&c, s2, &mut r);
374            code.decoder.iterations = 1;
375            e1 += code.decode_block(&y).unwrap().iter().zip(&info).filter(|(a, b)| a != b).count();
376            code.decoder.iterations = 8;
377            e8 += code.decode_block(&y).unwrap().iter().zip(&info).filter(|(a, b)| a != b).count();
378        }
379        assert!(e8 * 10 < e1.max(1), "1 iteration: {e1} errors, 8 iterations: {e8}");
380    }
381}