No description
Find a file
Alec Perkins 7e1074647b
Some checks failed
tests / pytest (push) Failing after 9s
Dedicate to the public domain (CC0) with LLM-authorship notice
This repo was produced primarily by an LLM, so most of it is not
eligible for copyright protection to begin with. CC0 covers any
portion that turns out to be copyrightable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 21:29:38 -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
LICENSE Dedicate to the public domain (CC0) with LLM-authorship notice 2026-05-07 21:29:38 -04:00
notes.md Implement test/control stations, simulator, tests 2026-05-07 21:27:41 -04:00
pyproject.toml Dedicate to the public domain (CC0) with LLM-authorship notice 2026-05-07 21:29:38 -04:00
README.md Dedicate to the public domain (CC0) with LLM-authorship notice 2026-05-07 21:29:38 -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.

Authorship & copyright. This repository was produced primarily by a large language model (Claude). LLM-generated output is generally not eligible for copyright in the US and similar jurisdictions, so this code is in the public domain. To the extent any part is copyrightable, it is dedicated to the public domain via CC0 1.0.

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).