1use 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#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
17pub enum TurboRate {
18 R1_2,
20 R1_3,
22 R1_4,
24 R1_6,
26}
27
28impl TurboRate {
29 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 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 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 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
75pub const TURBO_LENGTHS: [usize; 4] = [1784, 3568, 7136, 8920];
78
79pub 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#[derive(Clone, Debug, PartialEq)]
108pub struct CcsdsTurbo {
109 rate: TurboRate,
110 a: Trellis,
112 b: Trellis,
113 interleaver: Interleaver,
114 pub decoder: TurboDecoder,
116}
117
118const TURBO_TAIL: usize = 4;
120
121impl CcsdsTurbo {
122 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 pub fn rate(&self) -> TurboRate {
136 self.rate
137 }
138
139 pub fn interleaver(&self) -> &Interleaver {
141 &self.interleaver
142 }
143
144 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 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 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 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);