pub const FRAME_BYTES: usize = 18;
pub const FIELDS: usize = 14;
pub const PAYLOAD_BITS: usize = 139;
pub const SUPPRESS_BIT: usize = 138;
pub const SUPPRESS_SRC_BIT: usize = 143;
#[derive(Clone, Copy, Debug, Default)]
pub struct Params {
pub field: [i16; FIELDS],
pub suppress: bool,
}
pub const SUBFRAME_FIELDS: usize = 3;
pub const LAG: usize = 0;
pub const CODE: usize = 1;
pub const GAIN: usize = 2;
#[derive(Clone, Copy, Debug)]
pub struct Subframe {
pub lag: i16,
pub code: i16,
pub gain: i16,
}
impl Params {
pub fn half_base(half: usize) -> usize {
2 + 6 * half
}
pub fn subframe_base(half: usize, sub: usize) -> usize {
Self::half_base(half) + SUBFRAME_FIELDS * sub
}
pub fn subframe(&self, half: usize, sub: usize) -> Subframe {
let base = Self::subframe_base(half, sub);
Subframe {
lag: self.field[base + LAG],
code: self.field[base + CODE],
gain: self.field[base + GAIN],
}
}
}
pub fn unpack(words: &[i16; 9]) -> Params {
let w = |i: usize| words[i] as u16 as u32;
let mut p = Params::default();
p.field[0] = (w(0) >> 8) as i16;
p.field[1] = (((w(0) & 0xff) << 4) | (w(1) >> 12)) as i16;
p.field[2] = ((w(1) >> 4) & 0xff) as i16;
p.field[3] = (((w(1) & 0x0f) << 12) | (w(2) >> 4)) as i16;
p.field[4] = (((w(2) & 0x0f) << 3) | (w(3) >> 13)) as i16;
p.field[5] = ((w(3) >> 8) & 0x1f) as i16;
p.field[6] = (((w(3) & 0xff) << 8) | (w(4) >> 8)) as i16;
p.field[7] = ((w(4) >> 1) & 0x7f) as i16;
p.field[8] = (((w(4) & 0x01) << 7) | (w(5) >> 9)) as i16;
p.field[9] = (((w(5) & 0x1ff) << 7) | (w(6) >> 9)) as i16;
p.field[10] = ((w(6) >> 2) & 0x7f) as i16;
p.field[11] = (((w(6) & 0x03) << 3) | (w(7) >> 13)) as i16;
p.field[12] = (((w(7) & 0x1fff) << 3) | (w(8) >> 13)) as i16;
p.field[13] = ((w(8) >> 6) & 0x7f) as i16;
p.suppress = (w(8) >> 5) & 1 != 0;
p
}
pub fn canonicalize(frame: &[u8; FRAME_BYTES]) -> [i16; 9] {
let mut payload = [0u8; FRAME_BYTES];
for bit in 0..PAYLOAD_BITS {
if test_bit(frame, bit) {
set_bit(&mut payload, bit);
}
}
if test_bit(frame, SUPPRESS_SRC_BIT) {
set_bit(&mut payload, SUPPRESS_BIT);
}
let mut words = [0i16; 9];
for (i, w) in words.iter_mut().enumerate() {
*w = (((payload[i * 2] as u16) << 8) | payload[i * 2 + 1] as u16) as i16;
}
words
}
pub fn is_reset(words: &[i16; 9]) -> bool {
words[8] & 0x0f == 0x08
}
#[inline]
fn test_bit(buf: &[u8], bit: usize) -> bool {
buf[bit / 8] & (0x80 >> (bit % 8)) != 0
}
#[inline]
fn set_bit(buf: &mut [u8], bit: usize) {
buf[bit / 8] |= 0x80 >> (bit % 8);
}
pub fn parse_hex_line(line: &str) -> Option<[u8; FRAME_BYTES]> {
let mut out = [0u8; FRAME_BYTES];
let mut n = 0;
let mut upper: Option<u8> = None;
for ch in line.chars() {
if ch.is_ascii_whitespace() {
continue;
}
let nib = ch.to_digit(16)? as u8;
match upper {
None => upper = Some(nib),
Some(hi) => {
if n >= FRAME_BYTES {
return None;
}
out[n] = (hi << 4) | nib;
n += 1;
upper = None;
}
}
}
if upper.is_some() || n != FRAME_BYTES {
return None;
}
Some(out)
}
pub fn pack(p: &Params) -> [i16; 9] {
let f = |i: usize| p.field[i] as u16 as u32;
let s = p.suppress as u32;
let w = [
(f(0) << 8) | (f(1) >> 4),
((f(1) & 0xf) << 12) | (f(2) << 4) | (f(3) >> 12),
((f(3) & 0xfff) << 4) | (f(4) >> 3),
((f(4) & 0x7) << 13) | (f(5) << 8) | (f(6) >> 8),
((f(6) & 0xff) << 8) | (f(7) << 1) | (f(8) >> 7),
((f(8) & 0x7f) << 9) | (f(9) >> 7),
((f(9) & 0x7f) << 9) | (f(10) << 2) | (f(11) >> 3),
((f(11) & 0x7) << 13) | (f(12) >> 3),
((f(12) & 0x7) << 13) | (f(13) << 6) | (s << 5),
];
let mut out = [0i16; 9];
for (o, &v) in out.iter_mut().zip(w.iter()) {
*o = v as u16 as i16;
}
out
}
pub fn to_bytes(words: &[i16; 9]) -> [u8; FRAME_BYTES] {
let mut out = [0u8; FRAME_BYTES];
for (i, &w) in words.iter().enumerate() {
out[2 * i] = (w as u16 >> 8) as u8;
out[2 * i + 1] = w as u8;
}
out
}
pub fn to_hex_line(frame: &[u8; FRAME_BYTES]) -> String {
use std::fmt::Write as _;
let mut line = String::with_capacity(2 * FRAME_BYTES);
for b in frame {
let _ = write!(line, "{b:02x}");
}
line
}