use super::ldpc::{LdpcCode, Subframe};
pub const FRAME_SYMBOLS: usize = 6000;
pub const SYNC_PATTERN_HEX: &str = "CC63F74536F49E04A";
pub const SYNC_SYMBOLS: usize = 68;
pub const SB1_SYMBOLS: usize = 52;
pub const SB2_DATA_BITS: usize = 1176;
pub const SB34_DATA_BITS: usize = 846;
pub const INTERLEAVER_ROWS: usize = 60;
pub const INTERLEAVER_COLS: usize = 98;
pub const CRC24_POLY: u32 = 0x186_4CFB;
pub fn hex_bits(hex: &str, n: usize) -> Vec<u8> {
let mut bits = Vec::with_capacity(hex.len() * 4);
for ch in hex.chars() {
let v = ch.to_digit(16).expect("hexadecimal digit") as u8;
for k in (0..4).rev() {
bits.push((v >> k) & 1);
}
}
let skip = bits.len().saturating_sub(n);
bits[skip..].to_vec()
}
pub fn sync_pattern() -> Vec<u8> {
hex_bits(SYNC_PATTERN_HEX, SYNC_SYMBOLS)
}
pub fn spare_bits(n: usize) -> Vec<u8> {
(0..n).map(|i| (i % 2) as u8).collect()
}
pub fn encode_sb1(fid: u8, toi: u8) -> Result<Vec<u8>, String> {
if fid > 3 {
return Err(format!("FID {fid} does not fit two bits (0..3)"));
}
if toi > 99 {
return Err(format!("TOI {toi} outside 0..99 (LSIS-410)"));
}
Ok(bch_51_8(((fid as u16) << 7) | toi as u16))
}
pub fn decode_sb1(soft: &[f64]) -> Result<(u8, u8), String> {
if soft.len() != SB1_SYMBOLS {
return Err(format!("{SB1_SYMBOLS} subframe-1 symbols expected"));
}
let mut best = (0u16, 0.0f64);
for low8 in 0u16..256 {
let corr: f64 = soft
.iter()
.zip(&bch_51_8(low8))
.map(|(&s, &c)| if c == 0 { s } else { -s })
.sum();
if corr.abs() > best.1.abs() {
best = (low8, corr);
}
}
let word = (u16::from(best.1 < 0.0) << 8) | best.0;
Ok(((word >> 7) as u8, (word & 0x7F) as u8))
}
fn bch_51_8(word: u16) -> Vec<u8> {
let bit = |i: usize| ((word >> (8 - i)) & 1) as u8;
let mut stage = [0u8; 8];
for i in 1..=8 {
stage[8 - i] = bit(i);
}
let b0 = bit(0);
let mut out = Vec::with_capacity(SB1_SYMBOLS);
out.push(b0);
for _ in 0..51 {
out.push(stage[7] ^ b0);
let fb = stage[0] ^ stage[3] ^ stage[4] ^ stage[5] ^ stage[6] ^ stage[7];
for j in (1..8).rev() {
stage[j] = stage[j - 1];
}
stage[0] = fb;
}
out
}
pub fn crc24_bits(bits: &[u8]) -> [u8; 24] {
let mut reg: u32 = 0;
for &b in bits {
let top = ((reg >> 23) & 1) ^ (b as u32 & 1);
reg = (reg << 1) & 0xFF_FFFF;
if top == 1 {
reg ^= CRC24_POLY & 0xFF_FFFF;
}
}
let mut out = [0u8; 24];
for (i, o) in out.iter_mut().enumerate() {
*o = ((reg >> (23 - i)) & 1) as u8;
}
out
}
pub fn crc24_bytes(data: &[u8]) -> u32 {
let bits: Vec<u8> = data
.iter()
.flat_map(|&b| (0..8).rev().map(move |k| (b >> k) & 1))
.collect();
crc24_bits(&bits)
.iter()
.fold(0u32, |a, &b| (a << 1) | b as u32)
}
pub fn interleave(sym: &[u8]) -> Vec<u8> {
assert_eq!(sym.len(), INTERLEAVER_ROWS * INTERLEAVER_COLS);
let mut out = Vec::with_capacity(sym.len());
for c in 0..INTERLEAVER_COLS {
for r in 0..INTERLEAVER_ROWS {
out.push(sym[r * INTERLEAVER_COLS + c]);
}
}
out
}
pub fn deinterleave<T: Copy + Default>(sym: &[T]) -> Vec<T> {
assert_eq!(sym.len(), INTERLEAVER_ROWS * INTERLEAVER_COLS);
let mut out = vec![T::default(); sym.len()];
let mut j = 0;
for c in 0..INTERLEAVER_COLS {
for r in 0..INTERLEAVER_ROWS {
out[r * INTERLEAVER_COLS + c] = sym[j];
j += 1;
}
}
out
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct FrameData {
pub fid: u8,
pub toi: u8,
pub sb2: Vec<u8>,
pub sb3: Vec<u8>,
pub sb4: Vec<u8>,
}
pub fn with_crc(data: &[u8]) -> Vec<u8> {
let mut v: Vec<u8> = data.iter().map(|b| b & 1).collect();
v.extend_from_slice(&crc24_bits(data));
v
}
#[derive(Clone, Debug)]
pub struct FrameCoder {
pub sb2: LdpcCode,
pub sb34: LdpcCode,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DecodedFrame {
pub fid: u8,
pub toi: u8,
pub data: [Vec<u8>; 3],
pub parity_ok: [bool; 3],
pub crc_ok: [bool; 3],
pub sb1_valid: bool,
}
impl FrameCoder {
pub fn load() -> Result<Self, String> {
Ok(FrameCoder {
sb2: LdpcCode::load(Subframe::Sb2)?,
sb34: LdpcCode::load(Subframe::Sb34)?,
})
}
pub fn encode(&self, f: &FrameData) -> Result<Vec<u8>, String> {
if f.sb2.len() != SB2_DATA_BITS
|| f.sb3.len() != SB34_DATA_BITS
|| f.sb4.len() != SB34_DATA_BITS
{
return Err(format!(
"subframe data lengths {}/{}/{} (expected {SB2_DATA_BITS}/{SB34_DATA_BITS}/{SB34_DATA_BITS})",
f.sb2.len(),
f.sb3.len(),
f.sb4.len()
));
}
let mut coded = self.sb2.encode(&with_crc(&f.sb2))?;
coded.extend(self.sb34.encode(&with_crc(&f.sb3))?);
coded.extend(self.sb34.encode(&with_crc(&f.sb4))?);
let mut frame = sync_pattern();
frame.extend(encode_sb1(f.fid, f.toi)?);
frame.extend(interleave(&coded));
debug_assert_eq!(frame.len(), FRAME_SYMBOLS);
Ok(frame)
}
pub fn decode(&self, soft: &[f64], max_iter: usize) -> Result<DecodedFrame, String> {
if soft.len() != FRAME_SYMBOLS {
return Err(format!(
"{FRAME_SYMBOLS} symbols expected, {} given",
soft.len()
));
}
let (fid, toi) = decode_sb1(&soft[SYNC_SYMBOLS..SYNC_SYMBOLS + SB1_SYMBOLS])?;
let coded = deinterleave(&soft[SYNC_SYMBOLS + SB1_SYMBOLS..]);
let n2 = self.sb2.n_tx;
let n3 = self.sb34.n_tx;
let parts = [
(&self.sb2, &coded[..n2], SB2_DATA_BITS),
(&self.sb34, &coded[n2..n2 + n3], SB34_DATA_BITS),
(&self.sb34, &coded[n2 + n3..], SB34_DATA_BITS),
];
let mut data: [Vec<u8>; 3] = Default::default();
let mut parity_ok = [false; 3];
let mut crc_ok = [false; 3];
for (i, (code, llr, nd)) in parts.into_iter().enumerate() {
let (bits, ok) = code.decode_min_sum(llr, max_iter);
crc_ok[i] = crc24_bits(&bits[..nd]) == bits[nd..nd + 24];
parity_ok[i] = ok;
data[i] = bits[..nd].to_vec();
}
Ok(DecodedFrame {
fid,
toi,
data,
parity_ok,
crc_ok,
sb1_valid: toi <= 99,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn crc24_agrees_with_the_byte_level_crc24q() {
let data: Vec<u8> = (0u8..=200).map(|i| i.wrapping_mul(37) ^ 0x5A).collect();
assert_eq!(crc24_bytes(&data), crate::leo_navmsg::codec::crc24q(&data));
}
#[test]
fn out_of_range_toi_is_flagged_not_hidden() {
if !crate::lunar_afs::lsis::cache_present() {
eprintln!("SKIP: {}", crate::lunar_afs::lsis::fetch_hint());
return;
}
let coder = FrameCoder::load().unwrap();
let f = FrameData {
fid: 0,
toi: 7,
sb2: spare_bits(SB2_DATA_BITS),
sb3: spare_bits(SB34_DATA_BITS),
sb4: spare_bits(SB34_DATA_BITS),
};
let mut sym = coder.encode(&f).unwrap();
sym[SYNC_SYMBOLS..SYNC_SYMBOLS + SB1_SYMBOLS].copy_from_slice(&bch_51_8(120));
let soft: Vec<f64> = sym
.iter()
.map(|&b| if b == 1 { -4.0 } else { 4.0 })
.collect();
let d = coder.decode(&soft, 50).unwrap();
assert_eq!((d.fid, d.toi, d.sb1_valid), (0, 120, false));
assert_eq!(d.crc_ok, [true; 3]);
let ok = coder
.decode(
&coder
.encode(&f)
.unwrap()
.iter()
.map(|&b| if b == 1 { -4.0 } else { 4.0 })
.collect::<Vec<_>>(),
50,
)
.unwrap();
assert!(ok.sb1_valid && ok.toi == 7);
}
#[test]
fn interleaver_round_trips() {
let v: Vec<u8> = (0..5880).map(|i| (i % 7 == 0) as u8).collect();
let w = interleave(&v);
assert_ne!(w, v);
assert_eq!(deinterleave(&w), v);
assert_eq!(w[1], v[98]); }
#[test]
fn spare_and_sync_patterns() {
assert_eq!(spare_bits(4), vec![0, 1, 0, 1]);
let sp = sync_pattern();
assert_eq!(sp.len(), 68);
assert_eq!(&sp[..8], &[1, 1, 0, 0, 1, 1, 0, 0]);
}
}