use super::bits::{read_bits, BitWriter};
pub const ADKD0_BITS: usize = 549;
pub const ADKD4_BITS: usize = 141;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BitString {
pub bytes: Vec<u8>,
pub bits: usize,
}
const ADKD0_PIECES: [(u8, usize, usize); 5] = [
(1, 6, 120), (2, 6, 120), (3, 6, 122), (4, 6, 120), (5, 6, 67), ];
const ADKD4_PIECES: [(u8, usize, usize); 2] = [
(6, 6, 99), (10, 86, 42), ];
fn word_type(w: &[u8; 16]) -> u8 {
(w[0] >> 2) & 0x3F
}
fn collect(words: &[[u8; 16]], pieces: &[(u8, usize, usize)], total: usize) -> Option<BitString> {
let mut out = BitWriter::new();
for &(wt, off, len) in pieces {
let w = words.iter().find(|w| word_type(w) == wt)?;
let mut done = 0;
while done < len {
let n = (len - done).min(32);
out.push(read_bits(w, off + done, n)?, n);
done += n;
}
}
debug_assert_eq!(out.bit_len(), total);
Some(BitString {
bytes: out.into_bytes(),
bits: total,
})
}
pub fn adkd0(words: &[[u8; 16]]) -> Option<BitString> {
let iod = |wt: u8| {
words
.iter()
.find(|w| word_type(w) == wt)
.and_then(|w| read_bits(w, 6, 10))
};
let first = iod(1)?;
if (2..=4).any(|wt| iod(wt) != Some(first)) {
return None;
}
collect(words, &ADKD0_PIECES, ADKD0_BITS)
}
pub fn carries_ephemeris_words(words: &[[u8; 16]]) -> bool {
words.iter().any(|w| (1..=5).contains(&word_type(w)))
}
pub fn iodnav(words: &[[u8; 16]]) -> Option<u16> {
let w = words.iter().find(|w| word_type(w) == 1)?;
read_bits(w, 6, 10).map(|v| v as u16)
}
pub fn adkd4(words: &[[u8; 16]]) -> Option<BitString> {
collect(words, &ADKD4_PIECES, ADKD4_BITS)
}
pub fn dummy(adkd: u8) -> Option<BitString> {
let bits = match adkd {
0 | 12 => ADKD0_BITS,
4 => ADKD4_BITS,
_ => return None,
};
Some(BitString {
bytes: vec![0; bits.div_ceil(8)],
bits,
})
}
#[cfg(test)]
mod tests {
use super::*;
fn word(wt: u8, fill: impl Fn(usize) -> u8) -> [u8; 16] {
let mut w = BitWriter::new();
w.push(u64::from(wt), 6);
for i in 6..128 {
w.push(u64::from(fill(i)), 1);
}
let mut out = [0u8; 16];
out.copy_from_slice(&w.into_bytes());
out
}
#[test]
fn adkd0_has_549_bits_and_takes_the_right_pieces() {
let w1 = word(1, |i| u8::from((6..126).contains(&i)));
let w2 = word(2, |i| u8::from((6..126).contains(&i)));
let w3 = word(3, |i| u8::from((6..128).contains(&i)));
let w4 = word(4, |i| u8::from((6..126).contains(&i)));
let w5 = word(5, |i| u8::from((6..73).contains(&i)));
let d = adkd0(&[w5, w3, w1, w4, w2]).unwrap();
assert_eq!(d.bits, 549);
let ones: u32 = (0..549)
.map(|i| read_bits(&d.bytes, i, 1).unwrap() as u32)
.sum();
assert_eq!(ones, 120 + 120 + 122 + 120 + 67);
assert_eq!(read_bits(&d.bytes, 0, 10), Some(0x3FF));
}
#[test]
fn adkd0_needs_consistent_iodnav_and_all_words() {
let mk = |wt, iod: u64| {
let mut w = BitWriter::new();
w.push(wt, 6);
w.push(iod, 10);
w.push(0, 112);
let mut o = [0u8; 16];
o.copy_from_slice(&w.into_bytes());
o
};
let good = [mk(1, 5), mk(2, 5), mk(3, 5), mk(4, 5), mk(5, 0)];
assert!(adkd0(&good).is_some());
let mut bad = good;
bad[2] = mk(3, 6);
assert!(adkd0(&bad).is_none());
assert!(adkd0(&good[..4]).is_none());
}
#[test]
fn adkd4_is_141_bits_from_wt6_and_wt10() {
let w6 = word(6, |i| u8::from((6..105).contains(&i)));
let w10 = word(10, |i| u8::from(i >= 86));
let d = adkd4(&[w6, w10]).unwrap();
assert_eq!(d.bits, 141);
let ones: u32 = (0..141)
.map(|i| read_bits(&d.bytes, i, 1).unwrap() as u32)
.sum();
assert_eq!(ones, 141);
assert!(adkd4(&[w6]).is_none());
assert_eq!(dummy(4).unwrap().bits, 141);
assert!(dummy(7).is_none());
}
}