# MOSAIC alpha operator quickstart

Primary alpha path: `mosaic-modem`.
HF audio companion: `mosaic-hf-audio`.

## Practice before connecting a radio

The installed wheel includes a complete hardware-free practice station:

```bash
mosaic-modem demo
```

This opens the real operator console at `http://127.0.0.1:8088/`, creates
simulated channel activity and decoded traffic, and listens for KISS TCP data
frames on `127.0.0.1:8001`. It does not open radio hardware and cannot transmit
RF. Enable simulated TX, select `VA6DEMO-2`, open a QSO, exchange text, send 73,
close the QSO, and inhibit TX.

If either local port is occupied, choose free ports:

```bash
mosaic-modem demo --ui-port 8188 --kiss-port 8101
```

Attach the packaged keyboard client while practice mode is running:

```bash
mosaic-console --port 8001
```

## First HF audio QSO

For an ordinary transceiver sound interface, MOSAIC's short operator command
is `qso`: name your callsign, the station you expect, and the sound device to
use.  It starts the local browser console and listens immediately; TX remains
off until you deliberately enable it in the console.

```bash
mosaic-hf-audio qso \
  --callsign VE6SLP --peer VA6GA-2 \
  --input-device 13 --output-device 13
```

Optionally add `--first-contact` to this command. It performs a PTT-free local
converter-loopback check before serving: useful for confirming the selected
audio path, but it does not key PTT or transmit RF.

Find the device number first, if needed:

```bash
mosaic-hf-audio devices
```

That is the whole normal HF station command. Add `--ptt-port /dev/ttyUSB0`
only when your interface needs serial PTT. Advanced receive windows, worker
counts, rates, and KISS ports remain available through `mosaic-hf-audio serve
--help`, but are not required to get started.

### Watch, receive, then take a turn

In the browser, **Add station** creates a session-only receive watch entry. It
does not open a QSO, arm TX, or persist after the console stops. The receiver
uses the new entry on its next receive window; an in-progress capture keeps its
existing candidate snapshot. Open **RF details and receiver diagnostics** for
receive/decode counts, receiver health, spectrum/waterfall, and the front-end
IQ cloud (not a symbol constellation).

MOSAIC is half duplex on a shared channel: each cycle receives, then sends at
most one queued logical message. Additional queued messages wait for the next
receive window rather than becoming a back-to-back operator burst.

KISS listens on TCP port 8001 by default. Inside each KISS frame, the protocol
port-number field is 0 and the command is Data. The client payload is UTF-8
application text. MOSAIC adds station addressing and radio framing.

For a short waveform-only check:

```bash
mosaic-hf packet-produce --output hello.wav --payload-hex 48656c6c6f --signature 1234
mosaic-hf packet-consume --input hello.wav --signature 1234
```

The second command reports a CRC-valid payload containing `48656c6c6f`
(`Hello`).

## Realtime operator console

The modem serves a Linux-first local browser console aimed at RF operators:

- live spectrum, bounded waterfall, and front-end IQ cloud
- decoder stages from capture through addressed CRC-valid delivery
- decoded-station roster and channel transcript
- FT8-inspired CQ, select, open, exchange, 73, and close flow
- explicit TX confirmation and one-click return to fail-closed receive
- per-QSO `Auto / Basic / Fast / Turbo` rate control with the selection reason
- receiver health, decode backlog, and capture realtime ratio

