The wire protocol is re-specified as DongLoRa Protocol v2 (see PROTOCOL.md). Every type_id, tag-correlation rule, and field layout is incompatible with 0.1.0. * Add PROTOCOL.md as the normative spec for the v2 wire format. * Replace framing.rs with a streaming FrameDecoder that emits (type_id, tag, payload) triples on each COBS-delimited, CRC-checked frame. * Split the single-file lib.rs into dedicated modules: frame, commands, events, errors, modulation, info, crc, chip_id. Re-exports preserve convenience imports at the crate root. * Replace RadioConfig / Bandwidth with a Modulation enum covering LoRa, FSK/GFSK, LR-FHSS, and FLRC, each with its own typed parameter struct. * Add TxFlags, RxOrigin, SetConfigResult, OkPayload to model v2 response shapes, and RadioChipId for GET_INFO. * Rewrite fuzz corpus: replace the 4 cobs_* / command_roundtrip targets with decoder_feed, frame_roundtrip, lora_config_roundtrip, and parse_device_message; drop parse_radio_config. * Expand proptest and mutation-killer coverage for the new wire shape. * Rewrite canonical-byte test vectors against the v2 spec.
58 lines
1.8 KiB
Rust
58 lines
1.8 KiB
Rust
//! CRC-16/CCITT-FALSE — the wire-level integrity check used by DongLoRa Protocol.
|
|
//!
|
|
//! Polynomial `0x1021`, initial value `0xFFFF`, no reflection, XOR-out `0x0000`.
|
|
//! This is the same variant also known as CRC-16/AUTOSAR or
|
|
//! CRC-16/IBM-3740. It is **NOT** the same as CRC-16/KERMIT,
|
|
//! CRC-16/XMODEM, or plain "CRC-16/CCITT" — those use different initial
|
|
//! values or reflect their inputs.
|
|
//!
|
|
//! Check value: `crc16("123456789") == 0x29B1`. A module-level test pins
|
|
//! this, and `tests/vectors.rs` anchors every Appendix C wire example.
|
|
|
|
/// CRC-16/CCITT-FALSE of `data`.
|
|
pub fn crc16(data: &[u8]) -> u16 {
|
|
let mut crc: u16 = 0xFFFF;
|
|
for &byte in data {
|
|
crc ^= (byte as u16) << 8;
|
|
for _ in 0..8 {
|
|
if crc & 0x8000 != 0 {
|
|
crc = (crc << 1) ^ 0x1021;
|
|
} else {
|
|
crc <<= 1;
|
|
}
|
|
}
|
|
}
|
|
crc
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn check_value_matches_spec() {
|
|
// PROTOCOL.md §2.2: CRC-16/CCITT-FALSE of ASCII "123456789" is 0x29B1.
|
|
assert_eq!(crc16(b"123456789"), 0x29B1);
|
|
}
|
|
|
|
#[test]
|
|
fn empty_input_is_initial_value() {
|
|
// No reflection, no XOR-out: empty input leaves the init value untouched.
|
|
assert_eq!(crc16(&[]), 0xFFFF);
|
|
}
|
|
|
|
#[test]
|
|
fn single_zero_byte() {
|
|
// 0xFFFF XOR 0x0000 = 0xFFFF, then 8 shift-and-maybe-xor rounds.
|
|
// Worked out by hand against the reference impl in Appendix B.
|
|
assert_eq!(crc16(&[0x00]), 0xE1F0);
|
|
}
|
|
|
|
#[test]
|
|
fn differs_from_kermit_variant() {
|
|
// CRC-16/KERMIT of "123456789" is 0x2189 — if we ever accidentally
|
|
// swap init or reflection, this vector catches it.
|
|
assert_ne!(crc16(b"123456789"), 0x2189);
|
|
}
|
|
}
|