morse-code / tests /unit /wire.test.js
RemiFabre
feat: initial Reachy Mini Morse Code app
843a4b2
Raw
History Blame Contribute Delete
2.5 kB
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");
});
});