use core::fmt;
pub mod addr;
pub mod fcs;
pub mod frame;
pub mod hdlc;
pub use addr::{Address, Callsign, PathHop, Ssid};
pub use fcs::{Fcs, crc16_x25};
pub use frame::UiFrame;
pub use hdlc::{HdlcDeframer, RecoveryPolicy};
#[cfg(feature = "demod")]
use crate::demodulator::Demodulator;
#[cfg(feature = "demod")]
use crate::discriminator::{Discriminator, QuadratureCorrelator};
#[cfg(feature = "mod")]
use crate::modulator::{F32Samples, I16Samples, Modulator};
#[cfg(feature = "mod")]
use crate::nrzi;
#[cfg(feature = "demod")]
use crate::nrzi::NrziDecoder;
#[cfg(feature = "demod")]
use crate::types::Bit;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Ax25Error {
InvalidCallsignChar {
got: u8,
},
CallsignLengthInvalid {
got: usize,
},
SsidOutOfRange {
got: u8,
},
FrameTooLarge {
len: usize,
max: usize,
},
FrameTooShort {
len: usize,
min: usize,
},
FcsMismatch {
expected: u16,
computed: u16,
},
InvalidControl {
got: u8,
},
InvalidPid {
got: u8,
},
TooManyDigipeaters {
got: usize,
max: usize,
},
}
impl fmt::Display for Ax25Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Ax25Error::InvalidCallsignChar { got } => write!(
f,
"callsign byte 0x{got:02X} is invalid: must be an uppercase letter A-Z or digit 0-9"
),
Ax25Error::CallsignLengthInvalid { got } => write!(
f,
"callsign length {got} is invalid: must be 1..=6 characters"
),
Ax25Error::SsidOutOfRange { got } => {
write!(f, "SSID {got} is out of range: must be within 0..=15")
}
Ax25Error::FrameTooLarge { len, max } => write!(
f,
"frame of {len} bytes is too large: the buffer holds at most {max} bytes"
),
Ax25Error::FrameTooShort { len, min } => write!(
f,
"frame of {len} bytes is too short: an AX.25 UI frame needs at least {min} bytes"
),
Ax25Error::FcsMismatch { expected, computed } => write!(
f,
"frame check sequence mismatch: frame carries 0x{expected:04X} but contents compute to 0x{computed:04X}"
),
Ax25Error::InvalidControl { got } => write!(
f,
"control field 0x{got:02X} is invalid: a UI frame requires 0x03"
),
Ax25Error::InvalidPid { got } => write!(
f,
"PID field 0x{got:02X} is invalid: no-layer-3 requires 0xF0"
),
Ax25Error::TooManyDigipeaters { got, max } => write!(
f,
"{got} digipeater addresses exceed the supported maximum of {max}"
),
}
}
}
impl core::error::Error for Ax25Error {}
#[cfg(feature = "mod")]
pub fn tx_i16(
frame: &[u8],
modulator: Modulator,
) -> I16Samples<nrzi::EncodeIter<hdlc::FrameBits<'_>>> {
modulator.i16_samples(nrzi::encode_iter(hdlc::frame_bits(
frame,
hdlc::DEFAULT_PREAMBLE_FLAGS,
hdlc::DEFAULT_TAIL_FLAGS,
)))
}
#[cfg(feature = "mod")]
pub fn tx_f32(
frame: &[u8],
modulator: Modulator,
) -> F32Samples<nrzi::EncodeIter<hdlc::FrameBits<'_>>> {
modulator.f32_samples(nrzi::encode_iter(hdlc::frame_bits(
frame,
hdlc::DEFAULT_PREAMBLE_FLAGS,
hdlc::DEFAULT_TAIL_FLAGS,
)))
}
#[cfg(feature = "demod")]
#[derive(Debug, Clone)]
pub struct FrameReceiver<const N: usize, D = QuadratureCorrelator> {
demodulator: Demodulator<D>,
nrzi: NrziDecoder,
deframer: HdlcDeframer<N>,
}
#[cfg(feature = "demod")]
impl<const N: usize, D: Discriminator> FrameReceiver<N, D> {
#[must_use]
pub fn new(demodulator: Demodulator<D>) -> Self {
Self {
demodulator,
nrzi: NrziDecoder::default(),
deframer: HdlcDeframer::new(),
}
}
pub fn push_sample_i16(&mut self, sample: i16) -> Option<Result<&[u8], Ax25Error>> {
match self.demodulator.push_sample_i16(sample) {
Some(line) => self.push_line_bit(line),
None => None,
}
}
pub fn push_sample_f32(&mut self, sample: f32) -> Option<Result<&[u8], Ax25Error>> {
match self.demodulator.push_sample_f32(sample) {
Some(line) => self.push_line_bit(line),
None => None,
}
}
fn push_line_bit(&mut self, line: Bit) -> Option<Result<&[u8], Ax25Error>> {
let data = self.nrzi.decode(line);
self.deframer.push(data)
}
}