use crate::metric::BranchMetric;
use crate::trellis::Trellis;
pub const SENTINEL: u32 = u32::MAX / 2;
pub fn acs_stage<const S: usize, M: BranchMetric>(
cur: &[u32; S],
next: &mut [u32; S],
survivors: &mut [u64],
trellis: &Trellis<S>,
received: &[M::Sample],
) -> u32 {
let mut min = u32::MAX;
for w in survivors.iter_mut() {
*w = 0;
}
for state in 0..S {
let [a, b] = trellis.preds[state];
let ca = cur[a.from].saturating_add(M::branch_distance(a.output, received));
let cb = cur[b.from].saturating_add(M::branch_distance(b.output, received));
let (winner_metric, winner_slot) = if ca < cb || (ca == cb && a.from <= b.from) {
(ca, 0u64)
} else {
(cb, 1u64)
};
next[state] = winner_metric;
survivors[state / 64] |= winner_slot << (state % 64); if winner_metric < min {
min = winner_metric;
}
}
for m in next.iter_mut() {
*m -= min;
} debug_assert!(
next.iter()
.filter(|&&m| m < SENTINEL / 2)
.all(|&m| m <= M::MAX_SPREAD),
"reachable-state metric spread exceeded the metric's declared MAX_SPREAD (§4.3)"
);
min
}
#[cfg(test)]
mod tests {
use super::*;
use crate::metric::HardHamming;
use crate::params::CodeParams;
use crate::trellis::Trellis;
#[test]
fn stage_selects_min_and_renormalizes_to_zero() {
let t = Trellis::<64>::from_params(&CodeParams::ccsds_r1_2()).unwrap();
let mut cur = [SENTINEL; 64];
cur[0] = 0;
let mut next = [0u32; 64];
let mut surv = [0u64; 1]; let exp = t.forward[0][0].output;
let recv = [((exp >> 1) & 1) as u8, (exp & 1) as u8];
let min = acs_stage::<64, HardHamming>(&cur, &mut next, &mut surv, &t, &recv);
assert_eq!(min, 0); assert_eq!(*next.iter().min().unwrap(), 0); }
}