use crate::codec::crc::{ft8_add_crc, ft8_extract_crc, ft8_crc14};
use crate::codec::gray::{gray8_encode, gray8_decode};
use crate::codec::ldpc::{self, ldpc_encode, ldpc_decode_soft};
use crate::modulate::Ft8Frame;
use crate::modulate::ft8::FT8_DATA_SYMS;
pub type Ft8Bits = [u8; 10];
pub struct Ft8Codec;
impl Ft8Codec {
pub fn encode(payload: &Ft8Bits) -> Ft8Frame {
let mut a91 = [0u8; ldpc::K_BYTES];
ft8_add_crc(payload, &mut a91);
let mut codeword = [0u8; ldpc::N_BYTES];
ldpc_encode(&a91, &mut codeword);
let mut tones = [0u8; FT8_DATA_SYMS];
let mut mask: u8 = 0x80;
let mut byte_idx = 0usize;
for tone in tones.iter_mut() {
let mut bits3: u8 = 0;
for bit_pos in (0u8..3).rev() {
if codeword[byte_idx] & mask != 0 {
bits3 |= 1 << bit_pos;
}
mask >>= 1;
if mask == 0 {
mask = 0x80;
byte_idx += 1;
}
}
*tone = gray8_encode(bits3);
}
Ft8Frame::new(tones)
}
pub fn decode_hard(frame: &Ft8Frame) -> Option<Ft8Bits> {
let llr = Self::frame_to_llr_hard(frame);
Self::decode_llr(&llr)
}
pub fn decode_soft(llr: &[f32; ldpc::N]) -> Option<Ft8Bits> {
Self::decode_llr(llr)
}
pub fn frame_to_llr_hard(frame: &Ft8Frame) -> [f32; ldpc::N] {
let mut llr = [0.0f32; ldpc::N];
for (sym_idx, &tone) in frame.0.iter().enumerate() {
let bin = gray8_decode(tone);
for bit_pos in 0..3usize {
let bit = (bin >> (2 - bit_pos)) & 1;
llr[sym_idx * 3 + bit_pos] = if bit == 0 { 10.0 } else { -10.0 };
}
}
llr
}
fn decode_llr(llr: &[f32; ldpc::N]) -> Option<Ft8Bits> {
let mut plain = [0u8; ldpc::N];
let errors = ldpc_decode_soft(llr, 20, &mut plain);
if errors != 0 {
return None;
}
let mut a91 = [0u8; ldpc::K_BYTES];
for i in 0..ldpc::K {
if plain[i] == 1 {
a91[i / 8] |= 0x80 >> (i % 8);
}
}
let extracted = ft8_extract_crc(&a91);
let mut buf = a91;
buf[9] &= 0xF8; buf[10] = 0; buf[11] = 0; let computed = ft8_crc14(&buf, 82);
if extracted != computed {
return None;
}
let mut payload = [0u8; 10];
payload.copy_from_slice(&a91[..10]);
payload[9] &= 0xF8;
Some(payload)
}
}