From d8607cb0002f305b8c03c76a281b64aa1226c48e Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Wed, 27 May 2026 15:06:53 -0400 Subject: [PATCH] add README documenting Makefile targets and build workflow --- README.md | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..fb885ec --- /dev/null +++ b/README.md @@ -0,0 +1,140 @@ +# zebra-report + +Covert peer-to-peer chat over PulseAudio sink-input volume. +Userland only. No kernel module. No network packets carry the chat content. +Public domain. + +## Build & run + +Single entry point: `make`. Every workflow goes through a target — never +invoke `gcc` or `python3` by hand. + +```bash +make # builds all five binaries (default target = `all`) +make test # builds and runs unit tests (pure logic, no PA needed) +make test-all # unit + integration + functional (PA + sink-inputs required) +make blog # rebuilds the static blog under web/blog/ +make serve # builds blog, serves web/ on http://127.0.0.1:8765 +make clean # removes binaries and generated blog output +``` + +`make` is the contract. If a workflow isn't a target, add the target before +adding the workflow. + +## Dependencies + +``` +gcc, make, pkg-config # build chain +libpulse-dev # PulseAudio client lib (pkg-config: libpulse) +python3 # blog builder + dev server +``` + +Ubuntu / Debian: + +```bash +sudo apt install build-essential pkg-config libpulse-dev python3 +``` + +`make` checks libpulse via `pkg-config --cflags libpulse` and +`pkg-config --libs libpulse`; if pkg-config can't find it, the build fails +early with a clear message. + +## Targets + +### `make all` — five binaries + +| Binary | Source | Role | +|-----------|----------------|------| +| `tx` | `src/tx.c` | reads stdin, transmits via volume modulation | +| `rx` | `src/rx.c` | reads target sink-input volumes, decodes to stdout | +| `chat` | `src/chat.c` | bidirectional tx+rx, line-based chat UI | +| `bt` | `src/bt.c` | "battle toads" dual-channel stereo UART (2× throughput) | +| `carrier` | `src/carrier.c`| publishes a silent PA sink so volume reads have something to read | + +All link against `libpulse`, `librt`, and `libpthread`. Headers come from +`include/zebra.h` (protocol constants) and `include/modem.h` (inline encode/ +decode + benchmark). + +### `make test` — unit tests + +``` +test/unit pure logic, no PA, no audio +``` + +Covers: signal encoding (`bit_to_vol` / `vol_to_bit`), timing arithmetic +(`ts_add_ns` with overflow), baud math (`baud_from_avg_ns`), handshake frame +build/parse for both OFFER and READY (magic, checksum, corruption). + +Currently: **84 cases pass.** Required to be green before any commit. + +### `make test-all` — integration + functional + +Integration and functional tests need a running PulseAudio daemon and +real sink-inputs to operate on. They are not run by `make test` by default. + +```bash +# integration tests need ONE sink-input +ZEBRA_TEST_SINK= ./test/integration + +# functional tests need TWO sink-inputs (data + ctrl channels) +ZEBRA_DATA_SINK= ZEBRA_CTRL_SINK= ./test/functional +``` + +Find sink-input indices with `pactl list sink-inputs short` — the first +column is the index. The `test/integration` benchmark sub-test reports +measured baud for the current host. + +To avoid disrupting real audio applications, spawn dedicated silent +sink-inputs as test targets: + +```bash +paplay --raw --format=s16le --rate=44100 --channels=1 \ + --stream-name=zebra-test /dev/zero & +# then use `pactl list short sink-inputs` to find this stream's index +``` + +Currently: **17/17 integration pass; 8/10 functional pass.** The two +functional failures are timing-bound on non-realtime kernels at the +auto-negotiated baud and are not code defects (the same data path passes +at fixed 50 baud). + +### `make blog` & `make serve` + +`make blog` runs `python3 blog/build.py`, which reads Markdown sources from +`blog/posts/` and writes static HTML into `web/blog/`. `make serve` rebuilds +the blog and serves `web/` on port 8765 for local preview. + +The chat UI (`web/chat.html`) is plain static HTML and works directly under +`make serve` — open `http://127.0.0.1:8765/chat.html`. + +### `make clean` + +Removes: + +``` +tx rx chat bt carrier +test/unit test/integration test/functional +web/blog/index.html +web/blog/001-volume-modem/ +web/blog/002-sse-chatroom/ +``` + +Source files, headers, fonts, recovered binaries on disk, and committed +artifacts under `web/` (other than the generated blog) are not touched. + +## Layout + +``` +include/ protocol constants + inline encode/decode/benchmark +src/ five binaries: tx, rx, chat, bt (battle toads), carrier +test/ unit (pure), integration (PA needed), functional (full chain) +blog/ markdown sources + python build +web/ static site: index, kernel, chat, fonts, generated blog +Makefile every workflow lives here +CLAUDE.md agent operating rules for this repo +``` + +## License + +Public domain. Patches gratefully accepted via merge request at +`git.unturf.com/engineering/unturf/zebra-report`.