Crate dstar_gateway

Crate dstar_gateway 

Source
Expand description

§dstar-gateway

CI docs.rs Book License: GPL-2.0-or-later

Async Rust D-STAR reflector gateway library. Implements the DPlus, DExtra, and DCS reflector protocols with a full typestate session machine, a sans-io core, lenient parsing with structured diagnostics, and symmetric client + server scope.

§Status

Alpha. Pre-0.1.0, unpublished. The rewrite is feature-complete on the client side (DPlus + DExtra + DCS) but has not been stress-tested against real reflectors for an extended period. The reflector server currently ships DExtra only; DPlus and DCS server support, cross-protocol forwarding, per-client rate limiting, and the StreamCache header retransmit wiring are all open deferred work.

This crate lives in the kenwood monorepo and is not yet published to crates.io. Consume it via a path or git dependency until the first numbered release lands. See CHANGELOG.md for the work-in-progress feature list.

§The three crates

CrateWhat it does
dstar-gateway-coreSans-io codec + typestate Session<P, S> state machines. No tokio, no I/O.
dstar-gateway (this crate)Tokio AsyncSession<P> shell, DPlus TCP AuthClient, optional Pi-Star host-file fetcher.
dstar-gateway-serverMulti-client Reflector server (DExtra only today; DPlus/DCS deferred).

§Quickstart

Connect to a DExtra reflector, listen for 10 seconds, then disconnect cleanly.

use std::sync::Arc;
use std::time::{Duration, Instant};

use dstar_gateway::tokio_shell::AsyncSession;
use dstar_gateway_core::session::Driver;
use dstar_gateway_core::session::client::{
    ClientStateKind, Configured, DExtra, Session,
};
use dstar_gateway_core::types::{Callsign, Module};
use tokio::net::UdpSocket;
use tokio::time::timeout;

// 1. Bind a local UDP socket.
let sock = Arc::new(UdpSocket::bind("0.0.0.0:0").await?);

// 2. Build a Configured session.
let session: Session<DExtra, Configured> = Session::<DExtra, Configured>::builder()
    .callsign(Callsign::try_from_str("W1AW")?)
    .local_module(Module::try_from_char('B')?)
    .reflector_module(Module::try_from_char('C')?)
    .peer("127.0.0.1:30001".parse()?)
    .build();

// 3. Drive the handshake manually on the test thread.
let mut connecting = session.connect(Instant::now())?;
let tx = connecting.poll_transmit(Instant::now()).expect("LINK");
sock.send_to(tx.payload, tx.dst).await?;

let mut buf = [0u8; 64];
let (n, peer) = timeout(Duration::from_secs(5), sock.recv_from(&mut buf))
    .await??;
connecting.handle_input(Instant::now(), peer, &buf[..n])?;
assert_eq!(connecting.state_kind(), ClientStateKind::Connected);
let connected = connecting.promote()?;

// 4. Hand off to the tokio shell and listen for 10 seconds.
let mut session = AsyncSession::spawn(connected, Arc::clone(&sock));
let deadline = tokio::time::Instant::now() + Duration::from_secs(10);
loop {
    tokio::select! {
        _ = tokio::time::sleep_until(deadline) => break,
        ev = session.next_event() => {
            match ev {
                Some(e) => println!("{e:?}"),
                None => break,
            }
        }
    }
}

// 5. Graceful disconnect.
session.disconnect().await?;

For DPlus you additionally call AuthClient::authenticate before building the Session<DPlus, Configured>; DCS uses the same shape as DExtra.

See the dstar-gateway/examples/ directory for standalone runnable versions of each.

§Features

FeatureStatusNotes
DPlus client (REF, TCP auth)StableAuthClient + Session<DPlus, _>
DExtra client (XRF, XLX)StableSession<DExtra, _>
DCS clientStableSession<Dcs, _> — header caching handled by core
DExtra reflector serverStabledstar-gateway-server::Reflector
DPlus reflector serverDeferredCore typestate exists; shell not wired
DCS reflector serverDeferredCore typestate exists; shell not wired
blocking featureOptionalCLI-friendly variant of AsyncSession
hosts-fetcher featureOptionalPulls reqwest; downloads Pi-Star host files
Slow-data sub-codecStableShort messages embedded in voice frames
DPRS position reportsStableDecodes $$CRC-prefixed slow-data strings
Lenient parsingStableStructured Diagnostic via DiagnosticSink trait
Property tests + fuzzStable10 fuzz targets, 75M executions, 0 crashes

§Feature flags

[dependencies]
dstar-gateway = { path = "../dstar-gateway", features = ["hosts-fetcher"] }
  • blocking — compile a non-tokio blocking shell for CLI scripts and test fixtures.
  • hosts-fetcher — pulls reqwest; downloads the Pi-Star DPlus_Hosts.txt, DExtra_Hosts.txt, and DCS_Hosts.txt files.

§Documentation

§License

GPL-2.0-or-later, matching the upstream g4klx/ircDDBGateway and LX3JL/xlxd reference implementations. Async tokio shell for the dstar-gateway-core D-STAR reflector library.

This crate provides the async API consumers will use: AsyncSession<P> built on top of the sans-io typestate Session<P, S> in the core crate.

§Architecture

dstar-gateway-core is the runtime-agnostic, I/O-free sans-io core. It contains the codecs and typestate session machines. This crate wraps that core in a tokio::net::UdpSocket-backed driver loop, spawns it as a task, and exposes an tokio_shell::AsyncSession handle with send_header / send_voice / send_eot / disconnect methods.

[your app] <--AsyncSession--> [SessionLoop task] <--UdpSocket--> [Reflector]
                                       |
                                  Session<P, Connected>
                                  (sans-io core)

§Feature flags

  • blocking — additionally compiles a blocking-shell variant (no tokio dependency at runtime) under the blocking_shell module. Useful for CLI scripts and test fixtures that don’t want a tokio runtime.
  • hosts-fetcher — pulls reqwest for downloading Pi-Star host files under the hosts_fetcher module. Disabled by default so the crate stays dependency-light for consumers who don’t need HTTP.

§Core re-exports

The core types are re-exported from dstar-gateway-core for consumer convenience, so downstream crates don’t need a separate dstar-gateway-core dependency for the common types.

Modules§

auth
DPlus TCP authentication client.
tokio_shell
Tokio async shell driving the sans-io dstar-gateway-core.

Structs§

Callsign
D-STAR callsign (8 bytes, ASCII, space-padded on the right).
DStarHeader
D-STAR radio header.
HostEntry
A resolved reflector host entry.
HostFile
Collection of host file entries keyed by reflector name.
Module
D-STAR reflector module letter (A-Z).
ReflectorCallsign
Reflector callsign with a known protocol prefix.
StreamId
D-STAR voice stream identifier.
Suffix
4-byte D-STAR callsign suffix.
VoiceFrame
A D-STAR voice data frame (9 bytes AMBE + 3 bytes slow data).

Enums§

BandLetter
D-STAR radio band letter (A, B, C, D).
Error
Top-level error type for dstar-gateway-core.
ProtocolKind
D-STAR reflector protocol discriminator.
TypeError
Validation errors for dstar-gateway-core strong types.

Constants§

AMBE_SILENCE
AMBE silence frame (9 bytes) — used in EOT packets.
DSTAR_SYNC_BYTES
D-STAR sync bytes (3 bytes) — slow data filler for sync frames.