APRS-IS is the internet backbone that makes APRS global. It is a mesh of servers relaying a single worldwide stream of packets over plain TCP, in the TNC2 text format, one frame per line.
It is also where most APRS misbehaviour happens, because the internet side has no propagation limits to keep bad configuration local. A misconfigured RF digipeater annoys a county. A misconfigured IGate can annoy the planet.
Connecting
APRS-IS speaks a line-oriented text protocol. Connect a TCP socket, send a login line, and read lines forever.
user N0CALL pass 12345 vers myclient 1.0 filter r/36.16/-86.78/50
The fields:
user— your call sign, with SSID if you want one.pass— your passcode. Not a password you choose. It is a number deterministically derived from your call sign by a published algorithm, which means it is not a security measure — it is a speed bump confirming you have a call sign at all. Everyone’s passcode for a given call is the same number.vers— software name and version. Required; servers reject clients that do not identify themselves.filter— optional, and covered below.
Send -1 as the passcode for receive-only access. The server will accept the connection and stream packets, but will refuse anything you try to inject. If you are building a display, a logger, or a receive-only IGate, use -1 — there is no reason to authenticate for read access, and doing so risks accidentally becoming a transmitter.
The server replies with a comment line beginning #, then a verification line:
# aprsc 2.1.19-g730c5c0
# logresp N0CALL verified, server T2NUENGLD
verified means the passcode matched. unverified means it did not, and your packets will be dropped rather than gated.
Ports
Servers such as rotate.aprs2.net listen on several ports, and the choice matters:
| Port | Purpose |
|---|---|
| 14580 | Filtered feed. The normal choice — you specify a filter and receive only matching traffic. |
| 10152 | Full feed, all traffic worldwide. Firehose. |
| 8080 | HTTP POST submit-only, for injecting packets without a persistent socket. |
| 20152 | Full feed, binary/raw variants on some servers. |
Use 14580 with a filter. The full feed is thousands of packets per second and there is rarely a reason for a client to drink from it.
q constructs
Every packet that passes through APRS-IS gets a q construct appended to its digipeater path — a three-character token beginning with q that records how the packet entered the internet. This is the provenance mechanism, and it is what prevents loops between RF and the internet.
N0CALL-9>APRS,WIDE1-1,qAR,N0GATE-10:!3554.12N/08646.50W>
└┬─┘ └───┬───┘
construct the station that gated it
The ones you will actually see:
| Construct | Meaning |
|---|---|
qAC |
Received from a client that logged in with a verified passcode, directly on a server port. |
qAR |
Gated from RF by the station that follows. The most common construct on the network. |
qAo |
Gated from RF by a station that does not gate internet traffic back to RF. |
qAS |
Received from a server-to-server link. |
qAX |
Received from an unverified client. These packets are not relayed onward to RF. |
qAU |
Received via UDP submit. |
qAI |
Injected directly by the server itself. |
The practical rule: qAR says a real radio heard this packet, and names the receiver. qAC says it arrived over the internet and never touched RF. A gateway deciding whether to transmit something must look at the q construct, because gating an internet-only packet back to RF injects traffic that was never on the air in the first place.
The filter language
Filters are the reason port 14580 is usable. You set one at login, or change it at any time by sending:
#filter r/36.16/-86.78/50 t/poimqstunw
Filters are additive — a packet matching any one of them passes. Prefix a filter with - to make it a rejection.
Range and area
r/lat/lon/dist within dist km of a point (signed decimal degrees)
m/dist within dist km of MY last reported position
f/call/dist within dist km of another station's position
a/latN/lonW/latS/lonE inside a bounding box
r/ is the workhorse. r/36.16/-86.78/100 gives you everything within 100 km of a point — the natural filter for a local map or a regional IGate.
Note that r/ and f/ only match packets that have a position. A status report or a message has no coordinates and will not pass a range filter, which is a common surprise.
By station
p/aa/bb/cc source call STARTS WITH any of these prefixes
b/call1/call2 budlist — exact call match, SSID significant
o/obj1/obj2 objects and items by name
d/digi1/digi2 packets that were digipeated by these stations
e/call1/call2 packets gated to APRS-IS by these entry stations
g/call1/call2 messages addressed to these stations
u/unproto1 packets with these destination identifiers (tocalls)
p/ is prefix-based, so p/K passes every US call starting with K — usually far too broad. b/ is exact, and the SSID is part of the match: b/N0CALL-9 will not match N0CALL-7.
Several of these support wildcards with *.
By content
t/poimqstunw packet TYPE filter
s/pri/alt/over symbol filter
q/con/ana q construct filter
The type letters:
| Letter | Type |
|---|---|
p |
Position |
o |
Object |
i |
Item |
m |
Message |
q |
Query |
s |
Status |
t |
Telemetry |
u |
User-defined |
n |
NWS weather and weather objects |
w |
Weather |
c |
CWOP |
* |
Everything |
So t/w gives you weather reports only — which, if you are building a weather display, is dramatically more efficient than filtering a full feed client-side.
s/pri/alt/over filters by symbol: s/_ passes weather station symbols from the primary table. Combined as t/w s/_, you get a clean weather-only stream.
Every packet you filter server-side is bandwidth you do not carry, CPU you do not spend, and load you do not put on the network. A client that connects to the full feed and discards 99.9% of it works fine and is still doing the wrong thing. Push the predicate to the server.
Running an IGate
An IGate bridges RF and APRS-IS. Receive-only is trivially safe and genuinely useful. Bidirectional gating is where care is required.
Receive-only (RX IGate)
Hear a packet on 144.390, append qAR,YOURCALL, send it to APRS-IS. That is the whole job.
Requirements are modest: an RTL-SDR or any receiver, a 2 m antenna, and software such as Dire Wolf feeding an APRS-IS client. It adds coverage for everyone in your area and cannot cause RF harm, because it never transmits. If you want to contribute something to the network, this is the thing to build.
Before you do, check aprs.fi for existing IGates nearby — if your area is already well covered, your receiver adds little, whereas a genuine gap is valuable.
Bidirectional (TX IGate)
Transmitting internet traffic onto RF is where an IGate can do real damage, because the internet has no range limit and the RF channel does. The governing rules:
- Only gate messages to stations you have actually heard on RF recently. The conventional window is roughly 30 minutes. If you have not heard the station, transmitting to it puts a packet on the air that nobody local can use.
- Never gate a packet back to RF if it already has an RF origin in its path, or if your own call already appears. That is a loop.
- Never gate third-party traffic (
}) back to RF. - Do not gate position beacons from the internet to RF at all. Messages and their acknowledgements, yes. Everyone’s positions, no — that would flood the local channel with stations nobody can hear.
- Respect
qAX. Unverified packets must never reach RF.
Most IGate software implements these correctly by default. The failure mode is almost always someone overriding a default they did not understand.
Local RF beaconing
An IGate should beacon its own position occasionally — every 20 to 30 minutes — with the IGate symbol (/&, or an overlay) so the local network knows it exists. It should not beacon more often than that, and it should use a short path or none at all, since by definition it is connected to the internet and does not need digipeating to be seen.
Next: APRS Weather Stations covers the weather packet formats that ride on all of this, and APRS Packet Anatomy covers the frame structure underneath it.