Skip to content

VAFM ↔ DMR Bridge

vafm-homebrew-bridge carries voice between a VAFM reflector and a DMR network that uses the MMDVM Homebrew protocol. The DMR side can be a Vexillum DMR instance or an outside Homebrew master.

It is the only bridge that transcodes audio. VAFM carries Opus and DMR carries AMBE+2, so every call is decoded and re-encoded.

DMR  -> OpenDMR decoder -> 8 kHz PCM -> 48 kHz Opus          -> VAFM
VAFM -> Opus decoder    -> 8 kHz PCM -> OpenDMR or BLIP25 encoder -> DMR

Calls from DMR are decoded with OpenDMR, resampled, encoded as Opus, and sent to VAFM. Calls from VAFM are decoded, resampled to 8 kHz, encoded as AMBE+2, built into DMR voice bursts, and sent over the Homebrew connection.

VAFM calls are sent to DMR with the source ID, destination, slot, and call type you configure. They are not routed based on the VAFM callsign.

This bridge needs cgo and three native codec components:

Component Used for Build tag
OpenDMR Decoding DMR audio (required); optionally encoding it opendmr
libopus Encoding and decoding VAFM audio opus
BLIP25 Encoding DMR audio (recommended) blip25

You need:

  • Go 1.25 or newer
  • OpenDMR, built and installed from source (installs to /usr/local by default)
  • your operating system’s Opus development package (for example libopus-dev)
  • Rust 1.85 or newer, to build the BLIP25 encoder

Build the complete bridge with the recommended encoder:

git clone https://repo.kc1awv.net/Vexillum/vafm-homebrew-bridge.git
cd vafm-homebrew-bridge
CGO_ENABLED=1 go build -tags 'opendmr opus blip25' ./cmd/vafm-homebrew

If OpenDMR is installed somewhere other than /usr/local, point cgo at it with CGO_CFLAGS and CGO_LDFLAGS. The repository’s codec guide covers installing each codec and building BLIP25.

OpenDMR is the default encoder, but BLIP25 is recommended. In live testing, BLIP25 was much cleaner and easier to understand, without the swirly, over-compressed sound OpenDMR produced from the same input. Select it with -dmr-tx-codec blip25. OpenDMR is still used for decoding.

export HOMEBREW_PASSWORD='...'   # the DMR master's password
export VAFM_PASSPHRASE='...'     # only if the VAFM instance requires one

./vafm-homebrew \
  -master DMR_MASTER_HOST:62031 \
  -id 1234567 \
  -callsign N0CALL \
  -color-code 1 \
  -description 'VAFM DMR bridge' \
  -vafm-server VAFM_HOST:PORT \
  -vafm-transport udp \
  -dmr-tx-source 1234568 \
  -dmr-tx-destination 7 \
  -dmr-tx-slot 2 \
  -dmr-tx-codec blip25

This sends VAFM calls to DMR talkgroup 7 on slot 2. If -dmr-tx-destination is left out or set to 0, only DMR-to-VAFM audio is relayed. Use -vafm-transport tcp if the VAFM instance uses its TCP transport.

Once it’s running, designate the bridge on the VAFM instance by its -vafm-callsign (or -callsign, if that isn’t set). If -master is a Vexillum DMR instance, designate it there too, by its -id repeater ID, with the same label. Don’t reuse your hotspot’s DMR ID or callsign for the bridge: the designation would match your hotspot as well (see One designation, one session).

Flag Default Description
-master Homebrew master, host:port
-id 0 Repeater ID
-password-env HOMEBREW_PASSWORD Environment variable holding the master password
-callsign Repeater callsign, max 8 characters
-local (OS picks) Local UDP address to bind
-color-code 1 Color code, 0–15
-rx-frequency, -tx-frequency 0 Reported frequencies, in Hz
-power 0 Reported power in watts, 0–99
-latitude, -longitude 0 Reported position
-height 0 Reported antenna height in meters, 0–999
-location Reported location, max 20 characters
-description VAFM DMR bridge Reported description, max 19 characters
-url Reported URL, max 124 characters
-duplex false Report duplex operation
-slot1, -slot2 true Report which slots are enabled when duplex
-options Homebrew RPTO options string
Flag Default Description
-vafm-server VAFM instance, host:port
-vafm-transport udp udp or tcp
-vafm-callsign same as -callsign Callsign presented to VAFM
-vafm-passphrase-env VAFM_PASSPHRASE Environment variable holding the VAFM passphrase, if one is required
Flag Default Description
-dmr-tx-destination 0 Talkgroup (or private-call ID) for VAFM calls. 0 disables VAFM-to-DMR.
-dmr-tx-source same as -id DMR source ID for VAFM calls
-dmr-tx-slot 2 Slot for VAFM calls, 1 or 2
-dmr-tx-private false Send VAFM calls as private calls instead of group calls
-dmr-tx-codec opendmr Encoder: opendmr or blip25 (recommended)
-dmr-tx-gain 0 OpenDMR encoder gain in dB, -20–20

These help you tell which side of a codec is causing bad audio.

Flag Description
-decode-dir PATH Record DMR-to-VAFM audio just after AMBE decoding, one file per call
-dmr-tx-pcm-dir PATH Record VAFM-to-DMR audio just before AMBE encoding

Both write headerless 8 kHz, signed 16-bit little-endian mono PCM. You can play the files with, for example, aplay -f S16_LE -r 8000 -c 1 FILE.

  • Only one DMR call at a time is forwarded to VAFM.
  • Only one VAFM stream at a time is sent to DMR. Streams that overlap are not mixed.
  • Embedded link control isn’t sent in voice bursts A–D yet. DMR radios that tune in partway through a call may not show who is talking.
  • VAFM-to-DMR audio is timed with a fixed-size queue and the system clock. It isn’t a full network jitter buffer.
  • The bridge does not use VAFM callsigns to prevent loops. Callsigns don’t uniquely identify VAFM clients, and treating them as if they did would drop legitimate traffic from FM users.