1xBTS 1xBTS

Voice Gateway Setup

The voice gateway (cdma-voice-gw) is a separate process that bridges CDMA voice calls to SIP/PSTN networks. It handles SIP signaling, RTP media, and EVRC↔G.711 transcoding.

Architecture

CDMA Handset
    ↕ air interface (EVRC)
BTS / BSC / MSC media + policy
    ↕ A1 call control + A2p RTP bearer on the cellular side
cdma-voice-gw
    ↕ SIP/RTP (G.711)
SIP Trunk / PSTN

The cellular side separates call control from bearer media: MSC owns call policy, media orchestration, gateway control, and preemption over A1/A2p, while BSC executes the radio leg. The gateway remains the external SIP/RTP bridge for non-local calls.

MSC Configuration

Enable the gateway in config/msc.json:

{
  "voice": {
    "gateway": {
      "enabled": true,
      "endpoint": "http://127.0.0.1:17015",
      "fallback_to_wav": true
    }
  }
}
FieldDescription
enabledEnable MSC-owned gateway routing for non-local calls
endpointGateway gRPC address
fallback_to_wavFall back to WAV playback if gateway unreachable

Gateway Configuration

The gateway uses its own JSON config file. A complete example is at config/voice-gw.example.json.

gRPC

{
  "grpc": {
    "listen_addr": "127.0.0.1:17015"
  }
}

SIP

{
  "sip": {
    "listen_addr": "192.168.1.50:5060",
    "transport": "udp",
    "request_uri_template": "sip:{called}@trunk.example.com:5060",
    "from_domain": "bts.local",
    "caller_id_override": null,
    "user_agent": "1XBTS-VoiceGW/0.1",
    "keepalive_interval_secs": 0,
    "auth": {
      "username": "mytrunk",
      "password_env": "SIP_PASSWORD"
    },
    "registration": {
      "enabled": false,
      "registrar_uri": "sip:trunk.example.com",
      "expires_secs": 300
    }
  }
}
FieldDescription
listen_addrConcrete local IP:port (not 0.0.0.0 — libre requirement)
transportudp, tcp, or tls
request_uri_templateSIP URI template — {called} replaced with dialed digits
from_domainDomain in SIP From header
caller_id_overrideFixed DID for caller ID (null = use mobile’s MDN)
auth.usernameSIP digest auth username
auth.password_envEnvironment variable containing password
registration.enabledSend REGISTER to trunk (some providers require this)

RTP

{
  "rtp": {
    "listen_addr": "0.0.0.0",
    "port_range": [17100, 17200],
    "advertise_addr": null,
    "preferred_codecs": ["PCMU", "PCMA"]
  }
}
FieldDescription
listen_addrIP for RTP bind
port_rangeUDP port range for RTP sessions
advertise_addrPublic IP for SDP (if behind NAT)
preferred_codecsG.711 variants — PCMU (μ-law) or PCMA (A-law)

NAT

{
  "nat": {
    "mode": "disabled",
    "stun_server": "stun.l.google.com:19302"
  }
}
ModeDescription
disabledNo NAT traversal (default — use when gateway has public IP)
stun_latchSTUN for external IP discovery + RTP latching

Calls

{
  "calls": {
    "max_concurrent_calls": 32,
    "setup_timeout_ms": 30000,
    "ringing_timeout_ms": 120000,
    "media_idle_timeout_ms": 30000
  }
}

Jitter Buffer

{
  "jitter_buffer_ms": 60
}

Adaptive buffer (40-80 ms) for inbound RTP reordering. Inserts EVRC silence frames when packets are late.

Running the Gateway

# Set SIP password
export SIP_PASSWORD="mysecret"

# Start the gateway
cargo run --release -p cdma-voice-gw -- --config config/voice-gw.example.json

The gateway listens on the configured gRPC port. The MSC runtime connects automatically when gateway.enabled is true.

SIP Trunk Providers

The gateway works with any standard SIP trunk. Tested configurations:

ProviderTransportAuthRegisterNotes
FreeSWITCHUDPDigestOptionalLocal testing
FlowrouteUDPDigestYesProduction trunks
TwilioUDP/TLSDigestYesElastic SIP

FreeSWITCH Example

{
  "sip": {
    "request_uri_template": "sip:{called}@192.168.1.200:5060",
    "from_domain": "192.168.1.50",
    "auth": null,
    "registration": { "enabled": false }
  }
}

Flowroute Example

{
  "sip": {
    "request_uri_template": "sip:{called}@us-west-wa.sip.flowroute.com",
    "from_domain": "sip.flowroute.com",
    "auth": {
      "username": "your-tech-prefix",
      "password_env": "FLOWROUTE_PASSWORD"
    },
    "registration": {
      "enabled": true,
      "registrar_uri": "sip:sip.flowroute.com",
      "expires_secs": 300
    }
  }
}

Transcoding

The gateway transcodes between EVRC (air interface) and G.711 (SIP/RTP):

DirectionPipeline
Handset → PSTNEVRC primary bits → EVRC decode → PCM → G.711 encode → RTP
PSTN → HandsetRTP → G.711 decode → PCM → EVRC encode → EVRC primary bits

Media frames on the cellular bearer are exchanged as raw EVRC codec payloads (without MuxPDU headers). The BSC adds or strips the MuxPDU MM=0 header for the Abis and air-interface side, and the MSC forwards non-local call media between A2p and the gateway. A2p uses RTP/UDP per circuit and the EVRC RTP payload format from RFC 3558. Erasure frames with rate_bps=0 are skipped in the gateway media path.

Troubleshooting

SymptomCheck
Gateway not connectingMSC voice.gateway.endpoint matches gateway grpc.listen_addr
SIP INVITE rejectedAuth credentials, from_domain, trunk provider settings
No audioRTP listen address reachable from SIP peer, firewall rules on RTP port range
One-way audioNAT config — try stun_latch mode, or set advertise_addr
Choppy audioJitter buffer too small, network latency to SIP peer
Calls fall back to WAVGateway process not running, or fallback_to_wav is true
DNS resolution failureCheck request_uri_template domain resolves, try IP directly