An earlier version of this post that did data over D-Star was
misleading. This is the new version.
This blog post aims do describe the steps to setting up packet radio
on modern hardware with Linux. There’s lots of ham radio documentation
out there about various setups, but they’re usually at least 20 years
old, and you’ll find recommendations to use software that’s not been
updated is just as long.
Specifically here I’ll set up a Kenwood TH-D74 and ICom
9700 to talk to each other over D-Star and
AX.25. But for the latter you can also use use cheap
Baofengs just as well.
Note that 9600bps AX.25 can only be generated by a compatible
radio. 1200bps can be send to a non-supporting radio as audio, but
9600bps cannot. So both D-Star and AX.25 here will give only
1200bps. But with hundreds of watts you can get really far with it, at
least.
I’ll assume that you already know how to set up APRS (and therefore
KISS) on a D74. If not, get comfortable with that first by reading the
manual.
DMR doesn’t seem to have a data mode, and SystemFusion radios don’t
give the user access to send arbitrary payload. That leaves
FM-modulated AX.25 and D-Star at 1200bps.
It’s really frustrating that none of the three digital amateur radio
systems (D-Star, DMR, and SystemFusion) allow actually sending
arbitrary digital data. Or at least their radios don’t.
Again, for an experimental learning hobby, it’s surprisingly
closed.
My setup
I’ll connect a laptop to a D74 over bluetooth, and a raspberry pi to
the 9700 over USB. I’ll use callsign M0XXX
on the laptop, and
2E0XXX
on the raspberry pi.
If you copy-paste, please make sure to replace these with your own
call sign.
You don’t need to have two call signs to do this. I just did this to
make it clearer to me (and this post) what’s where, and my old
Intermediate call sign is still valid, so I can.
Technology stack
As I described a bit in my APRS blog post this
will involve a TNC. Luckily the D74 has one built in. The ICom
9700 has USB serial, and D-Star data, but not a TNC.
With the 9700 you can set the second virtual USB port to be for DV
Data
. That’ll give you direct access for two-way communication across
the D-Star data channel. But it’s ASCII only. For more details see
page 10-22 in the IC-977 Advanced Manual.
So we’ll leave D-Star data for now. I’ll come back to it later on.
To get packet data working on the D74:
- Connect bluetooth serial device (or use an USB serial port)
- Get working KISS communication with the radio’s TNC over serial
- Connect the KISS port to the kernel to get AX.25
- Run services on top of AX.25
While setting this up, don’t forget to turn your TX power down to
minimum. Likely it’ll still work for you too with a dummy load
attached instead of an antenna, even on minimum power.
1. Connect bluetooth serial device
- Enable bluetooth on the D74 (menu-931)
- Route KISS and DV/DR to bluetooth (menu-983&984)
- Confirm your laptop has a bluetooth device
laptop$ hcitool dev
Devices:
hci0 A0:51:0B:01:02:03
- Set the D74 in pairing mode (menu-934)
- Check that you can find the radio
laptop$ sudo hciconfig hci0 piscan
laptop$ sdptool add SP
laptop$ hcitool scan
[you should see the TH-D74 here]
laptop$ export D74=00:11:22:33:44:55 # (the mac address you see)
- Test connect to the radio:
laptop$ sudo rfcomm connect /dev/rfcomm0 $D74 2
If it says it’s good, that’s a confirmation so we press ^C. If it
doesn’t work, try inside and outside of pairing mode.
2. Get working KISS communication with the radio’s TNC over serial
- Set the system up to connect to the radio when anything opens
/dev/rfcomm0
:
laptop$ sudo rfcomm bind /dev/rfcomm0 $D74 2
- Enable
KISS12
(not KISS96
) mode on the D74. - To confirm that it works, start direwolf’s
kissutil
, as a test:
laptop$ mkdir tx rx
laptop$ kissutil -p /dev/rfcomm0 -f tx -o rx
- Send a test message (make sure you use your own call sign, not
M0XXX
)
laptop$ echo 'M0XXX-4>APDR15,WIDE1-1:=3807.41N/212006.78WbMESSAGE' > tx/msg
This should send the message via AX.25 on VFO A. You should see
kissutil
confirming this, and the radio should transmit. If you
have another radio capable of APRS then it should have received the
packet.
That should confirm that the serial device is working. Now turn off
kissutil
.
Actually the quicker way that doesn’t require direwolf is to just run:
laptop$ echo -ne '\xC0\x00hello world from M0XXX\xC0' > /dev/rfcomm0
That is a simple way to send a packet using the KISS
protocol.
Now you also know how to send arbitrary packets. And you can use
Xastir with APRS from here and be done, if that’s all you wanted to
do. Just point Xastir to /dev/rfcomm0
as a serial TNC device.
Xastir is an ancient program that uses 90s era UI components. If
you’re not used to Motif-looking UIs you may think that it’s
broken. But no it actually does work.
3. Connect the KISS port to the kernel to get AX.25
First you need to set up the AX.25 port.
laptop$ echo "radio M0XXX-1 1200 255 2 My D74 radio" | sudo tee -a /etc/ax25/axports
You’ll be able to override the window and packet sizes on a per-socket
basis, so those two values are not that important for socket
programming.
Then you can attach the port, thus turning your serial KISS port into
an AX.25 packet interface:
laptop$ sudo kissattach /dev/rfcomm0 radio
AX.25 port radio bound to device ax0
laptop$ ifconfig ax0
ax0: flags=67<UP,BROADCAST,RUNNING> mtu 255
ax25 M0XXX-1 txqueuelen 10 (AMPR AX.25)
RX packets 65 bytes 1361 (1.3 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 84 bytes 7884 (7.6 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
(you’ll have 0 packets in your counters, of course)
The D74 connected to the laptop is now set up and ready for AX.25
traffic.
You can set an IP address on the ax0
interface and try to use it
directly. I don’t think that’s the best use of the connection, since
while yes it did work, TCP/IP applications are very chatty, and don’t
work well on a half duplex 1200bps wire.
You’ll have to tweak your TCP stack. Default Linux settings sent a
second SYN
packet just a fraction of a second after the first, under
the assumption that it’d be lost. Woah there, cowboy. Patience.
Also you have to make sure on a modern system to stop all the chatty
things trying to autodiscover what’s on this new interface you just
connected. Not that amusing to have your radio be busy for a few
seconds while SSDP tries to find what chromecasts you have
attached.
For D-Star data you don’t need more software. But more on that later.
So now we set up direwolf, a software TNC.
To operate the push-to-talk we’ll first start rigctld
:
raspberry$ rigctld -m 3081 -r /dev/ttyUSB0 -s 19200
This needs to be from a fairly recent version of hamlib, since the
9700 hasn’t been supported for long. It’s a fairly new radio, after
all. I also needed to download and compile direwolf, since for some
reason the raspbian one didn’t have hamlib support.
My direwolf.conf
looks like this, but check aplay -l
for which
sound card is the right one. Since it’s a virtual sound card built
into the 9700, it’ll plobably be plughw:1,0
. I have another USB
sound card connected, so that’s why it’s 2,0
.
ADEVICE plughw:2,0
PTT RIG 2 localhost:4532
CHANNEL 0
MYCALL 2E0XXX-4
Then start direwolf:
raspberry$ direwolf -p -t 0 -c direwolf.conf
[…]
Virtual KISS TNC is available on /dev/pts/8
The -p
switch activates the KISS
port, which we’ll need. Now
attach it as an AX.25 port:
raspberry$ echo radio 2E0XXX-1 9600 255 2 My icom radio | sudo tee -a /etc/ax25/axports
raspberry$ kissattach /dev/pts/8 radio
Now the raspberry should also have a working ax0
interface.
Try assigning an IP address and see if the radios light up. Remember:
patience.
4. Run services on top of AX.25
While debugging it can be very useful to run axlisten -a
on both
machines. It’s tcpdump
for AX.25. tcpdump
does work on the ax0
interface, but it can only capture, not decode. So it’s of limited
use. Wireshark can read any captured pcap files though.
The config /etc/ax25/axports
seems to mainly define the radio and
the “main call sign”. Even though I defined 2E0XXX-1
on the
raspberry pi in that file, the other SSIDs are still usable if you
make sure to add 2E0XXX-1
as the path.
The multiplexer of services (think “TCP ports”) of this system is the
SSIDs when setting up ax25d
.
ax25d
is an inetd-like multiplexer, allowing you to write simple
programs that read from stdin and write to stdout, and thereby create
interactive applications. Then you can have 2E0XXX-2
show a funny
message, 2E0XXX-3
have a shell, 2E0XXX-4
be a chat system, etc…
Non-interactive program
Let’s say this is our program, stored in /home/pi/name
, and executable:
#!/usr/bin/env bashecho Hello world
# sleep so that the output doesn't disappear before we've seen it,# when axcall exits.exec sleep 10
Then we can set up 2E0XXX-3
to be that program:
raspberry$ sudo emacs /etc/ax25/ax25d.conf
[2E0XXX-3 VIA radio]
default * * * * * * * pi /home/pi/hello hello
Then start ax25d -l
as root.
You should now be able to “dial” into that “port”:
laptop$ axcall radio 2E0XXX-3
[full screen window should open, showing that hello world]
Interactive program
Sure, that’s cool. We can display a message, or even stream some
data. But let’s get some interactive stuff going!
Unfortunately axcall
uses CR-terminated newlines instead of
NL, so most programs won’t “just work”.
So i hacked together a wrapper that sits between ax25d
and
the program, and replaces CR with NL.
This allows this simple interactive “server”:
#!/usr/bin/env bashecho Hello world
read NAME
echo"Hello ${NAME}"# sleep so that the output doesn't disappear before we've seen it,# when axcall exits.exec sleep 10
raspberry$ sudo tee -a /etc/ax25/ax25d.conf
[2E0XXX-5 VIA radio]
default * * * * * * * pi /home/pi/nlwrap -e /dev/stdout name /home/pi/name
^D
rapsberry$ sudo pkill -HUP ax25d
The -e /dev/stdout
redirects the subprogram’s stderr to also be
shown in the stream. Annoyingly it will otherwise simply be shown on
the terminal where you happened to start ax25d
, even though ax25d
runs in the background.
You should see the ports if you run netstat --protocol=ax25
.
Now dial into this new port:
laptop$ axcall radio 2E0XXX-5
[you should now be talking to the interactive "BBS" you've made]
D-Star
D-Star will provide a streaming interface more than a packet one. And
it’ll only allow “ASCII”. Specifically 0x11 and 0x13 will be filtered
out, as they are part of XON/XOFF on the virtual serial
port.
For the story of me finding this out, see this post.
Sure, you can build packet data on top of just printable characters,
but if you base64 encode then you’ll lose even more of the 1200bps.
But here’s how to set up the data channel. First pkill kissattach
on
both computers, to give you back the serial ports and prevent
irrelevant traffic
- Turn off KISS mode on the D74 (
F
,5
, until it doesn’t say
APRS
or KISS
) - Select VFO B (
A/B
button) - Switch mode to DV/DR (click
Mode
until you get DV
or DR
) - Turn DV/DR mode to DV (
F
,Mode
, DV/DR
) - Tune VFO B to a frequency you want to use
- Switch to data mode (
F
-Mode
, Voice/Data
). Note that it’ll
switch back to Voice
if you switch out of D-Star mode and in again.
- Menu->Set->Connectors->USB (B)/Data function
- USB (B) Function:
DV Data
(default: OFF
) - DV Data/GPS Out Baud Rate:
9600
(default)
- Menu->Set->DV/DD Set
- DD TX Inhibit (Power ON):
OFF
- Set the radio to
DV
mode
And restart the radio. You can also press the CALL
button to turn
off DD TX Inhibit without restarting,
Send/receive data
raspberry$ minicom -s
# set device /dev/ttyUSB1 (/dev/ttyUSB0 is the main control port for
# the 9700, /dev/ttyUSB1 is the DV Data stream as we've configured it).
# Set baud rate 9600, 8N1. XON/XOFF
laptop$ minicom -s
# set device /dev/rfcomm0.
# Set baud rate 1200, 8N1. XON/XOFF
You can also data from the command line:
raspberry$ cat /dev/ttyUSB1
laptop$ echo hello world > /dev/rfcomm0
I was surprised to see that the baud rate actually does matter, even
though neither the D74 nor the 9700 use actual serial devices, but
Bluetooth and USB, respectively. I think this may be the first time
where serial over USB with the wrong baud rate has not “just worked”
for me. Though I usually set it to what it’s supposed to be, so I may
be off here.
Now start typing. As you type the radios should transmit.
I don’t know if this way of chatting has better range than
AX.25. Because it’s the year 2020 it’s not the best time to do range
tests.
More things you can do
IP over AX.25
Assign an IP address to the ax0
interfaces and start using IP over
radio. With the hundreds of watts ham radio allows this beats the hell
out of wifi. Except in speed, of course.
D-Star has a 128kbps mode too (still not wifi speed), but only on the
23cm band (1.2GHz). I only have one radio with 23cm (the Icom 9700),
so have not been able to play with it. Maybe if I set up D-Star packet
decoding with an SDR I’ll be able to do some fun there. Then it may be
worth doing base64 or something on top of D-Star DV Data.
Keep in mind though that this is probably not as useful as you
think. In most jurisdictions it’s illegal to obscure amateur radio
signals, which means no encryption.
Authenticated BBS / IP
While encryption is not allowed (you’re not allowed to “obscure the
meaning”), I don’t see a reason you can’t create an authenticated
shell that sends commands signed by a private key. The meaning is not
obscured.
I’ve created a library and some test programs for writing C++ that
does just that. See the examples
directory in axlib, or run
axsh
(shell) or axftp
(file transfer) directly.
I built a protocol that should be replay-safe, and it uses
ed25519 signatures.
Maybe it’s possible to patch the null cipher back into OpenSSH. I
don’t know enough about the protocol to know if there are still
“obscured meanings” left even then. But I’m also sceptical that this
is the right path. With the roundtrip latencies (mentioned above) I
don’t think I’d want a chatty protocol, but something where the
protocol is designed to minimize roundtrips and be as quiet as
possible.
In the olden days, before computer security was invented, people set
up login terminals over ham radio. You can map callsigns to
usernames for their login. This can be a cool proof of concept thing,
but maybe best run inside a VM that gets periodically wiped, since
anyone with a radio can do whatever they want to your connections.
Non-connected message passing and routing.
You can send messages via other stations. I’ve not yet tried sending
any data via my local D-Star repeater, but I’m hoping that it can be
routed back to me via my OpenSpot.
Look into NET/ROM, and ROSE
I have nothing to say about these yet, having not experimented with
them.
Look into JNOS (not a typo)
“JNOS is first and foremost a router for ax.25, netrom, and ip
protocols - ip over rf is possible by encapsulating the ip in ax.25
frames. It is a packet node, bbs, personal mailbox system, convers
server (chatroom), offers a variety of tcp services, supports ax.25
tunnels (axip and axudp) over wired networks, supports ip
encapsulation (ipip and ipdup) over wired networks”
— What is JNOS?
Links
In addition to links inline with the text.