use crate::params::CodeParams;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Branch {
pub from: usize,
pub next: usize,
pub input: u8,
pub output: u32,
}
pub struct Trellis<const S: usize> {
#[cfg(test)]
pub(crate) forward: [[Branch; 2]; S],
pub(crate) preds: [[Branch; 2]; S],
}
pub(crate) fn next_state(state: usize, input: u8, k: u8) -> usize {
let m = (k - 1) as u32;
let g = ((input as usize) << m) | state;
(g >> 1) & ((1usize << m) - 1)
}
pub(crate) fn output_symbol(state: usize, input: u8, p: &CodeParams) -> u32 {
let m = (p.k - 1) as u32;
let g = ((input as u32) << m) | state as u32;
let mut sym = 0u32;
for (i, (&gen, &inv)) in p.generators.iter().zip(&p.invert_outputs).enumerate() {
let mut bit = (g & gen).count_ones() & 1;
if inv {
bit ^= 1;
}
sym |= bit << (p.generators.len() - 1 - i); }
sym
}
impl<const S: usize> Trellis<S> {
pub fn from_params(p: &CodeParams) -> Result<Self, crate::params::ParamError> {
if S != 1usize << (p.k - 1) {
return Err(crate::params::ParamError::StateCountMismatch { states: S, k: p.k });
}
let blank = Branch {
from: 0,
next: 0,
input: 0,
output: 0,
};
let mut preds = [[blank; 2]; S];
let mut pred_count = [0usize; S];
for state in 0..S {
for input in 0u8..2 {
let next = next_state(state, input, p.k);
let output = output_symbol(state, input, p);
let b = Branch {
from: state,
next,
input,
output,
};
let slot = pred_count[next];
match preds[next].get_mut(slot) {
Some(cell) => *cell = b,
None => {
return Err(crate::params::ParamError::RateUnsupported {
n: p.generators.len(),
})
}
}
pred_count[next] += 1;
}
}
#[cfg(test)]
let forward = {
let mut forward = [[blank; 2]; S];
for (state, fwd) in forward.iter_mut().enumerate() {
for (input, cell) in fwd.iter_mut().enumerate() {
let input = input as u8;
*cell = Branch {
from: state,
next: next_state(state, input, p.k),
input,
output: output_symbol(state, input, p),
};
}
}
forward
};
Ok(Self {
#[cfg(test)]
forward,
preds,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::params::CodeParams;
#[test]
fn state0_input1_matches_ccsds() {
let p = CodeParams::ccsds_r1_2();
assert_eq!(super::output_symbol(0, 1, &p), 0b10);
assert_eq!(super::next_state(0, 1, p.k), 32);
}
#[test]
fn state0_input0_matches_ccsds() {
let p = CodeParams::ccsds_r1_2();
assert_eq!(super::output_symbol(0, 0, &p), 0b01);
assert_eq!(super::next_state(0, 0, p.k), 0);
}
#[test]
fn predecessors_invert_forward() {
let t = Trellis::<64>::from_params(&CodeParams::ccsds_r1_2()).unwrap();
let preds: Vec<usize> = t.preds[0].iter().map(|b| b.from).collect();
assert!(preds.contains(&0) && preds.contains(&1));
}
#[test]
fn from_params_rejects_wrong_state_count() {
assert!(matches!(
Trellis::<32>::from_params(&CodeParams::ccsds_r1_2()),
Err(crate::params::ParamError::StateCountMismatch { states: 32, k: 7 })
));
}
}