Skip to content
Snippets Groups Projects
ldpc.rs 10.9 KiB
Newer Older
Stephen D's avatar
Stephen D committed
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;
        }
    };
}

Stephen D's avatar
Stephen D committed
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..];
        }
    };
}

Stephen D's avatar
Stephen D committed
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..];
        }
    };
}

// On failure this still modifies the data array!
Stephen D's avatar
Stephen D committed
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<()> {
Stephen D's avatar
Stephen D committed
    if data_av.len() < 2 {
Stephen D's avatar
Stephen D committed
    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;
    }

Stephen D's avatar
Stephen D committed
    let (mut data, parity) = data_av.split_at_mut(len);
    let mut parity = parity.get_mut(..parity.len().checked_sub(2)?)?;
Stephen D's avatar
Stephen D committed
    let mut working = [0; LDPCCode::TM8192.decode_bf_working_len()];
Stephen D's avatar
Stephen D committed
        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];
            }
Stephen D's avatar
Stephen D committed
    // remove the parity data
    data_av.truncate(len);

Stephen D's avatar
Stephen D committed
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() {
Stephen D's avatar
Stephen D committed
        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);
Stephen D's avatar
Stephen D committed

    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,

            _ => {
                // Extra bits are padded with 0xAA
                // We need to tell the soft decoder that these bits can't have flipped
Stephen D's avatar
Stephen D committed
                // So we use T::maxval
Stephen D's avatar
Stephen D committed
                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,
                ]
Stephen D's avatar
Stephen D committed
                .map(|x| if x > 0 { -T::maxval() } else { T::maxval() });
Stephen D's avatar
Stephen D committed
                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() {
        // from the example in the docs
        let mut buf = [0; 8191];
        let mut data = Buffer::new_empty(&mut buf);
            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());
    }

Stephen D's avatar
Stephen D committed
    #[test]
    fn basic_encode_decode_short() {
        let mut buf = [0; 32];
        let mut buf2 = [0; 32];
        let mut data = Buffer::new_empty(&mut buf);
Stephen D's avatar
Stephen D committed
        data.try_extend_from_slice(b"Hello world!").unwrap();
        let orig = data.clone_backing(&mut buf2);
Stephen D's avatar
Stephen D committed

        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);
Stephen D's avatar
Stephen D committed

    #[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);
    }