use viterbi::{CcsdsSoftDecoder, CodeParams, CodedBlock, ViterbiEncoder};
fn hard_bit_to_llr(bit: u8) -> i8 {
if bit == 0 {
64
} else {
-64
}
}
fn coded_to_llrs(coded: &CodedBlock) -> Vec<i8> {
(0..coded.nbits)
.map(|i| hard_bit_to_llr((coded.bytes[i / 8] >> (7 - (i % 8))) & 1))
.collect()
}
const SPACED_ERROR_STRIDE: usize = 40;
#[test]
fn soft_round_trip_clean_channel() {
let enc = ViterbiEncoder::new(CodeParams::ccsds_r1_2()).unwrap();
let data = b"soft!";
let coded = enc.encode(data).unwrap();
let mut llrs = Vec::new();
for i in 0..coded.nbits {
let bit = (coded.bytes[i / 8] >> (7 - (i % 8))) & 1;
llrs.push(hard_bit_to_llr(bit));
}
let mut dec = CcsdsSoftDecoder::new(CodeParams::ccsds_r1_2(), 1_000).unwrap();
let out = dec.decode(&llrs, data.len() * 8).unwrap();
assert_eq!(&out.bytes[..data.len()], data);
}
#[test]
fn soft_decode_is_deterministic() {
let enc = ViterbiEncoder::new(CodeParams::ccsds_r1_2()).unwrap();
let data = b"determinism";
let coded = enc.encode(data).unwrap();
let llrs = coded_to_llrs(&coded);
let mut dec = CcsdsSoftDecoder::new(CodeParams::ccsds_r1_2(), 1_000).unwrap();
let a = dec.decode(&llrs, data.len() * 8).unwrap();
let b = dec.decode(&llrs, data.len() * 8).unwrap();
assert_eq!(a, b);
}
#[test]
fn soft_long_stream_round_trip_exercises_renorm() {
let enc = ViterbiEncoder::new(CodeParams::ccsds_r1_2()).unwrap();
let raw: Vec<u8> = (0..1_500u32).map(|i| (i as u8).wrapping_mul(37)).collect();
let coded = enc.encode(&raw).unwrap();
let llrs = coded_to_llrs(&coded);
let mut dec = CcsdsSoftDecoder::new(CodeParams::ccsds_r1_2(), 20_000).unwrap();
let out = dec.decode(&llrs, raw.len() * 8).unwrap();
assert_eq!(out.bytes, raw);
}
proptest::proptest! {
#[test]
fn soft_recovers_under_spaced_errors(
raw in proptest::collection::vec(proptest::num::u8::ANY, 1..128)
) {
let enc = ViterbiEncoder::new(CodeParams::ccsds_r1_2()).unwrap();
let coded = enc.encode(&raw).unwrap();
let mut llrs = coded_to_llrs(&coded);
let mut i = SPACED_ERROR_STRIDE / 2;
while i < coded.nbits {
llrs[i] = -llrs[i]; i += SPACED_ERROR_STRIDE;
}
let mut dec = CcsdsSoftDecoder::new(CodeParams::ccsds_r1_2(), 1_024).unwrap();
let out = dec.decode(&llrs, raw.len() * 8).unwrap();
proptest::prop_assert_eq!(out.bytes, raw);
}
}