|
All checks were successful
tests / pytest (push) Successful in 1m24s
Two related changes to make sender/receiver logs analyzable:
1. MeshCoreRadio.send no longer returns until the radio firmware
reports the packet was actually transmitted. send_chan_msg only
waits for EventType.OK (firmware accepted bytes) — no
tx-complete event is pushed for channel messages, so we poll
commands.get_stats_packets() and wait for nb_sent to strictly
increment past a pre-send baseline. Times out after 30s.
2. Each test packet now embeds a random 8-hex-char token as the
4th comma-separated field of the prefix, distinct from any
radio-assigned packet id. Receivers extract it from the text
and write it on every TSV row, so a sender's queued/sent rows
tie 1:1 to each receiver's record without depending on
whatever packet id the radio surfaces on each side.
TSV gains a new column (3rd, after packet_id):
queued: ts queued <id> <token> <freq,bw,sf,cr,pow>
sent: ts sent <id> <token> <duration_ms> <text>
received: ts received <id> <token> <text>
Missing token writes "-".
UDP simulator regex updated to accept both legacy 3-field test
payloads and the new 4-field format with token.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| .forgejo/workflows | ||
| docker | ||
| modjam | ||
| reference | ||
| sim | ||
| tests | ||
| .gitignore | ||
| .gitmodules | ||
| docker-compose.yml | ||
| LICENSE | ||
| notes.md | ||
| pyproject.toml | ||
| README.md | ||
| requirements.txt | ||
Modulation Jam
Operator quickstart for the LoRa modulation jam test framework. Protocol/spec lives in reference/concept.md.
Authorship & copyright. After 7424416b58dbbcdb490a099695cd46ecfe44d5fb, 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 composewith 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).