use labrador_ldpc::{decoder::DecodeFrom, LDPCCode};
use crate::{buffer::Buffer, error::EncodeError};
macro_rules! enc_chunk {
($d:ident, $i:ident, $t:ident, $n:literal) => {
::paste::paste! {
let mut codeword = [0; $n * 2];
codeword[0..$n].copy_from_slice(&$d[$i..($i + $n)]);
labrador_ldpc::LDPCCode::[<$t>].encode(&mut codeword);
$d.try_extend_from_slice(&codeword[$n..])
.map_err(|_| EncodeError::CatsOverflow)?;
$i += $n;
}
};
}
macro_rules! dec_chunk {
($d:ident, $p:ident, $w:ident, $t:ident, $n:literal) => {
::paste::paste! {
let code_data = &mut $d[..$n];
let code_parity = &$p.get_mut(..$n)?;
let mut input = [0; $n * 2];
input[..$n].copy_from_slice(code_data);
input[$n..].copy_from_slice(code_parity);
const CODE: LDPCCode = LDPCCode::[<$t>];
let mut out = [0; CODE.output_len()];
CODE.decode_bf(&input, &mut out, &mut $w[..CODE.decode_bf_working_len()], 16);
code_data.copy_from_slice(&out[..$n]);
$d = &mut $d[$n..];
$p = &mut $p[$n..];
}
};
}
macro_rules! dec_chunk_soft {
($d:ident, $p:ident, $w:ident, $w_u8:ident, $out:ident, $t:ident, $n:literal) => {
::paste::paste! {
let code_data = &mut $d[..$n];
let code_parity = &$p.get_mut(..$n)?;
let mut input = [T::zero(); $n * 2];
input[..$n].copy_from_slice(code_data);
input[$n..].copy_from_slice(code_parity);
const CODE: LDPCCode = LDPCCode::[<$t>];
let mut out_tmp = [0; CODE.output_len()];
CODE.decode_ms(&input, &mut out_tmp, &mut $w[..CODE.decode_ms_working_len()], &mut $w_u8[..CODE.decode_ms_working_u8_len()], 16);
$out.try_extend_from_slice(&out_tmp[..$n/8]).ok()?;
$d = &mut $d[$n..];
$p = &mut $p[$n..];
}
};
}
pub(crate) fn encode<const N: usize>(data: &mut Buffer<N>) -> Result<(), EncodeError> {
let mut i = 0;
let n = data.len();
loop {
match n - i {
512.. => {
enc_chunk!(data, i, TM8192, 512);
}
128.. => {
enc_chunk!(data, i, TM2048, 128);
}
32.. => {
enc_chunk!(data, i, TC512, 32);
}
16.. => {
enc_chunk!(data, i, TC256, 16);
}
8.. => {
enc_chunk!(data, i, TC128, 8);
}
0 => break,
_ => {
let mut codeword = [0xAA; 16];
codeword[0..(n - i)].copy_from_slice(&data[i..n]);
LDPCCode::TC128.encode(&mut codeword);
data.try_extend_from_slice(&codeword[8..])
.map_err(|_| EncodeError::CatsOverflow)?;
i = n;
}
}
}
data.try_extend_from_slice(&u16::try_from(n).unwrap().to_le_bytes())
.map_err(|_| EncodeError::CatsOverflow)?;
Ok(())
}
pub(crate) fn decode<const N: usize>(data_av: &mut Buffer<N>) -> Option<()> {
if data_av.len() < 2 {
return None;
}
let len = [data_av[data_av.len() - 2], data_av[data_av.len() - 1]];
let len = u16::from_le_bytes(len).into();
if len >= data_av.len() {
return None;
}
let (mut data, parity) = data_av.split_at_mut(len);
let mut parity = parity.get_mut(..parity.len().checked_sub(2)?)?;
let mut working = [0; LDPCCode::TM8192.decode_bf_working_len()];
loop {
match data.len() {
512.. => {
dec_chunk!(data, parity, working, TM8192, 512);
}
128.. => {
dec_chunk!(data, parity, working, TM2048, 128);
}
32.. => {
dec_chunk!(data, parity, working, TC512, 32);
}
16.. => {
dec_chunk!(data, parity, working, TC256, 16);
}
8.. => {
dec_chunk!(data, parity, working, TC128, 8);
}
0 => break,
_ => {
let mut code_data = [0xAA; 8];
code_data[..data.len()].copy_from_slice(data);
let code_parity = &parity.get_mut(..8)?;
let mut input = [0; 16];
input[..8].copy_from_slice(&code_data);
input[8..].copy_from_slice(code_parity);
let mut out = [0; LDPCCode::TC128.output_len()];
LDPCCode::TC128.decode_bf(
&input,
&mut out,
&mut working[..LDPCCode::TC128.decode_bf_working_len()],
16,
);
data.copy_from_slice(&out[..data.len()]);
data = &mut data[..0];
parity = &mut parity[..0];
}
}
}
data_av.truncate(len);
Some(())
}
pub(crate) fn decode_soft<const N: usize, const M: usize, T: DecodeFrom>(
data_av: &mut Buffer<N, T>,
out: &mut Buffer<M>,
) -> Option<()> {
if data_av.len() % 8 != 0 {
return None;
}
if data_av.len() < 16 {
return None;
}
let len: usize = len_from_soft(data_av[(data_av.len() - 16)..].try_into().unwrap()).into();
if len * 8 + 16 >= data_av.len() {
return None;
}
let data_len = data_av.len().checked_sub(16)?;
let data_av = data_av.get_mut(..data_len)?;
let (mut data, mut parity) = data_av.split_at_mut(len * 8);
let mut working = [T::zero(); LDPCCode::TM8192.decode_ms_working_len()];
let mut working_u8 = [0; LDPCCode::TM8192.decode_ms_working_u8_len()];
loop {
match data.len() {
4096.. => {
dec_chunk_soft!(data, parity, working, working_u8, out, TM8192, 4096);
}
1024.. => {
dec_chunk_soft!(data, parity, working, working_u8, out, TM2048, 1024);
}
256.. => {
dec_chunk_soft!(data, parity, working, working_u8, out, TC512, 256);
}
128.. => {
dec_chunk_soft!(data, parity, working, working_u8, out, TC256, 128);
}
64.. => {
dec_chunk_soft!(data, parity, working, working_u8, out, TC128, 64);
}
0 => break,
_ => {
let mut code_data = [
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
]
.map(|x| if x > 0 { -T::maxval() } else { T::maxval() });
code_data[..data.len()].copy_from_slice(data);
let code_parity = &parity.get_mut(..64)?;
let mut input = [T::zero(); 128];
input[..64].copy_from_slice(&code_data);
input[64..].copy_from_slice(code_parity);
let mut tmp_out = [0; LDPCCode::TC128.output_len()];
LDPCCode::TC128.decode_ms(
&input,
&mut tmp_out,
&mut working[..LDPCCode::TC128.decode_ms_working_len()],
&mut working_u8[..LDPCCode::TC128.decode_ms_working_u8_len()],
16,
);
out.try_extend_from_slice(&tmp_out[..(data.len() / 8)])
.ok()?;
data = &mut data[..0];
parity = &mut parity[..0];
}
}
}
Some(())
}
fn len_from_soft<T: DecodeFrom>(bits: &[T; 16]) -> u16 {
let mut upper = 0;
for b in &bits[0..8] {
upper <<= 1;
upper |= u8::from(b.hard_bit());
}
let mut lower = 0;
for b in &bits[8..] {
lower <<= 1;
lower |= u8::from(b.hard_bit());
}
u16::from_le_bytes([upper, lower])
}
#[cfg(test)]
mod tests {
use super::*;
use crate::soft_bit::FromHardBit;
use bitvec::{order::Msb0, view::BitView};
#[test]
fn len_test() {
let mut buf = [0; 8191];
let mut data = Buffer::new_empty(&mut buf);
for _ in 0..41 {
data.extend(b"Example packet data wueirpqwerwrywqoeiruy29346129384761");
}
data.extend(b"Example packet data wueirpqwerwrywqoeiru346129384761");
assert_eq!(2349, data.len());
encode(&mut data).unwrap();
assert_eq!(4703, data.len());
}
#[test]
fn basic_encode_decode_short() {
let mut buf = [0; 32];
let mut buf2 = [0; 32];
let mut data = Buffer::new_empty(&mut buf);
data.try_extend_from_slice(b"Hello world!").unwrap();
let orig = data.clone_backing(&mut buf2);
encode(&mut data).unwrap();
decode(&mut data).unwrap();
assert_eq!(*orig, *data);
}
#[test]
fn basic_encode_decode() {
let mut buf = [0; 8191];
let mut buf2 = [0; 8191];
let mut data = Buffer::new_empty(&mut buf);
for _ in 0..50 {
data.extend(b"This is a test packet. jsalksjd093809324JASLD:LKD*#$)(*#@)");
}
let orig = data.clone_backing(&mut buf2);
encode(&mut data).unwrap();
assert_ne!(*orig, *data);
decode(&mut data).unwrap();
assert_eq!(*orig, *data);
}
#[test]
fn encode_decode_with_bit_flips() {
let mut buf = [0; 8191];
let mut buf2 = [0; 8191];
let mut data = Buffer::new_empty(&mut buf);
for _ in 0..50 {
data.extend(b"jsalksjd093809324JASLD:LKD*#$)(*#@) Another test packet");
}
let orig = data.clone_backing(&mut buf2);
encode(&mut data).unwrap();
assert_ne!(*orig, *data);
data[234] ^= 0x55;
data[0] ^= 0xAA;
data[999] ^= 0x43;
decode(&mut data).unwrap();
assert_eq!(*orig, *data);
}
#[test]
fn basic_encode_decode_soft() {
let mut buf = [0; 8191];
let mut buf2 = [0; 8191];
let mut data = Buffer::new_empty(&mut buf);
for _ in 0..50 {
data.extend(b"This is a test packet. jsalksjd093809324JASLD:LKD*#$)(*#@)");
}
let orig = data.clone_backing(&mut buf2);
encode(&mut data).unwrap();
assert_ne!(*orig, *data);
let mut soft = [0.0; 8191 * 8];
let mut soft = Buffer::new_empty(&mut soft);
for b in data.view_bits::<Msb0>() {
soft.push(f32::from_hard_bit(*b));
}
let mut out = [0; 8191];
let mut out = Buffer::new_empty(&mut out);
decode_soft(&mut soft, &mut out).unwrap();
assert_eq!(*orig, *out);
}
}