pub const PN_SEQ_LEN: usize = 115;
pub const VECTOR_LENGTHS: [u8; 8] = [23, 23, 23, 23, 15, 15, 15, 7];
pub fn pn_sequence(u0: u16) -> [u16; PN_SEQ_LEN] {
debug_assert!(u0 < 4096, "û₀ is a 12-bit Golay info word");
let mut pr = [0u16; PN_SEQ_LEN];
pr[0] = u0.wrapping_mul(16);
for n in 1..PN_SEQ_LEN {
pr[n] = (173u32
.wrapping_mul(pr[n - 1] as u32)
.wrapping_add(13849)
& 0xFFFF) as u16;
}
pr
}
#[inline]
pub fn pn_mask_bit(pr_n: u16) -> u32 {
(pr_n >> 15) as u32
}
pub fn modulation_masks(u0: u16) -> [u32; 8] {
let pr = pn_sequence(u0);
let mut masks = [0u32; 8];
const LAYOUT: [(usize, usize, usize); 6] = [
(1, 1, 23),
(2, 24, 23),
(3, 47, 23),
(4, 70, 15),
(5, 85, 15),
(6, 100, 15),
];
for (vec_idx, start, len) in LAYOUT {
let mut m = 0u32;
for k in 0..len {
m |= pn_mask_bit(pr[start + k]) << (len - 1 - k);
}
masks[vec_idx] = m;
}
masks
}
pub fn demodulate(codewords: [u32; 8], u0: u16) -> [u32; 8] {
let masks = modulation_masks(u0);
let mut v = [0u32; 8];
for i in 0..8 {
v[i] = codewords[i] ^ masks[i];
}
v
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct AnnexHEntry {
pub bit1_vec: u8,
pub bit1_idx: u8,
pub bit0_vec: u8,
pub bit0_idx: u8,
}
include!(concat!(env!("OUT_DIR"), "/annex_h.rs"));
pub fn deinterleave(dibits: &[u8; 72]) -> [u32; 8] {
let mut c = [0u32; 8];
for (sym, d) in dibits.iter().enumerate() {
let entry = ANNEX_H[sym];
let hi = u32::from((d >> 1) & 1);
let lo = u32::from(d & 1);
c[entry.bit1_vec as usize] |= hi << entry.bit1_idx;
c[entry.bit0_vec as usize] |= lo << entry.bit0_idx;
}
c
}
pub fn interleave(codewords: &[u32; 8]) -> [u8; 72] {
let mut dibits = [0u8; 72];
for (sym, entry) in ANNEX_H.iter().enumerate() {
let hi = ((codewords[entry.bit1_vec as usize] >> entry.bit1_idx) & 1) as u8;
let lo = ((codewords[entry.bit0_vec as usize] >> entry.bit0_idx) & 1) as u8;
dibits[sym] = (hi << 1) | lo;
}
dibits
}
pub const SOFT_BITS: usize = 144;
pub fn soft_deinterleave(soft: &[i8; SOFT_BITS]) -> SoftCodeVectors {
let mut out = SoftCodeVectors::default();
for (sym, entry) in ANNEX_H.iter().enumerate() {
let hi = soft[2 * sym];
let lo = soft[2 * sym + 1];
place_soft(&mut out, entry.bit1_vec as usize, entry.bit1_idx as usize, hi);
place_soft(&mut out, entry.bit0_vec as usize, entry.bit0_idx as usize, lo);
}
out
}
fn place_soft(out: &mut SoftCodeVectors, vec_idx: usize, idx: usize, s: i8) {
let width = VECTOR_LENGTHS[vec_idx] as usize;
let msb_pos = width - 1 - idx;
match vec_idx {
0..=3 => out.golay[vec_idx][msb_pos] = s,
4..=6 => out.hamming[vec_idx - 4][msb_pos] = s,
7 => out.uncoded[msb_pos] = s,
_ => unreachable!(),
}
}
#[derive(Clone, Copy, Debug)]
pub struct SoftCodeVectors {
pub golay: [[i8; 23]; 4],
pub hamming: [[i8; 15]; 3],
pub uncoded: [i8; 7],
}
impl Default for SoftCodeVectors {
fn default() -> Self {
Self { golay: [[0i8; 23]; 4], hamming: [[0i8; 15]; 3], uncoded: [0i8; 7] }
}
}
pub fn soft_demodulate_vector<const W: usize>(soft: &mut [i8; W], mask: u32) {
for (i, s) in soft.iter_mut().enumerate() {
let mask_bit = (mask >> (W - 1 - i)) & 1;
if mask_bit == 1 {
*s = s.saturating_neg();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pn_seed_zero_is_all_zero_run_of_lcg() {
let pr = pn_sequence(0);
assert_eq!(pr[0], 0);
assert_eq!(pr[1], 13849);
assert_eq!(pr[2], ((173u32 * 13849 + 13849) & 0xFFFF) as u16);
}
#[test]
fn pn_seed_is_16_times_u0() {
for u0 in [0u16, 1, 7, 255, 4095] {
assert_eq!(pn_sequence(u0)[0], u0 * 16);
}
}
#[test]
fn pn_sequence_matches_recurrence_by_hand() {
let pr = pn_sequence(1);
assert_eq!(pr[0], 16);
assert_eq!(pr[1], 16617);
let expected_2 = ((173u32 * 16617 + 13849) & 0xFFFF) as u16;
assert_eq!(pr[2], expected_2);
}
#[test]
fn mask_bit_is_msb_of_pr_value() {
assert_eq!(pn_mask_bit(0), 0);
assert_eq!(pn_mask_bit(0x7FFF), 0);
assert_eq!(pn_mask_bit(0x8000), 1);
assert_eq!(pn_mask_bit(0xFFFF), 1);
}
#[test]
fn masks_zero_and_seven_are_zero() {
for u0 in [0u16, 1, 42, 4095] {
let m = modulation_masks(u0);
assert_eq!(m[0], 0, "m̂₀ must be zero (Eq. 86), u0={u0}");
assert_eq!(m[7], 0, "m̂₇ must be zero (Eq. 93), u0={u0}");
}
}
#[test]
fn masks_fit_within_vector_lengths() {
for u0 in [0u16, 1, 42, 4095] {
let m = modulation_masks(u0);
for i in 0..8 {
let len = VECTOR_LENGTHS[i] as u32;
let max_mask = if len == 32 { u32::MAX } else { (1u32 << len) - 1 };
assert!(
m[i] <= max_mask,
"mask {i} has bit beyond vector length {len} (u0={u0}, m=0x{:x})",
m[i]
);
}
}
}
#[test]
fn modulation_is_self_inverse() {
let u0 = 0xABC;
let masks = modulation_masks(u0);
let v: [u32; 8] = [0x123, 0x456, 0x789, 0xABC, 0x111, 0x222, 0x333, 0x44];
let c: [u32; 8] = std::array::from_fn(|i| v[i] ^ masks[i]);
let v2 = demodulate(c, u0);
assert_eq!(v2, v);
}
#[test]
fn different_u0_produce_different_masks() {
let m1 = modulation_masks(1);
let m2 = modulation_masks(2);
let any_diff = (1..=6).any(|i| m1[i] != m2[i]);
assert!(any_diff, "masks should depend on u0");
}
#[test]
fn pn_consumes_exactly_114_indices_for_masks() {
let total: u8 = [1u8, 2, 3, 4, 5, 6].iter().map(|&i| VECTOR_LENGTHS[i as usize]).sum();
assert_eq!(total, 114);
assert_eq!(PN_SEQ_LEN - 1, 114);
}
fn valid_mask(i: usize) -> u32 {
let len = VECTOR_LENGTHS[i] as u32;
if len == 32 { u32::MAX } else { (1u32 << len) - 1 }
}
#[test]
fn interleave_table_covers_every_codeword_bit_exactly_once() {
let mut seen = [[false; 23]; 8];
for entry in ANNEX_H.iter() {
for (v, i) in [(entry.bit1_vec, entry.bit1_idx), (entry.bit0_vec, entry.bit0_idx)] {
assert!((i as u8) < VECTOR_LENGTHS[v as usize]);
assert!(!seen[v as usize][i as usize], "double coverage at ({v}, {i})");
seen[v as usize][i as usize] = true;
}
}
for (v, row) in seen.iter().enumerate() {
for (i, &b) in row.iter().enumerate().take(VECTOR_LENGTHS[v] as usize) {
assert!(b, "({v}, {i}) never covered");
}
}
}
#[test]
fn interleave_deinterleave_roundtrip() {
let mut cw = [0u32; 8];
let mut state = 0xCAFEBABEu32;
for i in 0..8 {
state = state.wrapping_mul(1664525).wrapping_add(1013904223);
cw[i] = state & valid_mask(i);
}
let dibits = interleave(&cw);
for (s, d) in dibits.iter().enumerate() {
assert!(*d < 4, "symbol {s} produced {d} (not a dibit)");
}
let recovered = deinterleave(&dibits);
assert_eq!(recovered, cw);
}
#[test]
fn deinterleave_interleave_roundtrip() {
let mut dibits = [0u8; 72];
let mut state = 0xDEADBEEFu32;
for d in dibits.iter_mut() {
state = state.wrapping_mul(1103515245).wrapping_add(12345);
*d = (state >> 30) as u8; }
let cw = deinterleave(&dibits);
for (i, c) in cw.iter().enumerate() {
assert_eq!(c & !valid_mask(i), 0, "c[{i}] has bits beyond its width");
}
let re = interleave(&cw);
assert_eq!(re, dibits);
}
#[test]
fn single_bit_propagates_to_one_dibit_position() {
for v in 0..8 {
for idx in 0..VECTOR_LENGTHS[v] {
let mut cw = [0u32; 8];
cw[v] = 1u32 << idx;
let dibits = interleave(&cw);
let ones: u32 = dibits.iter().map(|d| u32::from(*d).count_ones()).sum();
assert_eq!(
ones, 1,
"single bit at (v={v}, idx={idx}) produced {ones} dibit-bits"
);
assert_eq!(deinterleave(&dibits), cw);
}
}
}
}