modjam/README.md

94 lines
3.4 KiB
Markdown
Raw Normal View History

# mesh-control
Operator quickstart for the LoRa modulation jam test framework. Protocol/spec lives in [reference/concept.md](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)
```sh
python3.11 -m venv .venv && source .venv/bin/activate
pip install -e .
```
Drop a config at `~/modjam-config.json`. Use [sim/station-a.json](sim/station-a.json) as a template; add `"port": "/dev/ttyUSB0"` (Pi) or `"/dev/tty.usbmodem1301"` (Mac) for the attached MeshCore node.
Run:
```sh
modjam-station # on each Pi
modjam-control # on the Mac
```
## Simulator
Three stations + control, no hardware:
```sh
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/A.tsv) / [reference/B.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.
```sh
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](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
```sh
pip install -e '.[test]'
pytest
```
CI runs the same suite via [.forgejo/workflows/test.yml](.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](reference/concept.md) and [notes.md](notes.md) for the protocol).