zebra-report/Makefile
Russell Ballestrini c8a755513f add zebrad: pulseaudio→websocket introspector for chat.html
single C file (~620 LOC), no third-party deps beyond libpulse.
closes a receive loop for web/chat.html: a browser tab cannot read
another tab's PulseAudio state, so each peer runs zebrad locally
to forward decoded frames to ws://127.0.0.1:7777.

components inline:
  * SHA-1 (RFC 3174) + base64 encode — for the WebSocket handshake;
    verified against the canonical RFC 6455 example
    (key dGhlIHNhbXBsZSBub25jZQ== → accept s3pPLMBiTxaQ9kYGzzhZRbK+xOo=)
  * CRC-32 (IEEE 802.3) — verifies DATA + HELLO frame payloads
  * UART symbol decoder with adaptive threshold:
    running peak with 2-second half-life decay, threshold = 50% of peak,
    falling-edge start detect, sample at 1.5..8.5 × sps from edge
  * frame assembler: magic-sync ZB, type dispatch OFFER/READY/DATA/HELLO,
    overflow + corrupt-length guards, resync on bad checksum
  * embedded WebSocket server: TCP listener on 127.0.0.1:7777,
    handshake responder, binary frames only (opcode 0x82), no masking
    (server→client), any inbound data closes the client (browser
    auto-reconnects); max 16 concurrent clients
  * pulseaudio: pa_mainloop integration, record stream on @DEFAULT_MONITOR@
    by default, PA_SAMPLE_FLOAT32LE mono @ 8 kHz, 10 ms fragsize, peak
    energy extracted from |abs(sample)|

adaptive baud: starts at ZEBRA_BAUD_HANDSHAKE (50). On valid READY frame
locks in negotiated baud from payload. Matches the chat.html send path.

binds 127.0.0.1 only — never reachable from the LAN, even though the
chat.html page itself may be served over HTTPS from a public domain.

smoke tested: PA connect + stream ready + WS handshake (RFC example
verified) + clean shutdown. -Wall -Wextra clean.

Makefile: zebrad now part of `make all`; clean target updated.
README: new section documenting it.
2026-05-27 15:57:22 -04:00

57 lines
1.5 KiB
Makefile

CC = gcc
CFLAGS = -Wall -Wextra -O2 -Iinclude $(shell pkg-config --cflags libpulse)
LDFLAGS = $(shell pkg-config --libs libpulse) -lrt -lpthread
PULSE = src/pulse.c
.PHONY: all clean serve blog test test-all zebrad
all: tx rx chat bt carrier zebrad
test: test/unit
@./test/unit
test-all: test/unit test/integration test/functional
@echo "--- unit ---"
@./test/unit
@echo "--- integration ---"
@./test/integration
@echo "--- functional ---"
@./test/functional
test/unit: test/unit.c include/zebra.h include/modem.h test/test.h
$(CC) $(CFLAGS) -o $@ test/unit.c
test/integration: test/integration.c src/pulse.c include/zebra.h include/modem.h test/test.h
$(CC) $(CFLAGS) -o $@ test/integration.c src/pulse.c $(LDFLAGS)
test/functional: test/functional.c src/pulse.c include/zebra.h include/modem.h test/test.h
$(CC) $(CFLAGS) -o $@ test/functional.c src/pulse.c $(LDFLAGS)
tx: src/tx.c $(PULSE)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
rx: src/rx.c $(PULSE)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
chat: src/chat.c $(PULSE)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
bt: src/bt.c $(PULSE)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
carrier: src/carrier.c
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
zebrad: src/zebrad.c include/zebra.h
$(CC) $(CFLAGS) -o $@ src/zebrad.c $(LDFLAGS) -lm
blog:
python3 blog/build.py
serve: blog
cd web && python3 -m http.server 8765
clean:
rm -f tx rx chat bt carrier zebrad test/unit test/integration test/functional
rm -rf web/blog/001-volume-modem web/blog/002-sse-chatroom web/blog/index.html