No description
Find a file
Alec Perkins 0f478bf720 Implement test/control stations, simulator, tests
Adds the modjam package: a MeshCore-backed test station service for
Pi (IDLE + RUNNING states, cuesheet-driven), a control station REPL
for the Mac, and a UDP simulator that swaps in for the radio when
SIMULATOR=true (drops cross-config packets and a configurable
fraction of test-payload datagrams to mimic real radio loss).
docker-compose runs three sim stations + control on a bridge net.
TSV log format matches the reference traces.

Pytest suite covers protocol codec, cuesheet builder, TSV logger,
config loader, and UDP radio packet routing/loss; .forgejo/workflows
runs it on push to main and on PRs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 21:27:41 -04:00
.forgejo/workflows Implement test/control stations, simulator, tests 2026-05-07 21:27:41 -04:00
docker Implement test/control stations, simulator, tests 2026-05-07 21:27:41 -04:00
modjam Implement test/control stations, simulator, tests 2026-05-07 21:27:41 -04:00
reference Reorg for mesh control-based version 2026-05-07 20:49:00 -04:00
sim Implement test/control stations, simulator, tests 2026-05-07 21:27:41 -04:00
tests Implement test/control stations, simulator, tests 2026-05-07 21:27:41 -04:00
.gitignore Implement test/control stations, simulator, tests 2026-05-07 21:27:41 -04:00
docker-compose.yml Implement test/control stations, simulator, tests 2026-05-07 21:27:41 -04:00
notes.md Implement test/control stations, simulator, tests 2026-05-07 21:27:41 -04:00
pyproject.toml Implement test/control stations, simulator, tests 2026-05-07 21:27:41 -04:00
README.md Implement test/control stations, simulator, tests 2026-05-07 21:27:41 -04:00
requirements.txt Reorg for mesh control-based version 2026-05-07 20:49:00 -04:00

mesh-control

Operator quickstart for the LoRa modulation jam test framework. Protocol/spec lives in reference/concept.md.

Components

  • modjam-station — runs on each Test Station (Raspberry Pi or simulator container) talking to a MeshCore node over USB serial.
  • modjam-control — interactive REPL on the macbook (or simulator container) that sends START/STOP commands.
  • Simulator — docker compose with three test stations (A, B, C) and one control container, swapping the radio for UDP.

Install (host: Pi or Mac)

python3.11 -m venv .venv && source .venv/bin/activate
pip install -e .

Drop a config at ~/modjam-config.json. Use sim/station-a.json as a template; add "port": "/dev/ttyUSB0" (Pi) or "/dev/tty.usbmodem1301" (Mac) for the attached MeshCore node.

Run:

modjam-station                # on each Pi
modjam-control                # on the Mac

Simulator

Three stations + control, no hardware:

docker compose build
docker compose up -d station-a station-b station-c
docker compose run --rm control

Inside the control REPL:

> start name=sim1 stations=A,B,C bw=500 sf=8 cr=5 pow=22 duration=20 padding=10 spacing=2 size=40 at=1
> stop
> quit

TSV logs appear in ./sim/logs/ (one per station per run, format matches reference/A.tsv / reference/B.tsv).

SIMULATOR=true is what flips the radio backend from MeshCore-USB to UDP. The UDP backend drops cross-config datagrams, so receivers only "hear" senders whose freq/bw/sf/cr/channel match — same behavior as real radios.

The simulator also drops a fraction of received test packets at random to mimic real-world packet loss. Default is 15%; override with SIM_PACKET_LOSS=0.0 (no loss) up to 1.0. Protocol traffic (START/STOP, heartbeats, next/done) is never dropped — only payloads matching the test-packet pattern. Each station gets a deterministic per-station RNG seed so reruns are repeatable; set SIM_SEED=... to vary.

SIM_PACKET_LOSS=0.3 docker compose up -d station-a station-b station-c

REPL commands

Command Effect
start [k=v ...] encode and broadcast a START command (see reference/concept.md for keys: name, f, bw, sf, cr, pow, size, stations, duration, padding, spacing, at)
stop multicast STOP — running stations return to IDLE
help show command list
quit exit

Layout

modjam/
├── cli.py            # entrypoints
├── config.py         # ~/modjam-config.json loader
├── protocol.py       # `<cmd>[:<station>]|k:v,…` codec + heartbeat/next/done formatters
├── cuesheet.py       # build cuesheet from START params
├── station.py        # IDLE/RUNNING state machine
├── control.py        # REPL + rx tail
├── log.py            # TSV logger
└── radio/
    ├── base.py       # Radio ABC
    ├── meshcore.py   # MeshCore over USB serial
    ├── udp.py        # simulator radio
    └── factory.py    # picks backend from SIMULATOR env

Tests

pip install -e '.[test]'
pytest

CI runs the same suite via .forgejo/workflows/test.yml on push to main and on PRs.

Scope

v1: IDLE + RUNNING only. RESULTS / DOWNLINKING is not implemented yet (see reference/concept.md and notes.md for the protocol).