#[inline(always)]
fn parity(x: u8) -> u8 {
let x = x ^ (x >> 4);
let x = x ^ (x >> 2);
(x ^ (x >> 1)) & 1
}
pub fn conv_encode(bits: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(bits.len() * 2);
let mut sr: u8 = 0;
for &b in bits {
let window = ((b & 1) << 4) | (sr & 0x0F);
out.push(parity(window & 0b10101)); out.push(parity(window & 0b10011)); sr = (sr >> 1) | ((b & 1) << 3);
}
out
}
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)
}
pub const DQPSK_EXP: [(f32, f32); 4] = [
( 1.0, 0.0), ( 0.0, -1.0), ( 0.0, 1.0), (-1.0, 0.0), ];
pub fn viterbi_decode(soft: &[f32]) -> Vec<u8> {
let n_syms = soft.len() / 2;
if n_syms == 0 {
return Vec::new();
}
let inf = f32::MAX / 2.0;
let mut pm = [inf; NUM_STATES];
pm[0] = 0.0;
let mut prev_state_table: Vec<[u8; NUM_STATES]> = vec![[0u8; NUM_STATES]; n_syms];
for t in 0..n_syms {
let s0 = soft[t * 2];
let s1 = soft[t * 2 + 1];
let mut new_pm = [inf; NUM_STATES];
for prev in 0..NUM_STATES {
if pm[prev] >= inf { continue; }
for &bit in &[0u8, 1u8] {
let (c0, c1) = branch_bits(prev as u8, bit);
let dibit = (c0 & 1) * 2 + (c1 & 1);
let (exp0, exp1) = DQPSK_EXP[dibit as usize];
let bm = (s0 - exp0) * (s0 - exp0) + (s1 - exp1) * (s1 - exp1);
let ns = next_state(prev as u8, bit) as usize;
let cand = pm[prev] + bm;
if cand < new_pm[ns] {
new_pm[ns] = cand;
prev_state_table[t][ns] = prev as u8;
}
}
}
pm = new_pm;
}
let mut state = pm
.iter()
.enumerate()
.min_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
.map(|(i, _)| i)
.unwrap_or(0);
let mut bits_out = vec![0u8; n_syms];
for t in (0..n_syms).rev() {
let prev = prev_state_table[t][state] as usize;
let b = (state >> 3) as u8 & 1;
bits_out[t] = b;
state = prev;
}
bits_out
}
pub fn viterbi_decode_coherent(soft: &[f32], phase_steps: &[(f32, f32); 4]) -> Vec<u8> {
let n_syms = soft.len() / 2;
if n_syms == 0 {
return Vec::new();
}
let inf = f32::MAX / 2.0;
let mut pm = [inf; NUM_STATES];
pm[0] = 0.0;
let mut hyp = [(1.0f32, 0.0f32); NUM_STATES];
let mut prev_state_table: Vec<[u8; NUM_STATES]> = vec![[0u8; NUM_STATES]; n_syms];
let mut hyp_table: Vec<[(f32, f32); NUM_STATES]> = vec![[(0.0, 0.0); NUM_STATES]; n_syms];
for t in 0..n_syms {
let s_re = soft[t * 2];
let s_im = soft[t * 2 + 1];
let mut new_pm = [inf; NUM_STATES];
let mut new_hyp = [(0.0f32, 0.0f32); NUM_STATES];
for prev in 0..NUM_STATES {
if pm[prev] >= inf { continue; }
let (h_re, h_im) = hyp[prev];
for &bit in &[0u8, 1u8] {
let (c0, c1) = branch_bits(prev as u8, bit);
let dibit = (c0 & 1) * 2 + (c1 & 1);
let (step_re, step_im) = phase_steps[dibit as usize];
let nh_re = h_re * step_re - h_im * step_im;
let nh_im = h_im * step_re + h_re * step_im;
let bm = (s_re - nh_re) * (s_re - nh_re)
+ (s_im - nh_im) * (s_im - nh_im);
let ns = next_state(prev as u8, bit) as usize;
let cand = pm[prev] + bm;
if cand < new_pm[ns] {
new_pm[ns] = cand;
new_hyp[ns] = (nh_re, nh_im);
prev_state_table[t][ns] = prev as u8;
hyp_table[t][ns] = (nh_re, nh_im);
}
}
}
pm = new_pm;
hyp = new_hyp;
}
let mut state = pm
.iter()
.enumerate()
.min_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
.map(|(i, _)| i)
.unwrap_or(0);
let mut bits_out = vec![0u8; n_syms];
for t in (0..n_syms).rev() {
let prev = prev_state_table[t][state] as usize;
let b = (state >> 3) as u8 & 1;
bits_out[t] = b;
state = prev;
}
bits_out
}
pub struct StreamingViterbi {
pm: [f32; NUM_STATES],
history: Vec<[u8; NUM_STATES]>, ptr: usize, count: usize, phase_steps: [(f32, f32); 4],
}
const TRACEBACK_DEPTH: usize = 32;
const PATHMEM: usize = 128;
impl StreamingViterbi {
pub fn new(phase_steps: &[(f32, f32); 4]) -> Self {
let inf = f32::MAX / 2.0;
let mut pm = [inf; NUM_STATES];
pm[0] = 0.0;
Self {
pm,
history: vec![[0u8; NUM_STATES]; PATHMEM],
ptr: 0,
count: 0,
phase_steps: *phase_steps,
}
}
pub fn feed_symbol(&mut self, s_re: f32, s_im: f32) -> Option<u8> {
let inf = f32::MAX / 2.0;
let mut new_pm = [inf; NUM_STATES];
for prev in 0..NUM_STATES {
if self.pm[prev] >= inf { continue; }
for &bit in &[0u8, 1u8] {
let (c0, c1) = branch_bits(prev as u8, bit);
let dibit = (c0 & 1) * 2 + (c1 & 1);
let (exp_re, exp_im) = self.phase_steps[dibit as usize];
let bm = (s_re - exp_re) * (s_re - exp_re)
+ (s_im - exp_im) * (s_im - exp_im);
let ns = next_state(prev as u8, bit) as usize;
let cand = self.pm[prev] + bm;
if cand < new_pm[ns] {
new_pm[ns] = cand;
self.history[self.ptr][ns] = prev as u8;
}
}
}
self.pm = new_pm;
if self.count % 256 == 255 {
let min_pm = self.pm.iter().copied()
.filter(|&v| v < inf)
.fold(inf, f32::min);
if min_pm > 0.0 {
for p in &mut self.pm {
if *p < inf { *p -= min_pm; }
}
}
}
self.ptr = (self.ptr + 1) % PATHMEM;
self.count += 1;
if self.count <= TRACEBACK_DEPTH {
return None;
}
let mut state = self.pm
.iter()
.enumerate()
.min_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
.map(|(i, _)| i)
.unwrap_or(0);
let mut p = (self.ptr + PATHMEM - 1) % PATHMEM; for _ in 0..TRACEBACK_DEPTH {
state = self.history[p][state] as usize;
p = (p + PATHMEM - 1) % PATHMEM;
}
Some(((state >> 3) & 1) as u8)
}
pub fn flush(&mut self) -> Vec<u8> {
let mut out = Vec::new();
for _ in 0..TRACEBACK_DEPTH {
if let Some(b) = self.feed_symbol(0.0, 0.0) {
out.push(b);
}
}
out
}
}
pub fn viterbi_decode_hard(bits: &[u8]) -> Vec<u8> {
let n_syms = bits.len() / 2;
let mut soft = Vec::with_capacity(n_syms * 2);
for i in 0..n_syms {
let c0 = bits[i * 2] & 1;
let c1 = bits[i * 2 + 1] & 1;
let dibit = c0 * 2 + c1;
let (re, im) = DQPSK_EXP[dibit as usize];
soft.push(re);
soft.push(im);
}
viterbi_decode(&soft)
}