Xiegu G90 and herding CATs under Linux

Continuing my adventures in radio, I got my Xiegu G90 in the mail the other day.

Yes, I renewed almost all of my radio hardware by now, why do you ask.

It’s a lovely piece of kit. It’s much smaller than most reviews make you think, especially the YouTube ones, because they zoom in to show off the device, when the screen is actually the size of a large-ish postage stamp. It also does do NFM just fine, even though many official looking sites tell you it doesn’t for some strange reason – might be, they added this in some specific firmware version and forgot to correct the sites.

It’s an SDR device, it would be strange if that wouldn’t be possible.

But the point of this post was to share the hard-earned knowledge of how to get it working with digital mode software under Linux, because I found it anything but trivial.

Advice on the net is all over the place, but most of it revolves around getting your rig control set to a specific version of an Icom radio whose protocol it imitates and crossing your fingers. None of that worked for me, because how closely it adheres to the Icom protocol has changed between firmware versions. In fact, I had to patch Hamlib and recompile it, which opened another can of worms.

So here’s how I did this in the end:

The first hurdle was the fact that none of the advice worked – I couldn’t get WSTJ-X to control it in any way except by selecting X108G from the list. But if you select this, the current firmware version, 1.74, will not accept the PTT command, so you’d be forced to rely on VOX for PTT, which I didn’t want to do.

Upon closer investigation, I found the problem deep in Hamlib sources: a prior version of the Xiegu firmware did not observe the Icom protocol properly, and they coded around it. Then Xiegu fixed the bug.

It was easy enough to fix:

From fba773bc81e7ab584cd6d81dfaa7f95f2989ef6a Mon Sep 17 00:00:00 2001
From: Eugene Medvedev <rn3aoh.g@gmail.com>
Date: Mon, 31 Aug 2020 14:35:28 +0300
Subject: [PATCH] Patch for Xiegu G90

 rigs/icom/x108g.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/rigs/icom/x108g.c b/rigs/icom/x108g.c
index cd1fe8c7..e75ad6a4 100644
--- a/rigs/icom/x108g.c
+++ b/rigs/icom/x108g.c
@@ -284,7 +284,7 @@ const struct rig_caps x108g_caps =
     .set_bank =  icom_set_bank,
     .vfo_op =  icom_vfo_op,
     .scan =  icom_scan,
-    .set_ptt =  x108g_set_ptt,
+    .set_ptt =  icom_set_ptt,
     .get_ptt =  icom_get_ptt,
     .get_dcd =  icom_get_dcd,
     .set_ts =  icom_set_ts,

I am hesitant to submit the patch upstream, though, because I have no idea if it will keep working with the original X108G or not, and creating yet another radio model would involve more fiddling with Hamlib than I would like.

Unfortunately, updating Hamlib, or even getting rid of the systemwide Hamlib and installing my patched version from source would involve recompiling a lot of other software that links to it, so after I applied that patch and installed the result into /usr/local while keeping the systemwide Hamlib installed, I had the problem of getting everything else to work with my patched version.

Enter rigctld and a lot of systemd trickery.

The basic idea behind this method is that you set up udev to invoke Hamlib’s rigctld when the USB cable with the correct serial number is inserted, and set all your software up to talk to that, instead of a serial port. This has the added advantage of being able to use multiple programs with the same radio simultaneously, and is basically the same thing as flrig, but slightly more standardized and better supported. Since this is a network protocol, it does not care which particular version of Hamlib your software is compiled with, and it does not depend on FlDigi’s more arcane xmlrpc rig control protocol. You don’t even have to do anything, you just plug the cable in and off it goes.

The first part of the plan is this rules file, which goes into /etc/udev/rules.d/98-serial.rules.

# Rules to symlink FTDI USB-serial by serial number of the chip. This is generally helpful.

SUBSYSTEMS=="usb", KERNEL=="ttyUSB[0-9]*", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SYMLINK+="usb_serial/ftdi_%s{serial}"

It introduces a udev rule that gives every FTDI-based USB-to-serial dongle a unique alias beyond the ubiquitous /dev/ttyUSB?, which depends on its serial number, and doesn’t change depending on what else you might have plugged in. In Raspbian in particular, something similar is done for you already – they get aliases in /dev/serial/by-id – though the way it is done, it seems to me that two identical USB-to-serial dongles may still get confused. The CAT cable that comes with Xiegu G90, at least the one I got, has no serial number. Which makes it unique enough among my collection of dongles, but is annoying nonetheless. Technically, you can change the serial number yourself, using FTDI’s utility but that thing is Windows only and I didn’t want to bother with it or search for a Linux solution.

The next fun step is getting systemd to invoke rigctld when you plug the cable in, now that you know the device is uniquely named when it happens.

This goes into ~/.config/systemd/user/rigctldx.service:

Description=RigCtlD for Xiegu G90

ExecStart=/usr/local/bin/rigctld -m 3076 -r /dev/usb_serial/ftdi_ -s 19200 -T -t 18001 -p /dev/usb_serial/ftdi_ -v


The [Unit] and [Install] sections are what directs this service to start once the specific device appears – in our case, the FTDI USB-to-Serial with no serial number. Pay attention particularly to the [Service] section:

  • The Environment line forces rigctld to use the version of Hamlib I have compiled from source and installed into /usr/local – this way, every other program that wants Hamlib and comes from Ubuntu repositories uses the one they were compiled for and avoids conflicts, but still talks to the rig through rigctld in TCP/IP.
  • The ExecStart line has the command line to start rigctld with the correct parameters to control the G90. As you can see, it listens on port 18001, which is where you point WSTJ-X and other software later.

Enable the service by running systemctl --user enable rigctldx.service and you’re done: upon plugging the cable in, the rigctld we have compiled from source earlier starts. Pull the cable out and it’s killed.

Voila, CAT control!