Skip to content

Repository files navigation

aprsutils

Go Reference

A Go library of building blocks for working with the APRS-IS network and the APRS protocol. It provides packet parsing, server-side filtering, q-construct processing, an APRS-IS client, passcode generation and a handful of related utilities.

It is the algorithm/utility layer behind the aprsgo APRS-IS server, but each package is usable on its own.

Install

go get github.com/APRSCN/aprsutils
import"github.com/APRSCN/aprsutils"

Requires a recent Go toolchain (see go.mod for the exact version).

Packages

Import pathPurpose
github.com/APRSCN/aprsutilsTop-level helpers: passcode, callsign validation, Base91, distance, logger interface.
.../aprsutils/parserParse raw APRS packets into a structured Parsed value.
.../aprsutils/filterCompile and evaluate APRS-IS server filters (the a/b/d/e/f/g/m/o/p/q/r/s/t/u classes).
.../aprsutils/qConstructApply the APRS-IS q-construct algorithm (path rewriting, loop/duplicate detection).
.../aprsutils/clientConnect to an APRS-IS server over TCP (full stream) or UDP (submit).
.../aprsutils/utilsSmall string helpers used across the library.

Top-level helpers (aprsutils)

Passcode

code:=aprsutils.Passcode("N0CALL") // APRS-IS login passcode for a callsign

The passcode is derived from the callsign root (SSID stripped, upper-cased, truncated to 8 characters).

Callsign validation

ok:=aprsutils.ValidateCallsign("N0CALL-9") // true

Base91

n, err:=aprsutils.ToDecimal("<*e7") // Base91 text -> integers, err:=aprsutils.FromDecimal(12345) // integer -> Base91 texts, err=aprsutils.FromDecimal(123, 4) // zero/"!"-padded to a fixed width

Distance

Great-circle / geodesic distance between two lat,lon pairs, returned in kilometres:

km:=aprsutils.CalculateDistanceVincentyInverse(lat1, lon1, lat2, lon2) // WGS-84, high accuracykm=aprsutils.CalculateDistanceHaversine(lat1, lon1, lat2, lon2) // spherical approximation

CalculateDistanceVincentyInverse returns NaN if the iteration fails to converge (near-antipodal points).

Logger

The library logs through a small interface so callers can plug in their own logger (e.g. zap):

typeLoggerinterface {
Debug(context.Context, ...any)
Info(context.Context, ...any)
Warn(context.Context, ...any)
Error(context.Context, ...any)
}

aprsutils.NewLogger() returns a default implementation that writes to stdout.


parser

Parse a raw APRS-IS line into a structured value.

import"github.com/APRSCN/aprsutils/parser"p, err:=parser.Parse("N0CALL>APRS,TCPIP*:!4903.50N/07201.75W-Test")
iferr!=nil {
// malformed packet
}
fmt.Println(p.From) // "N0CALL"fmt.Println(p.To) // "APRS"fmt.Println(p.Path) // ["TCPIP*"]fmt.Println(p.Lat, p.Lon) // 49.0583, -72.0291fmt.Println(p.HasPosition) // true

Parsed exposes the source/destination callsigns, digipeater path, position, symbol, comment, object/item names, weather, telemetry, message fields and a PacketType bitmask used by type filters.

PacketType

PacketType is a bitmask describing the packet category; test it with Has:

ifp.PacketType.Has(parser.TypePosition) { /* ... */ }

Available bits include TypePosition, TypeObject, TypeItem, TypeMessage, TypeQuery, TypeStatus, TypeTelemetry, TypeUserDef, TypeWeather, TypeNWS, TypeBulletin, TypeThirdParty, TypeNMEA and TypeCWOP.

Options

// Skip validation of the destination (tocall) field; useful for lenient// server-side parsing of arbitrary inbound traffic.p, err:=parser.Parse(raw, parser.WithDisableToCallsignValidate())

filter

Compile an APRS-IS server filter once, then evaluate it against parsed packets.

