When creating packet radio applications, there are several options on how to get the packets “out there”, and get them back. That is, how to interface with the modem.

Sure, you can write your own modem, and have the interface to the outside world be plain audio and PTT (push to talk, i.e. trigger transmit). But now you’re writing a modem, not an application. You should probably split the two, and have an interface between them.


You can use KISS, but it’s very limited. You can only send individual packets, so it’s only really good for sending unconnected (think UDP) packets like APRS. It’s not good for querying metadata, such as port information and outstanding transmit queue.

Think of KISS like a lower layer that applications shouldn’t think about. Like ethernet. Sure, as a good engineer you should know about KISS, but it’s not what your application should be interfacing with.

Linux kernel implementation

On Linux you can use AF_AX25 sockets, and program exactly like you do for regular internet/IP programs. SOCK_DGRAM for UI frames (UDP-like), and SOCK_STREAM for connected mode (TCP-like).

But the Linux kernel implementation is way too buggy. SOCK_STREAM works kinda OK, but does not handle all cases well. E.g. I don’t know if my patch making it possible to call write() while a read() is pending made it into the kernel. SOCK_DGRAM is just plain broken.

Sure, the kernel could be fixed. There’s a 180k EUR bounty on fixing it, but to my knowledge nothing is happening with that. And even if it does get fixed, I’m not a fan of the approach as a whole.

Any improvements to the kernel take years to actually get to users. And any added bugs will also take years to be noticed, and therefore we should expect multi-year breakages regularly.

This isn’t the late 1900s anymore, where we scripted downloading the latest kernel version as soon as Linus put it on an FTP server. It’s not realistic to expect people to even apply targeted patches, much less make the latest kernel work with their ZFS out of tree module, and some other vendor patches, like for Raspberry Pi.

I know what I’m doing, and I still don’t want to recompile the kernel on every machine on which I want to play with packet radio.

Also, at this point the kernel implementation is more likely to be removed, than fixed.


AGW, or AGWPE, is a slightly higher level protocol. The documentation and general usage is a bit unclear about if AGWPE refers to the protocol or the reference implementation, so naming is a bit inconsistent out there in the world. I’ll use AGW to refer to the protocol, for now.

AGW is way more capable than KISS. It supports connected mode, querying for ports, and other things. It’s an async protocol, and by the standards of amateur radio (and the time it was designed) it’s not too bad.

It has some reserved fields, and is not super extensible for the future, but it’s also pretty complete. In any case it is what a huge set of programs support, when they support something other than the broken kernel API.

As an example, Direwolf supports talking AGW over TCP, as does soundmodem. So this seems to be the best interface available today to code applications against.

I think that because direwolf supports both KISS and AGW, I should be able to use the KISS interface to go at 9600bps using a Kenwood TH-D74/D75. But that part I’ve not tried yet.

Not that connected mode AX.25 scales linearly from 1200bps to 9600bps, as WB2OSZ has described, but for bulk transfers that’s another project of mine.

Rust API for the AGW protocol

I made a Rust library for using AGW, code here. Example code is a simple curses-based terminal.

Terminal connected


Today’s surprise was watching a youtube video teaching people about amateur radio stuff and encountering this, red circle and all, from this video on Modern Introduction to Packet Radio - APRS BBS TCP/IP AX25 and NPR.

Unrelated photo