#[cfg(not(test))]
use log::{debug, info};
#[cfg(test)]
use std::println as debug;
#[cfg(test)]
use std::println as info;
use super::assembler;
use super::combiner;
use super::LinkState;
#[derive(Clone, Debug)]
pub struct Framer {
state: State,
symbol_count_last_burst: u64,
max_prefix_bit_errors: u32,
max_invalid_bytes: u32,
}
impl Framer {
pub fn new(max_prefix_bit_errors: u32, max_invalid_bytes: u32) -> Self {
Self {
state: State::Idle,
symbol_count_last_burst: 0,
max_prefix_bit_errors,
max_invalid_bytes,
}
}
pub fn reset(&mut self) {
self.state = State::Idle;
self.symbol_count_last_burst = 0;
}
pub fn input(&mut self, data: u8, symbol_count: u64, restart: bool) -> LinkState {
if restart {
let out = self.end();
debug!("burst: searching: framer restarted");
self.state = State::PrefixSearch(0, 0);
let _ = self.input(data, symbol_count, false);
match out {
LinkState::Burst(_) => return out,
_ => return LinkState::Searching,
}
}
match self.state {
State::Idle => LinkState::NoCarrier,
State::PrefixSearch(ref mut search_word, ref mut count) => {
*search_word = (*search_word << 8) | data as u32;
*count += 1;
if message_prefix_errors(*search_word) <= self.max_prefix_bit_errors {
info!("burst: started: after {} bytes", count);
let prefix_data = search_word.to_be_bytes();
let mut data = Vec::with_capacity(assembler::MAX_MESSAGE_LENGTH);
data.extend_from_slice(&prefix_data[0..4]);
self.state = State::DataRead(data, 0);
} else if *count > Self::PREFIX_SEARCH_LEN {
info!(
"burst: abandoned: could not find start after {} bytes",
Self::PREFIX_SEARCH_LEN
);
self.state = State::Idle;
}
self.state()
}
State::DataRead(ref mut msg, ref mut invalid_byte_count) => {
*invalid_byte_count += !combiner::is_allowed_byte(data) as u32;
if *invalid_byte_count > self.max_invalid_bytes {
self.end()
} else {
msg.push(data);
self.state()
}
}
}
}
pub fn end(&mut self) -> LinkState {
match std::mem::take(&mut self.state) {
State::DataRead(msg, _) => {
self.state = State::Idle;
LinkState::Burst(msg)
}
_ => {
self.state = State::Idle;
LinkState::NoCarrier
}
}
}
pub fn state(&self) -> LinkState {
match &self.state {
State::Idle => LinkState::NoCarrier,
State::PrefixSearch(..) => LinkState::Searching,
State::DataRead(..) => LinkState::Reading,
}
}
const PREFIX_SEARCH_LEN: u32 = 21;
}
#[derive(Clone, Debug, PartialEq, Eq)]
enum State {
Idle,
PrefixSearch(u32, u32),
DataRead(Vec<u8>, u32),
}
impl Default for State {
fn default() -> Self {
Self::Idle
}
}
fn message_prefix_errors(inp: u32) -> u32 {
const PREFIX_BYTES_START: u32 =
u32::from_be_bytes(['Z' as u8, 'C' as u8, 'Z' as u8, 'C' as u8]);
const PREFIX_BYTES_END: u32 = u32::from_be_bytes(['N' as u8, 'N' as u8, 'N' as u8, 'N' as u8]);
let err_start = (inp ^ PREFIX_BYTES_START).count_ones();
let err_end = (inp ^ PREFIX_BYTES_END).count_ones();
u32::min(err_start, err_end)
}
#[cfg(test)]
mod tests {
use super::super::waveform;
use super::*;
fn is_link_active(lx: &LinkState) -> bool {
match lx {
LinkState::Searching => true,
LinkState::Reading => true,
_ => false,
}
}
#[test]
fn test_message_prefix_errors() {
const START: [u8; 4] = ['Z' as u8, 'C' as u8, 'Z' as u8, 'C' as u8];
const END: [u8; 4] = ['N' as u8, 'N' as u8, 'N' as u8, 'N' as u8];
const PREAMBLE: [u8; 4] = [171, 171, 171, 171];
const ZCZE: [u8; 4] = ['Z' as u8, 'C' as u8, 'Z' as u8, 'E' as u8];
assert_eq!(0, message_prefix_errors(u32::from_be_bytes(START)));
assert_eq!(0, message_prefix_errors(u32::from_be_bytes(END)));
assert_eq!(18, message_prefix_errors(u32::from_be_bytes(PREAMBLE)));
assert_eq!(2, message_prefix_errors(u32::from_be_bytes(ZCZE)));
}
#[test]
fn test_framer_prefix() {
const DATA: &[u8] = &['Z' as u8, 'C' as u8, 'Z' as u8, 'C' as u8];
let mut framer = Framer::new(1, 10);
let mut gave_up = false;
for i in 0..32 {
match framer.input(waveform::PREAMBLE, 0, i == 0) {
LinkState::NoCarrier => {
assert!(i >= Framer::PREFIX_SEARCH_LEN);
gave_up = true;
}
LinkState::Searching => {}
_ => unreachable!(),
}
}
assert!(gave_up);
framer.input(waveform::PREAMBLE, 0, true);
framer.input(waveform::PREAMBLE, 0, true);
let mut last = LinkState::NoCarrier;
for d in DATA {
last = framer.input(*d, 0, false);
assert!(is_link_active(&last));
}
assert_eq!(last, LinkState::Reading);
match &framer.state {
State::DataRead(msg, 0) => {
assert_eq!(msg.len(), 4);
assert_eq!(&msg[0..4], DATA);
}
_ => unreachable!(),
}
assert_eq!(LinkState::Burst("ZCZC".as_bytes().to_owned()), framer.end());
assert_eq!(LinkState::NoCarrier, framer.end());
}
#[test]
fn test_framer_burst_process() {
const MESSAGE: &str = "gArbAZgEZCZC-ORG-EEE-012345-567890+0000-0001122-NOCALL00-GARBAGE";
const PERMIT_INVALID: u32 = 10;
let mut found = false;
let mut framer = Framer::new(2, PERMIT_INVALID);
framer.input(waveform::PREAMBLE, 0, true);
for c in MESSAGE.as_bytes() {
assert!(is_link_active(&framer.input(*c, 0, false)));
}
for j in 0..PERMIT_INVALID + 1 {
let out = framer.input(waveform::PREAMBLE, 0, false);
if j >= PERMIT_INVALID {
if let LinkState::Burst(m) = out {
assert!(&m.starts_with(
"ZCZC-ORG-EEE-012345-567890+0000-0001122-NOCALL00-".as_bytes()
));
found = true;
} else {
unreachable!();
}
} else {
assert!(is_link_active(&out));
}
}
assert!(found);
}
}