import"github.com/APRSCN/aprsutils/filter"f:=filter.Compile("r/33/-96/100 t/m") // within 100 km of 33,-96 OR messagesiff.Match(&p, nil) {
// packet passes the filter
}

Compile returns *Filter, which is safe to reuse across packets. Match applies negated terms first, then positive terms (matching the reference APRS-IS ordering).

Stateful filters (m/, f/, t/ ranges)

Filters that depend on station positions (e.g. m/ "my range", f/ "friend range") need a position source. Pass a Context:

typeContextinterface {
ClientPosition() (filter.Position, bool) // the connecting client's last positionStationPosition(callstring) (filter.Position, bool) // any station's last position
}

Pass nil when no positional state is available; such filters then simply do not match.

Supported filter classes: a (area), b (budlist, exact), d (digipeater), e (entry station), f (friend range), g (group/message-to), m (my range), o/os (object/strict object), p (prefix), q (q-construct), r (range), s (symbol, three layers), t (type, incl. t/c CWOP), u (unproto destination). Each term may be negated with a leading -.


qConstruct

Apply the APRS-IS q-construct algorithm: rewrite the q-path, detect loops and duplicate logins, and decide whether a packet should be dropped.

import"github.com/APRSCN/aprsutils/qConstruct"cfg:=&qConstruct.QConfig{
ServerLogin: "MYSRV",
ClientLogin: "N0CALL",
ConnectionType: qConstruct.ConnectionVerified,
IsVerified: true,
}
res, err:=qConstruct.QConstruct(p, cfg)
iferr!=nil||res.ShouldDrop||res.IsLoop {
// drop the packetreturn
}
// Splice the new path back into the raw line.raw, err=qConstruct.Replace(raw, p.To, res.Path)

ConnectionType selects the handling rules; the available types are ConnectionDirectUDP, ConnectionUnverified, ConnectionVerifiedClientOnly, ConnectionVerified, ConnectionOutboundServer, ConnectionSendOnly and ConnectionClientOnly.

QResult reports the rewritten Path, whether the packet ShouldDrop (with a DropReason) and whether it is a routing loop (IsLoop).

Replace rewrites only the header (path) segment of the raw line, leaving the payload untouched.


client

An APRS-IS client supporting two transports:

  • TCP — a persistent stream: login handshake, packet receive loop, heartbeat and (optional) automatic reconnect.
  • UDP — "submit" mode: each datagram is prefixed with the login line so the server can authenticate and inject packets independently. There is no receive loop in UDP mode.
import"github.com/APRSCN/aprsutils/client"c:=client.NewClient(
"N0CALL", "12345", // callsign, passcodeclient.IGate, client.TCP, // mode, protocol"rotate.aprs.net", 14580, // host, portclient.WithFilter("r/33/-96/100"),
client.WithHandler(func(packetstring) {
fmt.Println("RX:", packet)
}),
)
iferr:=c.Connect(); err!=nil {
log.Fatal(err)
}
deferc.Close()
_=c.SendPacket("N0CALL>APRS:>hello from aprsutils")
c.Wait() // block until the client is closed

Modes and protocols

client.Fullfeed// receive everything (no filter)client.IGate// receive filtered traffic (use WithFilter)client.TCP// persistent streamclient.UDP// submit-only datagrams

Options

OptionEffect
WithLogger(l)Use a custom aprsutils.Logger.
WithHandler(fn)Callback for each received packet (TCP).
WithSoftwareAndVersion(name, ver)Advertise software name/version in the login line.
WithFilter(spec)Server-side filter to request (igate mode).
WithRetryTimes(n)Reconnect attempts after a drop (0 disables internal retry).
WithBufSize(n)Read buffer size in bytes.

Accessors

Callsign, Filter, Mode, Protocol, Host, Port, Up, Uptime, Server (upstream software banner), ServerID (upstream callsign from the logresp line), RemoteAddr (resolved IP:port of the current session) and GetStats (byte/packet counters and rates).


Testing

go test ./...
go test -race ./...
go vet ./...

License

See LICENSE.

About

APRS lib for Go

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages