APRSdroid - X-APRS: A New Hope

Copyright Violation

Unfortunately, we have been made aware of the fact that both "X-APRS" and "APRX" are names already in use within the APRS community, and we may not reuse them for our protocol. This fact that we can not combine "X" for XML and "APRS" for APRS has made it effectively impossible to further our goals of making a modern and awesome APRS replacement. Effective April 2nd, 2017, we are stopping all activities.

You can find the original content of our proposal below.


X-APRS: a new protocol in APRSdroid

Published 2017-04-01 by Georg DO1GL

The Automatic Packet Reporting System (APRS™) protocol has been in use and development by the Amateur Radio community since the 1980ies. However, its ASCII-text based protocol format and different complex encodings are clear relics of the limited hardware capabilities of the olden days.

Today, we introduce X-APRS: a revival of the APRS protocol based on the modern, open, standardized, versatile, battle-tested XMPP protocol. The new protocol has been designed from scratch with the following goals:

X-APRS Protocol Design

XMPP is a protocol that has been designed for good extensibility and machine readability. It is not very easy to read and write by humans, but neither is APRS.

Besides of this, XMPP is used by the Military, even over low-bandwidth wireless comparable to Packet Radio (XEP-0365: Server to Server communication over STANAG 5066 ARQ). It is well known that the Military employs the smartest of people, so this alone is a solid argument pro XMPP.

The use of JSON has been evaluated, but the vast number of available, well-designed XMPP extensions made the final decision easy.

Packet Format

Let's start with a simple example of a position report, and dissect it afterwards:

<?xml version='1.0'?>
<!-- comments are annotations only and will be stripped for the wire format -->
<!-- a serverless stream (XEP-0174) for a single fire-and-forget message -->
<stream:stream xmlns='jabber:client'
        xmlns:stream='http://etherx.jabber.org/streams'
        from='DO1GL'
        to='APRS'
        version='1.0'>
    <!-- the message is the main payload unit, encoding everything else -->
    <!-- APRS routing is done by re-using the JabberID/resource format -->
    <message from='DO1GL-5' to='WIDE1-1/WIDE2-2' id='aprs-f001'>
        <!-- the 'body' element contains the user's comment -->
        <body>Georg on mobile</body>
        <!-- a GPS location with optional geocoding (XEP-0080) -->
        <geoloc xmlns='http://jabber.org/protocol/geoloc' xml:lang='en'>
            <lat>52.524268</lat>
            <lon>13.40629</lon>
            <alt>65</alt>
            <bearing>130</bearing>
            <accuracy>50</accuracy>
            <!-- the following fields are optional -->
            <country>Germany</country>
            <countrycode>DE</countrycode>
            <locality>Berlin</locality>
        </geoloc>
        <!-- timestamp of original transmission (XEP-0203) -->
        <delay xmlns='urn:xmpp:delay'
                from='DO1GL-5'
                stamp='2017-04-01T01:23:45Z'>
        <!-- optional Software Version element (XEP-0092) -->
        <version xmlns='jabber:iq:version'>
            <name>APRSdroid</name>
            <version>2.0</version>
            <os>Android 4.1</os>
        </version>
    </message>
</stream:stream>

Serverless X-APRS

Typical XMPP is a connection-oriented client-server protocol, i.e. a client opens a TCP connection, performs a TLS handshake, exchanges XMPP stream features, opens a session, etc., pp.

To be usable for a highly mobile wireless scenario, we need to cut down the number of round-trips to zero, making the protocol effectively fire-and-forget.

This can be achieved by using XEP-0174: Serverless Messaging, where clients directly communicate to each-other, without an intermediary. The default modus operandi is to use multicast DNS to detect other X-APRS-enabled parties on the local network, and then to establish a TCP connection with a client-to-client stream on them. This can be used to connect to an iGate, similar to APRS-IS over TCP/IP.

However, to allow proper mobile usage, the whole "connection" must be further streamlined. This is achieved by creating a one-shot X-APRS "stream" that contains a single X-APRS message. This "stream" can then be transmitted as a single broadcast UDP datagram (using the local broadcast address 255.255.255.255 for IPv4 or a yet-to-be-defined site-local IPv6 multicast group, e.g. ff05::f001).

Implementation note: UDP datagrams are limited to 64KB of payload size. Some X-APRS payloads might exceed that, so they MUST be transmitted over an established TCP connection only. If you want to prevent fragmentation at the IP layer, you further should restrict the overall packet size, IP headers included, to 1500 Bytes.

If reuse of the widely-available AX.25 (Packet Radio) physical layer protocol is desired, the implementation MUST make use of XEP-0322: Efficient XML Interchange (EXI) Format, which provides a compression format for XMPP messages.

Path Addressing

Legacy APRS provides a vast amount of different, flexible, adaptable addressing schemes to control the forwarding and distribution of messages. While many of them have been deprecated over the decades, the WIDEn-n path paradigm has emerged as the default.

A typical example is WIDE1-1,WIDE2-2 which allows for up to three retransmissions by nearby radio relays:

Animation of WIDEn-n forwarding
Image and explanation source: WA8LMF

On the other hand, routing in XMPP is typically performed based on the JID, which consists of three parts:

They are put together using @ and / as follows: user@domain/resource.

Because both the user and the resource parts are optional, it is possible to reduce message size in X-APRS by only using the domain part for immediate routing needs. As each site is usually run by a single operator anyway, we propose the following mapping function for the destination address:

Examples:

In cases where an operator wants to indicate that they are using a station owned or operated by a different callsign, it is possible to use the user@domain notation as follows:

Example:

<message from="DO1GL-5@DA0CCC" to="WIDE1-1/WIDE2-2">...</message>

Message Payload

The X-APRS message is a regular XMPP message as defined in RFC 6120. It can be extended with optional elements from generic XMPP extensions or from specific X-APRS extensions.

The following payloads MUST be supported for position reports:

To facilitate weather information and hazard warnings, clients SHOULD support the following extension:

Radio Information

Ham Radio is mostly about the gear. Therefore, an XMPP extension is needed to specify the most important attributes of the transceiver and antenna setup.

The following is the suggested extension for message packets (yet to be written down into its own XEP, X-APRS Extension Protocol; the wrapping stream and message elements are skipped to improve readability):

<radio xmlns="urn:xaprs:gear">
    <transceiver>
        <vendor>Barbielumber</vendor>
        <model-name>X-APRS TRX</model-name>
        <model-number>2018</model-number>
        <power>75W</power>
        <frequency>440.475MHz</frequency>
    </transceiver>
    <cable>
        <type>hollow conductor</type>
        <length unittype="metric">10m</length>
        <length unittype="imperial">33ft</length>
        <connector to="transceiver">N</connector>
        <connector to="antenna">N</connector>
    </cable>
    <antenna>
        <type>j-pole</type>
        <altitude unittype="metric">5m</altitude>
        <altitude unittype="imperial">16ft</altitude>
    </antenna>
</radio>

Forwarding (iGates and Digipeaters)

During regular operation, APRS packets are received and forwarded by digital repeaters, according to the configured path.

XMPP provides the right tool for this job as well, XEP-0297: Stanza Forwarding. This extension allows an XMPP entity to take a message from another entity, add a timestamp and forward it as a wrapped element, similar to how emails get forwarded.

X-APRS digipeaters can use the <forwarded/> message element to wrap the original message and augment it with additional data. The following example depicts a position report message (details stripped) from DO1GL that is digipeated by F1RST and then by SEC0ND. The trace contains full timestamps of each hop, and can be even augmented by <radio> elements of all digipeaters to allow better analysis of propagation conditions:

<!-- this is the message sent by the second digipeater that encountered it -->
<message from='SEC0ND' to='WIDE2-1'>
    <forwarded xmlns='urn:xmpp:forward:0'>
        <delay xmlns='urn:xmpp:delay' stamp='2017-04-01T01:45:25Z'/>
        <!-- this is the message sent by the first digipeater that encountered it -->
        <message from='F1RST' to='WIDE2-2' xmlns='jabber:client'>
            <forwarded xmlns='urn:xmpp:forward:0'>
                <delay xmlns='urn:xmpp:delay' stamp='2017-04-01T01:42:25Z'/>
                <!-- this is the original message sent by DO1GL-5 -->
                <message from='DO1GL-5' to='WIDE1-1/WIDE2-2' id='aprs-f001'>
                    <!-- original content stripped for brevity -->
                </message>
            </forwarded>
        </message>
    </forwarded>
    <radio xmlns="urn:xaprs:gear">
        <!-- details of SEC0ND radio gear stripped for brevity -->
    </radio>
</message>

Legacy Format

Of course we realize that such a protocol transition can not be completed overnight. To allow both old and new devices to coexist, we are introducing a new message extension, aprs1 that allows to forward the original, unparsed, APRS (version 1.0, 1.01, 1.1, 1.2 or any other) payloads inside of X-APRS.

APRS over X-APRS

Example legacy message (only representing the actual 'message' without the enclosing stream):

<message from='DO1GL-5' to='LEGACY'>
    <!-- the 'aprs1' element contains the original APRS payload in TNC2 format -->
    <aprs1 xmlns="urn:xaprs:legacy">DO1GL-5&gt;APDR20,WIDE1-1,WIDE2-2:=5252.42N/01340.62E$ Georg on mobile</aprs1>
</message>

Alternative data encoding to reduce the string escaping complexity:

<message from='DO1GL-5' to='LEGACY'>
    <aprs1 xmlns="urn:xaprs:legacy"><![CDATA[DO1GL-5>APDR20,WIDE1-1,WIDE2-2:=5252.42N/01340.62E$ Georg on mobile]]></aprs1>
</message>

In case the original message contains characters that are not allowed in XML (e.g. NUL), or is not valid UTF-8, the transmitter must encode the APRS1 message into Base-64:

<message from='DO1GL-5' to='LEGACY'>
    <!-- the 'aprs1' element can also contain Base-64 encoded APRS data -->
    <aprs1 xmlns="urn:xaprs:legacy">
        RE8xR0wtNSZndDtBUERSMjAsV0lERTEtMSxXSURFMi0yOj01MjUyLjQyTi8wMTM0MC42MkUkIEdl
        b3JnIG9uIG1vYmlsZQ==
    </aprs1>
</message>

Receivers must check if the aprs1 content is a valid Base-64 string and decode that before further processing the APRS1 packet.

Implementation note: For a gateway implementing APRS to X-APRS transmission, it is legal to parse the APRS1 packet and to add extracted fields into the generated XML message, according to the X-APRS message format. It is also allowed to merely encode the original packet into the aprs1 element without providing additional elements, shifting the burden of parsing onto the receiving client. The aprs1 element, if present, is always considered as authoritative.

X-APRS over APRS

Of course, such a complex protocol transition will inevitably also lead to situations where X-APRS devices need to communicate over the legacy APRS1 network. For this use case, the XAPRS to-call can be used:

DO1GL-5>XAPRS:<message from='DO1GL-5' to='APRS'>...</message>

Implementation note: In legacy APRS, the leading < character in the payload might be interpreted as a "Station Capabilities" Data Type Identifier. Legacy clients MUST NOT attempt to interpret APRS packets with a to-call of XAPRS.

Transition Period

To allow for a smooth transition period, implementations are encouraged to programmatically disable legacy aprs1 support in their code one year after initial deployment of X-APRS, on April 1st, 2018.

Testing Your Implementation

You can test your client implementation by connecting via TCP/IP to xaprs.aprsdroid.org port 20481 for a raw X-APRS stream, or port 20482 for an ANSI-colored version meant for debugging purposes:

netcat xaprs.aprsdroid.org 20482

The X-APRS gateway implementation has been kindly provided by Jonas, DD0JWI.