use crate::codec::conv_encode;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ConvCode {
#[default]
K5,
DvbK7,
}
impl ConvCode {
#[inline]
pub const fn constraint_length(self) -> usize {
match self {
ConvCode::K5 => 5,
ConvCode::DvbK7 => 7,
}
}
#[inline]
pub const fn reg_bits(self) -> usize {
self.constraint_length() - 1
}
#[inline]
pub const fn num_states(self) -> usize {
1usize << self.reg_bits()
}
#[inline]
pub const fn tail_bits(self) -> usize {
self.reg_bits()
}
#[inline]
const fn generators(self) -> (u16, u16) {
match self {
ConvCode::K5 => (0b10101, 0b10011),
ConvCode::DvbK7 => (0b1111001, 0b1011011),
}
}
}
#[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;
#[inline]
fn branch_bits(code: ConvCode, s: u16, b: u8) -> (u8, u8) {
let (g0, g1) = code.generators();
let window = (((b & 1) as u16) << code.reg_bits()) | (s & reg_mask(code));
(parity(window & g0), parity(window & g1))
}
#[inline]
fn next_state(code: ConvCode, s: u16, b: u8) -> u16 {
(s >> 1) | (((b & 1) as u16) << (code.reg_bits() - 1))
}
#[inline]
fn reg_mask(code: ConvCode) -> u16 {
(1u16 << code.reg_bits()) - 1
}
#[inline]
fn parity(x: u16) -> u8 {
(x.count_ones() & 1) as u8
}
#[derive(Clone, Copy)]
struct Branch {
sym: u8,
next: u16,
}
fn branch_table(code: ConvCode) -> Vec<Branch> {
let num_states = code.num_states();
let mut out = Vec::with_capacity(num_states * 2);
for s in 0..num_states as u16 {
for b in 0..2u8 {
let (c0, c1) = branch_bits(code, s, b);
out.push(Branch {
sym: (c0 << 1) | c1,
next: next_state(code, s, b),
});
}
}
out
}
fn conv_encode_code(code: ConvCode, bits: &[u8]) -> Vec<u8> {
if code == ConvCode::K5 {
return conv_encode(bits);
}
let mut out = Vec::with_capacity(bits.len() * 2);
let mut state: u16 = 0;
for &b in bits {
let (c0, c1) = branch_bits(code, state, b);
out.push(c0);
out.push(c1);
state = next_state(code, state, b);
}
out
}
pub fn conv_encode_punctured(info_bits: &[u8], rate: PunctureRate) -> Vec<u8> {
conv_encode_punctured_with(ConvCode::K5, info_bits, rate)
}
pub fn conv_encode_punctured_with(code: ConvCode, info_bits: &[u8], rate: PunctureRate) -> Vec<u8> {
let mut padded = info_bits.to_vec();
padded.extend(std::iter::repeat_n(0u8, code.tail_bits()));
let coded = conv_encode_code(code, &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 {
punctured_coded_len_with(ConvCode::K5, info_bits, rate)
}
pub fn punctured_coded_len_with(code: ConvCode, info_bits: usize, rate: PunctureRate) -> usize {
let n_steps = info_bits + code.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> {
viterbi_decode_soft_with(ConvCode::K5, coded_llrs, info_bits, rate)
}
pub fn viterbi_decode_soft_with(
code: ConvCode,
coded_llrs: &[f32],
info_bits: usize,
rate: PunctureRate,
) -> Vec<u8> {
let n_steps = info_bits + code.tail_bits();
let num_states = code.num_states();
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 = vec![neg_inf; num_states];
pm[0] = 0.0; let table = branch_table(code);
let mut prev_state = vec![0u16; n_steps * num_states];
let top_bit = code.reg_bits() - 1;
let mut new_pm = vec![neg_inf; num_states];
for t in 0..n_steps {
let l0 = full[t * 2];
let l1 = full[t * 2 + 1];
let corr = [l0 + l1, l0 - l1, -l0 + l1, -l0 - l1];
new_pm.iter_mut().for_each(|m| *m = neg_inf);
let row = &mut prev_state[t * num_states..(t + 1) * num_states];
for (prev, &pm_prev) in pm.iter().enumerate() {
if pm_prev <= neg_inf {
continue;
}
for br in &table[prev * 2..prev * 2 + 2] {
let ns = br.next as usize;
let cand = pm_prev + corr[br.sym as usize];
if cand > new_pm[ns] {
new_pm[ns] = cand;
row[ns] = prev as u16;
}
}
}
std::mem::swap(&mut pm, &mut new_pm);
}
let mut state = 0usize;
let mut bits = vec![0u8; n_steps];
for t in (0..n_steps).rev() {
let prev = prev_state[t * num_states + state] as usize;
bits[t] = ((state >> top_bit) & 1) as u8;
state = prev;
}
bits.truncate(info_bits);
bits
}