```bash
# default panel: http://127.0.0.1:8088/
mosaic-modem \
  --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. Receive begins immediately, but TX stays fail-closed until
**TX INHIBITED** is clicked and the confirmation is accepted. Select a decoded
station, open the QSO, use a template or type plain text, then send 73 and close.
Return the transmitter to **TX INHIBITED** when finished. Use `--ui-port 0` to
disable the console. The HTTP API remains bound to `127.0.0.1` by default.

The IQ plot is deliberately labelled a front-end sample cloud, not a symbol
constellation: MOSAIC uses noncoherent FSK, for which a conventional symbol
constellation would be misleading.

### Adaptive mixed-rate mode

Add `--adaptive-mcs` to enable the Version-2 profile bank on both transmit and
receive. UHF then offers Basic (800 coded bit/s), Fast (1600), and Turbo (3200)
inside the same approximately 40 kHz channel. HF offers Basic (50), Chat (100),
and Fast (200) inside the same SSB-width channel.

```bash
mosaic-modem \
  --callsign VE6SLP --ssid 1 --peer VA6GA-2 \
  --profile uhf-live --adaptive-mcs --mcs-mode auto \
  --frequency-hz 431200000 --fpga hostedx115-latest.rbf \
  --txvga2-db 10 --execute
```

Auto starts at Basic until peer capabilities are known, promotes only after
three high-margin CRC-valid frames, and falls back immediately on a failed
expected reply. The Link rate control may force a profile for testing. Legacy
Version-1 operation remains the default when `--adaptive-mcs` is absent.

The retained three-radio gate recovered UHF Basic/Fast/Turbo 3/3 and HF
Basic/Chat/Fast 3/3 when the HF family was transported over a UHF carrier.
Both captures measured 100% active-span occupancy. The latter validates the
waveform and receiver only; it is not an HF-band propagation result. See
`site/assets/adaptive-mcs-rf-evidence.png`.

HF collision receive, including adaptive mixed-rate receive, automatically
applies bounded impulse limiting and persistent-carrier suppression before
acquisition. No operator control is required. The front end is intentionally
narrow: at most three full-span coherent lines at least 24 dB above the
in-band spectral median are removed, and only samples beyond an eight-sigma
MAD threshold are limited. It is proven only in finite software simulations
for CW-like QRM and impulsive QRN; it is not a general noise blanker or an
on-air HF performance claim.

## 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 |
| `--tx-access-jitter-s` | HF ≈1 packet / UHF 0.25 | uncoordinated ALOHA-style pre-TX hold; raise to several packet durations on congested HF nets |
| `--tx-diversity-repeats` | `1` | set `2`+ to re-radiate later; receiver peels extra same-signature copies after CRC |

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 LIVE** 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
mosaic-modem 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
mosaic-modem \
  --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
mosaic-modem \
  --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
mosaic-modem \
  --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 one-shot HF audio commands keep the legacy shared lab signature by default.
The continuous service derives each station's access signature from its
callsign, matching the live UHF modem.

List sound devices:

```bash
mosaic-hf-audio devices
```

Run a continuous operator station:

```bash
mosaic-hf-audio \
  --callsign VE6SLP --ssid 1 \
  --ptt-port /dev/ttyUSB0 --ptt-line rts \
  serve --peer VA6GA-2 \
  --input-device 13 --output-device 13 \
  --listen-port 8001 --ui-port 8088
```

Receive begins immediately. The browser console and KISS interface are the same
ones used by `mosaic-modem`. Sound output and PTT remain inhibited until TX is
armed in the browser. Repeat `--peer` to add each callsign the receiver should
search. Set `--ui-port 0` for a headless KISS-only service. Peer callsigns
deterministically select their access signatures, so no separate signature
exchange is needed.

The browser can address any configured peer. A generic KISS data frame has no
destination field, so KISS-originated text is addressed to the first
`--peer`; run separate service instances when independent KISS applications
need different fixed destinations.

Install the sound-card and serial-PTT dependencies with:

```bash
python -m pip install "mosaic-hf[audio]"
```

Listen continuously:

```bash
mosaic-hf-audio \
  --callsign VE6SLP --ssid 1 \
  receive --input-device 13 --seconds 30 --continuous
```

Send one addressed text message:

```bash
mosaic-hf-audio \
  --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
mosaic-hf-audio \
  --callsign VE6SLP --ssid 1 \
  --destination VA6GA-2 \
  --amplitude 0.9 \
  loopback --device 13 --repeat 3 \
  --text "VE6SLP-1 DE VA6GA-2 QSL 599 K"
```
