1use crate::codec::crc::{ft8_add_crc, ft8_crc14, ft8_extract_crc};
12use crate::codec::gray::{gray4_decode, gray4_encode};
13use crate::codec::ldpc::{self, ldpc_decode_soft, ldpc_encode};
14use crate::modulate::Ft4Frame;
15use crate::modulate::ft4::FT4_DATA_SYMS;
16
17pub type Ft4Bits = [u8; 10];
19
20const FT4_XOR: [u8; 10] = [0x4A, 0x5E, 0x89, 0xB4, 0xB0, 0x8A, 0x79, 0x55, 0xBE, 0x28];
23
24pub struct Ft4Codec;
26
27impl Ft4Codec {
28 pub fn encode(payload: &Ft4Bits) -> Ft4Frame {
30 let mut scrambled = [0u8; 10];
32 for i in 0..10 {
33 scrambled[i] = payload[i] ^ FT4_XOR[i];
34 }
35
36 let mut a91 = [0u8; ldpc::K_BYTES];
38 ft8_add_crc(&scrambled, &mut a91);
39
40 let mut codeword = [0u8; ldpc::N_BYTES];
42 ldpc_encode(&a91, &mut codeword);
43
44 let mut tones = [0u8; FT4_DATA_SYMS];
46 let mut mask: u8 = 0x80;
47 let mut byte_idx = 0usize;
48
49 for tone in tones.iter_mut() {
50 let mut bits2: u8 = 0;
51 for bit_pos in (0u8..2).rev() {
52 if codeword[byte_idx] & mask != 0 {
53 bits2 |= 1 << bit_pos;
54 }
55 mask >>= 1;
56 if mask == 0 {
57 mask = 0x80;
58 byte_idx += 1;
59 }
60 }
61 *tone = gray4_encode(bits2);
62 }
63
64 Ft4Frame::new(tones)
65 }
66
67 pub fn decode_hard(frame: &Ft4Frame) -> Option<Ft4Bits> {
69 let llr = Self::frame_to_llr_hard(frame);
70 Self::decode_llr(&llr)
71 }
72
73 pub fn decode_soft(llr: &[f32; ldpc::N]) -> Option<Ft4Bits> {
75 Self::decode_llr(llr)
76 }
77
78 pub fn frame_to_llr_hard(frame: &Ft4Frame) -> [f32; ldpc::N] {
80 let mut llr = [0.0f32; ldpc::N];
81 for (sym_idx, &tone) in frame.0.iter().enumerate() {
82 let bin = gray4_decode(tone);
83 for bit_pos in 0..2usize {
84 let bit = (bin >> (1 - bit_pos)) & 1;
85 llr[sym_idx * 2 + bit_pos] = if bit == 0 { 10.0 } else { -10.0 };
86 }
87 }
88 llr
89 }
90
91 fn decode_llr(llr: &[f32; ldpc::N]) -> Option<Ft4Bits> {
92 let mut plain = [0u8; ldpc::N];
93 let errors = ldpc_decode_soft(llr, 20, &mut plain);
94 if errors != 0 {
95 return None;
96 }
97
98 let mut a91 = [0u8; ldpc::K_BYTES];
100 for i in 0..ldpc::K {
101 if plain[i] == 1 {
102 a91[i / 8] |= 0x80 >> (i % 8);
103 }
104 }
105
106 let extracted = ft8_extract_crc(&a91);
110 let mut buf = a91;
111 buf[9] &= 0xF8;
112 buf[10] = 0;
113 buf[11] = 0;
114 let computed = ft8_crc14(&buf, 82);
115 if extracted != computed {
116 return None;
117 }
118
119 let mut payload = [0u8; 10];
123 for i in 0..10 {
124 payload[i] = a91[i] ^ FT4_XOR[i];
125 }
126 payload[9] &= 0xF8;
127 Some(payload)
128 }
129}