import { describe, it, expect } from "vitest"; import { tokensToOnsets, textToSchedule, onsetsToMorse, decodeOnsets, } from "../../lib/wire.js"; import { morseToTokens } from "../../lib/morse.js"; import { makeTiming } from "../../lib/timing.js"; const T = makeTiming(120); // dah=240 elem=480 letter=960 word=1680 describe("tokensToOnsets", () => { it("dot = 1 impulse", () => { expect(tokensToOnsets(["dot"], T)).toEqual([0]); }); it("dash = 2 impulses spaced by dahGap", () => { expect(tokensToOnsets(["dah"], T)).toEqual([0, T.dahGapMs]); }); it("A (.-) spaces the dash element after elemGap from the dot", () => { // dot@0, elemGap, dah@(elem), dah2@(elem+dah) expect(tokensToOnsets(morseToTokens(".-"), T)).toEqual([ 0, T.elemGapMs, T.elemGapMs + T.dahGapMs, ]); }); }); describe("onsetsToMorse round-trip (ideal timing)", () => { const samples = ["SOS", "HELLO WORLD", "HI YOU", "CQ CQ", "E T 1 9", "ABCDE"]; for (const text of samples) { it(`recovers "${text}"`, () => { const { morse, onsets } = textToSchedule(text, T); expect(onsetsToMorse(onsets, T)).toBe(morse); expect(decodeOnsets(onsets, T).text).toBe(text); }); } }); describe("onsetsToMorse with timing jitter", () => { // Deterministic pseudo-jitter (no Math.random): perturb each onset by a // bounded sawtooth. Should still decode because gap classes are well // separated (geometric midpoints). function jitter(onsets, maxMs) { return onsets.map((t, i) => t + (((i * 37) % 11) - 5) / 5 * maxMs); } it("decodes SOS with +-30ms jitter", () => { const { onsets, text } = { ...textToSchedule("SOS", T) }; const jittered = jitter(onsets, 30); expect(decodeOnsets(jittered, T).text).toBe("SOS"); expect(text).toBeUndefined; // sanity: textToSchedule has no .text }); it("decodes a sentence with +-40ms jitter", () => { const { onsets } = textToSchedule("HELLO WORLD", T); expect(decodeOnsets(jitter(onsets, 40), T).text).toBe("HELLO WORLD"); }); }); describe("edge cases", () => { it("empty onsets -> empty", () => { expect(onsetsToMorse([], T)).toBe(""); expect(decodeOnsets([], T)).toEqual({ morse: "", text: "" }); }); it("single impulse -> E", () => { expect(decodeOnsets([0], T).text).toBe("E"); }); });