#[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, &pm_prev) in pm.iter().enumerate().take(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)
}
use crate::Block;
use crate::codec::varicode::VaricodeDecoder;
use crate::demodulate::psk31::{Bpsk31Decider, Bpsk31Demod, Qpsk31Demod};
use num_complex::Complex32 as C32;
pub enum Psk31Stream {
Bpsk {
demod: Bpsk31Demod,
decider: Bpsk31Decider,
vdec: VaricodeDecoder,
fed_up_to: usize,
},
Qpsk {
demod: Qpsk31Demod,
viterbi: StreamingViterbi,
vdec: VaricodeDecoder,
fed_up_to: usize,
},
}
impl Psk31Stream {
pub fn new_bpsk(fs: f32, carrier_hz: f32, gain: f32) -> Self {
Psk31Stream::Bpsk {
demod: Bpsk31Demod::new(fs, carrier_hz, gain),
decider: Bpsk31Decider::new(),
vdec: VaricodeDecoder::new(),
fed_up_to: 0,
}
}
pub fn new_qpsk(fs: f32, carrier_hz: f32, gain: f32) -> Self {
Psk31Stream::Qpsk {
demod: Qpsk31Demod::new(fs, carrier_hz, gain),
viterbi: StreamingViterbi::new(&DQPSK_EXP),
vdec: VaricodeDecoder::new(),
fed_up_to: 0,
}
}
pub fn fed_up_to(&self) -> usize {
match self {
Psk31Stream::Bpsk { fed_up_to, .. } => *fed_up_to,
Psk31Stream::Qpsk { fed_up_to, .. } => *fed_up_to,
}
}
pub fn set_fed_up_to(&mut self, v: usize) {
match self {
Psk31Stream::Bpsk { fed_up_to, .. } => *fed_up_to = v,
Psk31Stream::Qpsk { fed_up_to, .. } => *fed_up_to = v,
}
}
pub fn feed(&mut self, iq: &[C32]) -> String {
if iq.is_empty() {
return String::new();
}
match self {
Psk31Stream::Bpsk {
demod,
decider,
vdec,
..
} => {
let max_syms = iq.len() / 32 + 4;
let mut soft = vec![0.0_f32; max_syms];
let wr = demod.process(iq, &mut soft);
soft.truncate(wr.out_written);
let mut bits = vec![0_u8; soft.len()];
let dr = decider.process(&soft, &mut bits);
bits.truncate(dr.out_written);
let mut text = String::new();
for &b in &bits {
vdec.push_bit(b);
while let Some(ch) = vdec.pop_char() {
if (0x20..0x7f).contains(&ch) {
text.push(ch as char);
}
}
}
text
}
Psk31Stream::Qpsk {
demod,
viterbi,
vdec,
..
} => {
let max_soft = iq.len() / 32 + 8;
let mut soft = vec![0.0_f32; max_soft];
let wr = demod.process(iq, &mut soft);
soft.truncate(wr.out_written);
let mut text = String::new();
let n_syms = soft.len() / 2;
for i in 0..n_syms {
let d_re = soft[i * 2];
let d_im = soft[i * 2 + 1];
if d_re * d_re + d_im * d_im < 0.01 {
continue;
}
if let Some(b) = viterbi.feed_symbol(d_re, d_im) {
vdec.push_bit(b);
while let Some(ch) = vdec.pop_char() {
if (0x20..0x7f).contains(&ch) {
text.push(ch as char);
}
}
}
}
text
}
}
}
pub fn flush(&mut self) -> String {
match self {
Psk31Stream::Bpsk { vdec, .. } => {
vdec.push_bit(0);
vdec.push_bit(0);
let mut text = String::new();
while let Some(ch) = vdec.pop_char() {
if (0x20..0x7f).contains(&ch) {
text.push(ch as char);
}
}
text
}
Psk31Stream::Qpsk { viterbi, vdec, .. } => {
let mut text = String::new();
for b in viterbi.flush() {
vdec.push_bit(b);
while let Some(ch) = vdec.pop_char() {
if (0x20..0x7f).contains(&ch) {
text.push(ch as char);
}
}
}
vdec.push_bit(0);
vdec.push_bit(0);
while let Some(ch) = vdec.pop_char() {
if (0x20..0x7f).contains(&ch) {
text.push(ch as char);
}
}
text
}
}
}
}