use crate::block::BlockCode;
use crate::block::SoftDecode;
use crate::block::container_traits;
use crate::block::expect_len;
use crate::conv::Trellis;
use crate::interleaving::Interleaver;
use crate::turbo::TurboDecoder;
use comnum_core::Error;
use comnum_core::Result;
use comnum_core::ensure;
use comnum_core::signal::hard_decisions;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum TurboRate {
R1_2,
R1_3,
R1_4,
R1_6,
}
impl TurboRate {
pub fn bits_per_info_bit(self) -> usize {
match self {
TurboRate::R1_2 => 2,
TurboRate::R1_3 => 3,
TurboRate::R1_4 => 4,
TurboRate::R1_6 => 6,
}
}
pub fn name(self) -> &'static str {
match self {
TurboRate::R1_2 => "1/2",
TurboRate::R1_3 => "1/3",
TurboRate::R1_4 => "1/4",
TurboRate::R1_6 => "1/6",
}
}
fn forwards(self) -> (&'static [u32], &'static [u32]) {
match self {
TurboRate::R1_2 | TurboRate::R1_3 => (&[0o33], &[0o33]),
TurboRate::R1_4 => (&[0o25, 0o37], &[0o33]),
TurboRate::R1_6 => (&[0o33, 0o25, 0o37], &[0o33, 0o37]),
}
}
}
impl std::str::FromStr for TurboRate {
type Err = Error;
fn from_str(s: &str) -> Result<TurboRate> {
match s.trim() {
"1/2" => Ok(TurboRate::R1_2),
"1/3" => Ok(TurboRate::R1_3),
"1/4" => Ok(TurboRate::R1_4),
"1/6" => Ok(TurboRate::R1_6),
_ => Err(Error::Config(format!("the CCSDS turbo rate must be 1/2, 1/3, 1/4 or 1/6, got {s:?}"))),
}
}
}
pub const TURBO_LENGTHS: [usize; 4] = [1784, 3568, 7136, 8920];
pub fn turbo_permutation(k: usize) -> Result<Interleaver> {
const PRIMES: [usize; 8] = [31, 37, 43, 47, 53, 59, 61, 67];
ensure(TURBO_LENGTHS.contains(&k), || format!("the CCSDS turbo codes have k = 1784, 3568, 7136 or 8920, got {k}"))?;
let (k1, k2) = (8, k / 8);
let perm = (1..=k)
.map(|s| {
let m = (s - 1) % 2;
let i = (s - 1) / (2 * k2);
let j = (s - 1) / 2 - i * k2;
let t = (19 * i + 1) % (k1 / 2);
let q = t % 8 + 1;
let c = (PRIMES[q - 1] * j + 21 * m) % k2;
2 * (t + c * k1 / 2 + 1) - m - 1
})
.collect();
Interleaver::from_permutation(perm)
}
#[derive(Clone, Debug, PartialEq)]
pub struct CcsdsTurbo {
rate: TurboRate,
a: Trellis,
b: Trellis,
interleaver: Interleaver,
pub decoder: TurboDecoder,
}
const TURBO_TAIL: usize = 4;
impl CcsdsTurbo {
pub fn new(k: usize, rate: TurboRate) -> Result<CcsdsTurbo> {
let interleaver = turbo_permutation(k)?;
let (fa, fb) = rate.forwards();
let a = Trellis::recursive_parities(5, 0o23, fa)?;
let b = Trellis::recursive_parities(5, 0o23, fb)?;
Ok(CcsdsTurbo { rate, a, b, interleaver, decoder: TurboDecoder::default() })
}
pub fn rate(&self) -> TurboRate {
self.rate
}
pub fn interleaver(&self) -> &Interleaver {
&self.interleaver
}
fn slots(&self, t: usize) -> &'static [(u8, u8)] {
match self.rate {
TurboRate::R1_2 if t % 2 == 0 => &[(0, 0), (0, 1)],
TurboRate::R1_2 => &[(0, 0), (1, 1)],
TurboRate::R1_3 => &[(0, 0), (0, 1), (1, 1)],
TurboRate::R1_4 => &[(0, 0), (0, 1), (0, 2), (1, 1)],
TurboRate::R1_6 => &[(0, 0), (0, 1), (0, 2), (0, 3), (1, 1), (1, 2)],
}
}
}
impl BlockCode for CcsdsTurbo {
fn k(&self) -> usize {
self.interleaver.len()
}
fn n(&self) -> usize {
(self.k() + TURBO_TAIL) * self.rate.bits_per_info_bit()
}
fn encode_block(&self, info: &[u8]) -> Result<Vec<u8>> {
expect_len(info.len(), self.k(), "bits")?;
let ca = self.a.encode(info, true);
let cb = self.b.encode(&self.interleaver.interleave(info), true);
let (na, nb) = (self.a.n_out(), self.b.n_out());
let mut out = Vec::with_capacity(self.n());
for t in 0..self.k() + TURBO_TAIL {
for &(e, o) in self.slots(t) {
out.push(if e == 0 { ca[t * na + usize::from(o)] } else { cb[t * nb + usize::from(o)] });
}
}
Ok(out)
}
fn decode_block(&self, llr: &[f64]) -> Result<Vec<u8>> {
Ok(hard_decisions(&self.app(llr, None)?))
}
}
impl SoftDecode for CcsdsTurbo {
fn app_into(&self, llr: &[f64], apriori: Option<&[f64]>, out: &mut [f64]) -> Result<usize> {
expect_len(llr.len(), self.n(), "LLRs")?;
let k = self.k();
expect_len(out.len(), k, "a posteriori LLRs")?;
if let Some(a) = apriori {
expect_len(a.len(), k, "a priori LLRs")?;
}
let steps = k + TURBO_TAIL;
let (na, nb) = (self.a.n_out(), self.b.n_out());
let (mut la, mut lb) = (vec![0.0; steps * na], vec![0.0; steps * nb]);
let mut pos = 0;
for t in 0..steps {
for &(e, o) in self.slots(t) {
if e == 0 {
la[t * na + usize::from(o)] = llr[pos];
} else {
lb[t * nb + usize::from(o)] = llr[pos];
}
pos += 1;
}
}
if let Some(a) = apriori {
for (t, v) in a.iter().enumerate() {
la[t * na] += v;
}
}
let sys: Vec<f64> = (0..k).map(|t| la[t * na]).collect();
let sys2 = self.interleaver.interleave(&sys);
for (t, &v) in sys2.iter().enumerate() {
lb[t * nb] = v;
}
if self.decoder.iterations == 0 {
out.copy_from_slice(&sys);
return Ok(0);
}
crate::turbo::iterate(
&self.a,
&self.b,
&la,
&lb,
&sys,
&sys2,
self.interleaver.perm(),
steps,
self.decoder,
out,
)
}
}
container_traits!(CcsdsTurbo);