agw

package
v0.13.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 9, 2026 License: GPL-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package agw implements a minimal AGWPE-compatible TCP server for APRS use. The wire format matches direwolf's server.c so existing clients (APRSIS32, UI-View, YAAC, Xastir) interoperate without modification.

Only the frame types needed for APRS are implemented:

Client → server:
  'R'  query AGW version
  'G'  query port information (list of radio ports)
  'g'  query port capabilities
  'X'  register callsign
  'x'  unregister callsign
  'm'  start monitoring (enable 'U'-type rx packets)
  'k'  transmit raw AX.25 frame
  'M'  transmit UNPROTO (UI) frame — server must build the AX.25 header

Server → client:
  'R'  version response
  'G'  port info response
  'g'  port capability response
  'X'  callsign registered ack
  'U'  monitored UI frame from RF

Connected-mode frame types ('C', 'D', 'd', 'v', 'V', 'c', ...) are accepted and logged but not implemented, matching the "AX.25 UI only" constraint for graywolf Phase 2.

Index

Constants

View Source
const (
	KindVersion            byte = 'R'
	KindPortInfo           byte = 'G'
	KindPortCaps           byte = 'g'
	KindRegisterCallsign   byte = 'X'
	KindUnregisterCallsign byte = 'x'
	KindMonitorOn          byte = 'm'
	KindSendUnproto        byte = 'M' // client → server: send UI frame
	KindSendUnprotoVia     byte = 'V' // client → server: send UI frame via digipeaters
	KindSendRaw            byte = 'K' // both directions: raw AX.25
	KindMonitoredUI        byte = 'U' // server → client: rx UI frame
)

Data kinds as single ASCII bytes. Direction is context-dependent (some kinds appear in both directions).

View Source
const HeaderSize = 36

HeaderSize is the fixed AGW frame header length.

Variables

This section is empty.

Functions

func EncodeHeader

func EncodeHeader(h *Header) []byte

EncodeHeader writes h into a 36-byte buffer.

func WriteFrame

func WriteFrame(w io.Writer, h *Header, data []byte) error

WriteFrame serialises h (with DataLen overwritten to len(data)) and writes header+data to w atomically using a single buffer allocation.

Types

type Header struct {
	Port     uint8
	DataKind byte
	PID      uint8
	CallFrom string // 10-char NUL-padded
	CallTo   string // 10-char NUL-padded
	DataLen  uint32 // little-endian on the wire
	User     uint32
}

Header is the 36-byte AGWPE frame header.

func DecodeHeader

func DecodeHeader(buf []byte) (*Header, error)

DecodeHeader parses a 36-byte header.

func ReadFrame

func ReadFrame(r io.Reader) (*Header, []byte, error)

ReadFrame reads one header + data payload from r.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server is a multi-client AGWPE-compatible TCP server.

func NewServer

func NewServer(cfg ServerConfig) *Server

NewServer builds an AGW server. Does not listen until ListenAndServe.

func (*Server) ActiveClients

func (s *Server) ActiveClients() int

ActiveClients returns the current client count.

func (*Server) BroadcastMonitoredUI

func (s *Server) BroadcastMonitoredUI(port uint8, f *ax25.Frame)

BroadcastMonitoredUI sends a received UI frame to every connected monitoring client as an AGW 'U' record.

func (*Server) ListenAndServe

func (s *Server) ListenAndServe(ctx context.Context) error

ListenAndServe binds and serves until ctx is cancelled or Shutdown is called. Blocks. When it returns, the listener is closed and the bound port is free.

func (*Server) LocalAddr

func (s *Server) LocalAddr() net.Addr

LocalAddr returns the actual bound listener address. Returns nil until ListenAndServe has successfully bound. Useful for tests that pass ":0" and want the OS-assigned port.

func (*Server) Shutdown

func (s *Server) Shutdown(ctx context.Context) error

Shutdown triggers an orderly exit of ListenAndServe without requiring the caller's context to be cancelled. It closes the listener (breaking Accept) and closes every live client connection to unblock their readers, then waits for all tracked goroutines up to ctx's deadline. Safe to call more than once; subsequent calls are no-ops.

type ServerConfig

type ServerConfig struct {
	ListenAddr string
	// PortCallsigns lists the mycall of each radio port, in AGWPE port
	// order (index 0 = port 0). Used in the 'G' response.
	PortCallsigns []string
	// PortToChannel maps an AGW port number to a graywolf channel. If a
	// port isn't listed it defaults to PortToChannel[0] or channel 1.
	PortToChannel map[uint8]uint32
	// Sink receives parsed AX.25 frames for transmission. Typically
	// *txgovernor.Governor in production.
	Sink txgovernor.TxSink
	// Logger is optional.
	Logger *slog.Logger
	// OnClientChange is invoked with the new total-client count on connect
	// and disconnect. Optional.
	OnClientChange func(active int)
	// OnDecodeError is invoked for each raw-frame decoding attempt that
	// fails, with stage == "initial" when the first ax25.Decode fails (and
	// the skip-byte fallback is about to be tried) or stage == "fallback"
	// when both attempts fail and the frame is dropped. Optional.
	OnDecodeError func(stage string)
}

ServerConfig configures the AGW TCP server.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL