use crate::codec::conv_encode;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PunctureRate {
R1_2,
R2_3,
R3_4,
R5_6,
R7_8,
}
impl PunctureRate {
fn matrix(self) -> (&'static [u8], &'static [u8]) {
match self {
PunctureRate::R1_2 => (&[1], &[1]),
PunctureRate::R2_3 => (&[1, 1], &[1, 0]),
PunctureRate::R3_4 => (&[1, 1, 0], &[1, 0, 1]),
PunctureRate::R5_6 => (&[1, 1, 0, 1, 0], &[1, 0, 1, 0, 1]),
PunctureRate::R7_8 => (&[1, 1, 1, 1, 0, 1, 0], &[1, 0, 0, 0, 1, 0, 1]),
}
}
fn period(self) -> usize {
self.matrix().0.len()
}
fn kept_per_period(self) -> usize {
let (g0, g1) = self.matrix();
g0.iter().chain(g1.iter()).filter(|&&x| x == 1).count()
}
}
pub const TAIL_BITS: usize = 4;
const NUM_STATES: usize = 16;
#[inline]
fn branch_bits(s: u8, b: u8) -> (u8, u8) {
let window = ((b & 1) << 4) | (s & 0x0F);
(parity(window & 0b10101), parity(window & 0b10011))
}
#[inline]
fn next_state(s: u8, b: u8) -> u8 {
(s >> 1) | ((b & 1) << 3)
}
#[inline]
fn parity(x: u8) -> u8 {
let x = x ^ (x >> 4);
let x = x ^ (x >> 2);
(x ^ (x >> 1)) & 1
}
pub fn conv_encode_punctured(info_bits: &[u8], rate: PunctureRate) -> Vec<u8> {
let mut padded = info_bits.to_vec();
padded.extend(std::iter::repeat_n(0u8, TAIL_BITS));
let coded = conv_encode(&padded); puncture(&coded, rate)
}
fn puncture(coded: &[u8], rate: PunctureRate) -> Vec<u8> {
if rate == PunctureRate::R1_2 {
return coded.to_vec();
}
let (g0, g1) = rate.matrix();
let period = rate.period();
let mut out = Vec::with_capacity(coded.len());
let n_steps = coded.len() / 2;
for t in 0..n_steps {
let col = t % period;
if g0[col] == 1 {
out.push(coded[t * 2]);
}
if g1[col] == 1 {
out.push(coded[t * 2 + 1]);
}
}
out
}
pub fn punctured_coded_len(info_bits: usize, rate: PunctureRate) -> usize {
let n_steps = info_bits + TAIL_BITS; if rate == PunctureRate::R1_2 {
return n_steps * 2;
}
let period = rate.period();
let full_periods = n_steps / period;
let rem = n_steps % period;
let (g0, g1) = rate.matrix();
let mut len = full_periods * rate.kept_per_period();
for col in 0..rem {
len += (g0[col] + g1[col]) as usize;
}
len
}
pub fn viterbi_decode_soft(coded_llrs: &[f32], info_bits: usize, rate: PunctureRate) -> Vec<u8> {
let n_steps = info_bits + TAIL_BITS;
let mut full = vec![0.0f32; n_steps * 2];
if rate == PunctureRate::R1_2 {
let n = coded_llrs.len().min(full.len());
full[..n].copy_from_slice(&coded_llrs[..n]);
} else {
let (g0, g1) = rate.matrix();
let period = rate.period();
let mut src = 0usize;
for t in 0..n_steps {
let col = t % period;
if g0[col] == 1 {
if src < coded_llrs.len() {
full[t * 2] = coded_llrs[src];
}
src += 1;
}
if g1[col] == 1 {
if src < coded_llrs.len() {
full[t * 2 + 1] = coded_llrs[src];
}
src += 1;
}
}
}
let neg_inf = f32::MIN / 2.0;
let mut pm = [neg_inf; NUM_STATES];
pm[0] = 0.0; let mut prev_state_table: Vec<[u8; NUM_STATES]> = vec![[0u8; NUM_STATES]; n_steps];
for t in 0..n_steps {
let l0 = full[t * 2];
let l1 = full[t * 2 + 1];
let mut new_pm = [neg_inf; NUM_STATES];
for (prev, &pm_prev) in pm.iter().enumerate().take(NUM_STATES) {
if pm_prev <= neg_inf {
continue;
}
for &bit in &[0u8, 1u8] {
let (c0, c1) = branch_bits(prev as u8, bit);
let corr = (1.0 - 2.0 * c0 as f32) * l0 + (1.0 - 2.0 * c1 as f32) * l1;
let ns = next_state(prev as u8, bit) as usize;
let cand = pm_prev + corr;
if cand > new_pm[ns] {
new_pm[ns] = cand;
prev_state_table[t][ns] = prev as u8;
}
}
}
pm = new_pm;
}
let mut state = 0usize;
let mut bits = vec![0u8; n_steps];
for t in (0..n_steps).rev() {
let prev = prev_state_table[t][state] as usize;
bits[t] = ((state >> 3) & 1) as u8;
state = prev;
}
bits.truncate(info_bits);
bits
}