# MOSAIC alpha operator quickstart

Primary alpha path: `tools/mosaic_modem.py`.
HF audio companion: `tools/mosaic_hf_audio.py`.

## Radio-engineer UI

The modem serves a browser panel aimed at RF operators, not log scrapers:

- callsign / peer / frequency / TX inhibit
- live QSO transcript
- channel energy strip
- **receiver health**: keeping-up flag, decode backlog, capture realtime ratio
- Enable TX / Inhibit TX / Send

```bash
# default panel: http://127.0.0.1:8088/
python tools/mosaic_modem.py \
  --callsign VE6SLP --ssid 1 --peer VA6GA-2 \
  --profile uhf-live --frequency-hz 431200000 \
  --fpga hostedx115-latest.rbf --txvga2-db 10 \
  --execute --ui-port 8088
```

Open the printed URL. TX stays fail-closed until **Enable TX**. Use `--ui-port 0`
to disable the panel. Status JSON still includes a `realtime` block for scripts.

## Continuous demod

Live receive is pipelined: capture runs back-to-back while workers decode earlier
windows. Defaults favour keeping up with the RF stream:

| Setting | Default | Why |
|---|---|---|
| `--profile` | `uhf-live` | same UHF waveform, tighter CFO (±500 Hz) |
| `--decode-workers` | `2` | fewer processes, more threads each |
| `--acquisition-threads` | `4` | finish one cropped window near real time |
| `--max-decode-backlog` | workers+1 | drop late windows instead of growing forever |

Squelch + burst crop skip silence. Decode uses a fast per-signature packet walk
first; full SIC runs only when that finds nothing. Check
`realtime.keeping_up` in status, or the green/amber **RX STREAM** pill in the UI.

Prove the offline path:

```bash
python tools/uhf_realtime_check.py --seconds 30 --mode live --workers 2
```

## UHF SDR: list radios

```bash
python tools/mosaic_modem.py devices
```

## UHF SDR: start one operator station

This owns the live radios on **431.200 MHz**. `--execute` enables live receive.
Transmit stays **inhibited by default**. For bladeRF TX, keep `--txvga2-db 10`
or lower and pin the FPGA image with `--fpga hostedx115-latest.rbf`.

```bash
python tools/mosaic_modem.py \
  --callsign VE6SLP --ssid 1 \
  --peer VA6GA-2 --peer VE6NAS-3 \
  --radio bladerf-hackrf --profile uhf-live \
  --frequency-hz 431200000 \
  --fpga hostedx115-latest.rbf \
  --txvga2-db 10 \
  --execute --operator-stdin --human-events --status-interval-s 0
```

Decoded traffic prints as:

```text
[12:34:56Z] <<< VA6GA-2 -> VE6SLP-1: QSL 599 K
```

## What the operator types to QSO

```text
/help
/to VA6GA-2
/tx on
CQ CQ DE VE6SLP-1
VA6GA-2> QSL 599 K
/tx off
```

Notes:
- Plain text sends to the current default destination.
- `DESTINATION> message` overrides the default for one line.
- `/tx off` returns the station to fail-closed receive-only mode.
- The browser panel can queue the same traffic without the text console.

## Three-radio lab setup this week

Station A uses bladeRF TX + HackRF RX:

```bash
python tools/mosaic_modem.py \
  --callsign VE6SLP --ssid 1 \
  --peer VA6GA-2 \
  --radio bladerf-hackrf --profile uhf-live \
  --frequency-hz 431200000 \
  --fpga hostedx115-latest.rbf \
  --txvga2-db 10 \
  --execute --ui-port 8088 --human-events --status-interval-s 0
```

Station B uses the B210:

```bash
python tools/mosaic_modem.py \
  --callsign VA6GA --ssid 2 \
  --peer VE6SLP-1 \
  --radio usrp --usrp-device-args type=b200 \
  --profile uhf-live --frequency-hz 431200000 \
  --execute --ui-port 8089 --human-events --status-interval-s 0
```

That is the current three-radio bench: bladeRF, HackRF, and one B210.

## HF audio path

The HF audio path keeps one shared lab signature by default and carries station
identity in the addressed payload.

List sound devices:

```bash
python tools/mosaic_hf_audio.py devices
```

Listen continuously:

```bash
python tools/mosaic_hf_audio.py \
  --callsign VE6SLP --ssid 1 \
  receive --input-device 13 --seconds 30 --continuous
```

Send one addressed text message:

```bash
python tools/mosaic_hf_audio.py \
  --callsign VE6SLP --ssid 1 \
  --destination VA6GA-2 \
  transmit --output-device 13 \
  --text "VE6SLP-1 DE VA6GA-2 QSL 599 K"
```

Converter loopback before touching a rig:

```bash
python tools/mosaic_hf_audio.py \
  --callsign VE6SLP --ssid 1 \
  --destination VA6GA-2 \
  --amplitude 0.9 \
  loopback --device 13 --repeat 3 \
  --text "VE6SLP-1 DE VA6GA-2 QSL 599 K"
```
