94 lines
3.5 KiB
TypeScript
94 lines
3.5 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { escapeBuffer, unescapeBuffer, crc16Ccitt, verifyAndStripFcs, computeFcs, encodeHDLC, appendFcsLE, bitStuffBits } from '../src/hdlc';
|
|
|
|
describe('HDLC.escapeBuffer', () => {
|
|
it('escapes and unescapes special bytes', () => {
|
|
const raw = new Uint8Array([0x01, 0x7e, 0x7d, 0x02]);
|
|
const e = escapeBuffer(raw);
|
|
const u = unescapeBuffer(e);
|
|
expect(Array.from(u)).toEqual(Array.from(raw));
|
|
});
|
|
});
|
|
|
|
describe('HDLC.computeFcs', () => {
|
|
it('crc16 and verify works', () => {
|
|
const payload = new Uint8Array([1, 2, 3, 4, 5]);
|
|
const fcs = computeFcs(payload);
|
|
const buf = new Uint8Array([...payload, fcs & 0xff, (fcs >> 8) & 0xff]);
|
|
const res = verifyAndStripFcs(buf);
|
|
expect(res.ok).toBe(true);
|
|
expect(Array.from(res.payload || [])).toEqual(Array.from(payload));
|
|
});
|
|
|
|
it('CRC/X-25 standard test string', () => {
|
|
const payload = new TextEncoder().encode('123456789');
|
|
const pre = crc16Ccitt(payload);
|
|
expect(pre).toBe(0x29b1);
|
|
const fcs = computeFcs(payload);
|
|
expect(fcs).toBe(0xd64e);
|
|
const withFcs = appendFcsLE(payload);
|
|
// transmitted as little-endian bytes: low then high
|
|
expect(withFcs[withFcs.length - 2]).toBe(0x4e);
|
|
expect(withFcs[withFcs.length - 1]).toBe(0xd6);
|
|
});
|
|
});
|
|
|
|
describe('HDLC.bitStuffBits', () => {
|
|
it('bit stuffing inserts a zero after five 1s', () => {
|
|
const rawBits = '01111111';
|
|
const stuffed = bitStuffBits(rawBits);
|
|
expect(stuffed).toBe('011111011');
|
|
});
|
|
});
|
|
|
|
describe('HDLC.encodeHDLC', () => {
|
|
it('HDLC framing wraps with FLAG and escapes bytes', () => {
|
|
const raw = new Uint8Array([0x7e, 0x7d, 0x11]);
|
|
const framed = encodeHDLC(raw);
|
|
expect(framed[0]).toBe(0x7e);
|
|
expect(framed[framed.length - 1]).toBe(0x7e);
|
|
// internal must not contain raw FLAG (0x7e); ESC (0x7d) will appear as escape
|
|
const inner = framed.slice(1, framed.length - 1);
|
|
for (const b of inner) expect(b !== 0x7e).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('HDLC.verifyAndStripFcs', () => {
|
|
it('CRC appends low byte then high byte and verify strips it', () => {
|
|
const payload = new Uint8Array([0x10, 0x20, 0x30]);
|
|
const fcs = computeFcs(payload);
|
|
const buf = new Uint8Array([...payload, fcs & 0xff, (fcs >> 8) & 0xff]);
|
|
const res = verifyAndStripFcs(buf);
|
|
expect(res.ok).toBe(true);
|
|
expect(res.fcs).toBe(fcs);
|
|
expect(Array.from(res.payload || [])).toEqual(Array.from(payload));
|
|
});
|
|
});
|
|
|
|
describe('HDLC.APRS', () => {
|
|
it('APRS UI-frame example and HDLC encoding', () => {
|
|
const destHex = [0x82, 0xa0, 0xa4, 0xa6, 0x40, 0x40, 0x60];
|
|
const srcHex = [0x9c, 0x6c, 0xa0, 0x8e, 0x40, 0x40, 0x61];
|
|
const infoStr = '!4540.00N/12300.00W-';
|
|
const info = new TextEncoder().encode(infoStr);
|
|
const parts: number[] = [];
|
|
parts.push(...destHex, ...srcHex);
|
|
parts.push(0x03, 0xf0);
|
|
parts.push(...info);
|
|
const ax25 = Uint8Array.from(parts);
|
|
|
|
// compute HDLC frame with FCS
|
|
const hdlc = encodeHDLC(ax25, { includeFcs: true });
|
|
|
|
// sanity checks: starts/ends with flag and contains escaped data
|
|
expect(hdlc[0]).toBe(0x7e);
|
|
expect(hdlc[hdlc.length - 1]).toBe(0x7e);
|
|
// parse the AX.25 portion back from the unescaped frame
|
|
const inner = hdlc.slice(1, hdlc.length - 1);
|
|
// unescapeBuffer is internal; use HDLCDeframer to extract - simpler: verify FCS appended
|
|
const withFcs = appendFcsLE(ax25);
|
|
// last two bytes of withFcs are little-endian FCS
|
|
expect(withFcs[withFcs.length - 2]).toBe((computeFcs(ax25) & 0xff));
|
|
});
|
|
});
|