Skip to main content

comnum_fec/ccsds/
turbo.rs

1//! The turbo codes of CCSDS 131.0-B-5 Sec. 6, [`CcsdsTurbo`], and their permutation.
2
3use crate::block::BlockCode;
4use crate::block::SoftDecode;
5use crate::block::container_traits;
6use crate::block::expect_len;
7use crate::conv::Trellis;
8use crate::interleaving::Interleaver;
9use crate::turbo::TurboDecoder;
10use comnum_core::Error;
11use comnum_core::Result;
12use comnum_core::ensure;
13use comnum_core::signal::hard_decisions;
14
15/// The rates of the turbo codes (Sec. 6.3).
16#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
17pub enum TurboRate {
18    /// 1/2.
19    R1_2,
20    /// 1/3.
21    R1_3,
22    /// 1/4.
23    R1_4,
24    /// 1/6.
25    R1_6,
26}
27
28impl TurboRate {
29    /// The coded bits per information bit, $1/r$ (the inverse of the nominal rate).
30    pub fn bits_per_info_bit(self) -> usize {
31        match self {
32            TurboRate::R1_2 => 2,
33            TurboRate::R1_3 => 3,
34            TurboRate::R1_4 => 4,
35            TurboRate::R1_6 => 6,
36        }
37    }
38
39    /// The name of the rate, `1/2` to `1/6`.
40    pub fn name(self) -> &'static str {
41        match self {
42            TurboRate::R1_2 => "1/2",
43            TurboRate::R1_3 => "1/3",
44            TurboRate::R1_4 => "1/4",
45            TurboRate::R1_6 => "1/6",
46        }
47    }
48
49    /// The forward vectors of encoders a and b (octal, the current register input the
50    /// most significant): $G_{1} = 33$, $G_{2} = 25$, $G_{3} = 37$ (Sec. 6.3 h)).
51    fn forwards(self) -> (&'static [u32], &'static [u32]) {
52        match self {
53            TurboRate::R1_2 | TurboRate::R1_3 => (&[0o33], &[0o33]),
54            TurboRate::R1_4 => (&[0o25, 0o37], &[0o33]),
55            TurboRate::R1_6 => (&[0o33, 0o25, 0o37], &[0o33, 0o37]),
56        }
57    }
58}
59
60impl std::str::FromStr for TurboRate {
61    type Err = Error;
62
63    /// `1/2`, `1/3`, `1/4` or `1/6`.
64    fn from_str(s: &str) -> Result<TurboRate> {
65        match s.trim() {
66            "1/2" => Ok(TurboRate::R1_2),
67            "1/3" => Ok(TurboRate::R1_3),
68            "1/4" => Ok(TurboRate::R1_4),
69            "1/6" => Ok(TurboRate::R1_6),
70            _ => Err(Error::Config(format!("the CCSDS turbo rate must be 1/2, 1/3, 1/4 or 1/6, got {s:?}"))),
71        }
72    }
73}
74
75/// The information block lengths of the turbo codes and their $k_{2}$ (Tables 6-1 and
76/// 6-3; $k_{1} = 8$).
77pub const TURBO_LENGTHS: [usize; 4] = [1784, 3568, 7136, 8920];
78
79/// The permutation of the turbo codes of `k` bits (Sec. 6.3 g)): the bit $s$ read by the
80/// second encoder is the bit $\pi(s)$ of the block, $k = k_{1} k_{2}$, $k_{1} = 8$,
81/// $m = (s - 1) \bmod 2$, $i = \lfloor (s - 1)/(2 k_{2}) \rfloor$, $j = \lfloor (s - 1)/2
82/// \rfloor - i k_{2}$, $t = (19 i + 1) \bmod (k_{1}/2)$, $q = t \bmod 8 + 1$,
83/// $c = (p_{q} j + 21 m) \bmod k_{2}$, $\pi(s) = 2 (t + c k_{1}/2 + 1) - m$, $p = (31, 37, 43,
84/// 47, 53, 59, 61, 67)$. As an [`Interleaver`], output $s - 1$ takes input $\pi(s) - 1$.
85///
86/// # Errors
87/// [`Error::Config`] for another $k$.
88pub fn turbo_permutation(k: usize) -> Result<Interleaver> {
89    const PRIMES: [usize; 8] = [31, 37, 43, 47, 53, 59, 61, 67];
90    ensure(TURBO_LENGTHS.contains(&k), || format!("the CCSDS turbo codes have k = 1784, 3568, 7136 or 8920, got {k}"))?;
91    let (k1, k2) = (8, k / 8);
92    let perm = (1..=k)
93        .map(|s| {
94            let m = (s - 1) % 2;
95            let i = (s - 1) / (2 * k2);
96            let j = (s - 1) / 2 - i * k2;
97            let t = (19 * i + 1) % (k1 / 2);
98            let q = t % 8 + 1;
99            let c = (PRIMES[q - 1] * j + 21 * m) % k2;
100            2 * (t + c * k1 / 2 + 1) - m - 1
101        })
102        .collect();
103    Interleaver::from_permutation(perm)
104}
105
106/// A turbo code of CCSDS (see the module).
107#[derive(Clone, Debug, PartialEq)]
108pub struct CcsdsTurbo {
109    rate: TurboRate,
110    /// The trellises of encoders a and b (the systematic output first).
111    a: Trellis,
112    b: Trellis,
113    interleaver: Interleaver,
114    /// The decoder settings.
115    pub decoder: TurboDecoder,
116}
117
118/// The termination steps of a constituent encoder.
119const TURBO_TAIL: usize = 4;
120
121impl CcsdsTurbo {
122    /// The code of `k` information bits (1784, 3568, 7136 or 8920) and rate `rate`.
123    ///
124    /// # Errors
125    /// [`Error::Config`] for another $k$.
126    pub fn new(k: usize, rate: TurboRate) -> Result<CcsdsTurbo> {
127        let interleaver = turbo_permutation(k)?;
128        let (fa, fb) = rate.forwards();
129        let a = Trellis::recursive_parities(5, 0o23, fa)?;
130        let b = Trellis::recursive_parities(5, 0o23, fb)?;
131        Ok(CcsdsTurbo { rate, a, b, interleaver, decoder: TurboDecoder::default() })
132    }
133
134    /// The rate.
135    pub fn rate(&self) -> TurboRate {
136        self.rate
137    }
138
139    /// The permutation of the second encoder.
140    pub fn interleaver(&self) -> &Interleaver {
141        &self.interleaver
142    }
143
144    /// The outputs of bit time `t` (from 0) as indices into the outputs of encoders a and b:
145    /// `(encoder, output)`, encoder 0 = a, output 0 = systematic.
146    fn slots(&self, t: usize) -> &'static [(u8, u8)] {
147        match self.rate {
148            TurboRate::R1_2 if t % 2 == 0 => &[(0, 0), (0, 1)],
149            TurboRate::R1_2 => &[(0, 0), (1, 1)],
150            TurboRate::R1_3 => &[(0, 0), (0, 1), (1, 1)],
151            TurboRate::R1_4 => &[(0, 0), (0, 1), (0, 2), (1, 1)],
152            TurboRate::R1_6 => &[(0, 0), (0, 1), (0, 2), (0, 3), (1, 1), (1, 2)],
153        }
154    }
155}
156
157impl BlockCode for CcsdsTurbo {
158    fn k(&self) -> usize {
159        self.interleaver.len()
160    }
161
162    /// $(k + 4)/r$ (Table 6-2).
163    fn n(&self) -> usize {
164        (self.k() + TURBO_TAIL) * self.rate.bits_per_info_bit()
165    }
166
167    fn encode_block(&self, info: &[u8]) -> Result<Vec<u8>> {
168        expect_len(info.len(), self.k(), "bits")?;
169        let ca = self.a.encode(info, true);
170        let cb = self.b.encode(&self.interleaver.interleave(info), true);
171        let (na, nb) = (self.a.n_out(), self.b.n_out());
172        let mut out = Vec::with_capacity(self.n());
173        for t in 0..self.k() + TURBO_TAIL {
174            for &(e, o) in self.slots(t) {
175                out.push(if e == 0 { ca[t * na + usize::from(o)] } else { cb[t * nb + usize::from(o)] });
176            }
177        }
178        Ok(out)
179    }
180
181    fn decode_block(&self, llr: &[f64]) -> Result<Vec<u8>> {
182        Ok(hard_decisions(&self.app(llr, None)?))
183    }
184}
185
186impl SoftDecode for CcsdsTurbo {
187    /// The iterative decoder of [`crate::turbo`] on the two constituent codes: the
188    /// a posteriori LLRs of the second decoder after the last iteration, deinterleaved;
189    /// the a priori LLRs add to the systematic channel LLRs.
190    fn app_into(&self, llr: &[f64], apriori: Option<&[f64]>, out: &mut [f64]) -> Result<usize> {
191        expect_len(llr.len(), self.n(), "LLRs")?;
192        let k = self.k();
193        expect_len(out.len(), k, "a posteriori LLRs")?;
194        if let Some(a) = apriori {
195            expect_len(a.len(), k, "a priori LLRs")?;
196        }
197        let steps = k + TURBO_TAIL;
198        let (na, nb) = (self.a.n_out(), self.b.n_out());
199        // the channel LLRs of every output of both encoders, zero where not sent
200        let (mut la, mut lb) = (vec![0.0; steps * na], vec![0.0; steps * nb]);
201        let mut pos = 0;
202        for t in 0..steps {
203            for &(e, o) in self.slots(t) {
204                if e == 0 {
205                    la[t * na + usize::from(o)] = llr[pos];
206                } else {
207                    lb[t * nb + usize::from(o)] = llr[pos];
208                }
209                pos += 1;
210            }
211        }
212        if let Some(a) = apriori {
213            for (t, v) in a.iter().enumerate() {
214                la[t * na] += v;
215            }
216        }
217        let sys: Vec<f64> = (0..k).map(|t| la[t * na]).collect();
218        let sys2 = self.interleaver.interleave(&sys);
219        for (t, &v) in sys2.iter().enumerate() {
220            lb[t * nb] = v;
221        }
222        if self.decoder.iterations == 0 {
223            out.copy_from_slice(&sys);
224            return Ok(0);
225        }
226        crate::turbo::iterate(
227            &self.a,
228            &self.b,
229            &la,
230            &lb,
231            &sys,
232            &sys2,
233            self.interleaver.perm(),
234            steps,
235            self.decoder,
236            out,
237        )
238    }
239}
240
241container_traits!(CcsdsTurbo);