94 lines
3.4 KiB
Markdown
94 lines
3.4 KiB
Markdown
|
|
# 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).
|