Monthly Archives: March 2026

QK4 on Arch Linux AUR repository

I wrote PKGBUILD script for QK4 (Elecraft K4 remote software) and submitted that to the AUR repository. Arch Linux users can install QK4 with yay as follows:

$ yay -S qk4-git

It will create the proper menu / icon entries & decrease pipewire latency so that CW can be used. To update to the latest version use:

$ yay --devel -Suyy

Antenna widget in gtk+

There is no documentation on libqt6c -> decided to switch to gtk+. Cleaned up the code a bit too.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <gtk/gtk.h>

// Define the antenna names
#define ANTENNA1 "6m beam"
#define ANTENNA2 "WARC beam"
#define ANTENNA3 "20-15-10 stack"
#define ANTENNA4 "40m beam"
#define ANTENNA5 "No antenna"
#define ANTENNA6 "No antenna"
#define ANTENNA7 "No antenna"
#define ANTENNA8 "No antenna"
#define ANTENNA9 "No antenna"
#define ANTENNA10 "No antenna"
#define ANTENNA11 "No antenna"
#define ANTENNA12 "No antenna"
#define MAX_ANTENNAS 12

// Device for BM5
#define DEVICE "/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_B003ISA1-if00-port0"

// Update every 100 msec
#define UPDATE 100

char *antennas[] = {ANTENNA1, ANTENNA2, ANTENNA3, ANTENNA4, ANTENNA5, ANTENNA6,
ANTENNA7, ANTENNA8, ANTENNA9, ANTENNA10, ANTENNA11, ANTENNA12};

#define MAX_SM 11
char *stack_match[] = {"No antenna", "Top antenna", "Mid antenna", "Top+mid antennas",
"Bottom antenna", "Top+bot antennas", "Mid+bot antennas",
"All antennas", "AUX", "Unknown", "Phase"};

int fd;
GtkWidget *ant, *sm;

struct packet {
unsigned char cmd;
unsigned char dst;
unsigned char src;
unsigned char channel;
int len;
unsigned char data[256];
};

int read_untilcr(int fd, char *buf, int maxlen) {

int pos = 0, bytes_rd;

while(1) {
if((bytes_rd = read(fd, buf + pos, 1)) < 0) return -1;
if(bytes_rd == 0) {
sleep(1); // could be less than 1sec
continue;
}
if(!pos && (*buf == '\r' || *buf == '\n')) continue;
if(pos == maxlen) return -1;
if(buf[pos] == '\r') break;
pos++;
}
buf[pos] = '\0';
return pos;
}

int parse_packet(char *str, struct packet *packet) {

memset((void *) packet, 0, sizeof(struct packet));
if(sscanf(str, "[T%2xR%2x%2xH%2xM%[^]]", &(packet->cmd), &(packet->dst), &(packet->src), &(packet->channel), &(packet->data)) != 5) return -1;
return 0;
}

void set_tty() {

struct termios param;

if(tcgetattr(fd, &param) < 0) {
fprintf(stderr, "Status get failed.\n");
exit(1);
}

param.c_cflag &= ~PARENB;
param.c_cflag &= ~CSTOPB;
param.c_cflag |= CS8;
param.c_cflag &= ~CRTSCTS;
param.c_cflag |= CREAD | CLOCAL;
param.c_lflag &= ~ICANON;
param.c_lflag &= ~ECHO;
param.c_lflag &= ~ECHOE;
param.c_lflag &= ~ECHONL;
param.c_lflag &= ~ISIG;
param.c_iflag &= ~(IXON | IXOFF | IXANY);
param.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL);
param.c_oflag &= ~OPOST;
param.c_oflag &= ~ONLCR;
cfsetispeed(&param, B57600);
cfsetospeed(&param, B57600);

if(tcsetattr(fd, TCSANOW, &param) < 0) {
fprintf(stderr, "Status set failed.\n");
exit(1);
}
}

static gboolean update_band_info(void *asd) {

int len, antenna, relay;
struct packet packet;
char buf[512], tr = 0;

if((len = read_untilcr(fd, buf, sizeof(buf))) < 0) {
fprintf(stderr, "Read error.\n");
exit(1);
}

if(parse_packet(buf, &packet) < 0) {
printf("Incomplete packet -- skipping.\n");
return 1;
}

switch(packet.cmd) {
case 0xc4: // C4 = Bandmaster 5 status
if(sscanf(packet.data, "0%1d01EEEEEEEEEEEE%4d", &antenna, &relay) != 2) {
printf("Error extracting antenna & relay information.");
antenna = 99;
}
if(antenna > 0 && antenna < MAX_ANTENNAS)
sprintf(buf, "<span color='blue'>%s (relays = %d)</span>", antennas[antenna-1], relay);
else
sprintf(buf, "<span color='red'>Unknown</span>");
gtk_label_set_markup(GTK_LABEL(ant), buf);
break;
case 0x45: // Stack match
sscanf(packet.data, "%2x", &antenna);
if(antenna >= 128) {
antenna -= 128; // transmitting
tr = 1;
}
if(antenna >= 0 && antenna < MAX_SM)
sprintf(buf, "<span color='blue'>%s (%s)</span>", stack_match[antenna], tr?"TX":"RX");
else
strcpy(buf, "<span color='red'>Unknown state</span>");
gtk_label_set_markup(GTK_LABEL(sm), buf);
break;
case 0x13: // decoder data
printf("Decoder data ignored.\n");
break;
default:
printf("Unknown packet (%x) - ignored.\n", packet.cmd);
}
return 1;
}

void closewin(GtkWidget *win, gpointer data) {

gtk_main_quit();
}

int main(int argc, char *argv[]) {

GtkWidget *window;
GtkWidget *ant_label, *sm_label, *hbox1, *hbox2, *vbox;

gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Antenna Status");
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 350, 60);
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(closewin), NULL);

ant_label = gtk_label_new("Antenna:");
gtk_label_set_xalign(GTK_LABEL(ant_label), 0.0); // 0 = left, 1 = right
sm_label = gtk_label_new("Stack match:");
gtk_label_set_xalign(GTK_LABEL(sm_label), 0.0);
ant = gtk_label_new("-----");
gtk_label_set_xalign(GTK_LABEL(ant), 0.5);
sm = gtk_label_new("-----");
gtk_label_set_xalign(GTK_LABEL(sm), 0.5);

hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
hbox2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);

gtk_box_pack_start(GTK_BOX(hbox1), ant_label, TRUE, TRUE, 5);
gtk_box_pack_start(GTK_BOX(hbox1), ant, TRUE, TRUE, 5);

gtk_box_pack_start(GTK_BOX(hbox2), sm_label, TRUE, TRUE, 5);
gtk_box_pack_start(GTK_BOX(hbox2), sm, TRUE, TRUE, 5);

gtk_box_pack_start(GTK_BOX(vbox), hbox1, TRUE, TRUE, 5);
gtk_box_pack_start(GTK_BOX(vbox), hbox2, TRUE, TRUE, 5);
gtk_container_add(GTK_CONTAINER(window), vbox);

if((fd = open(DEVICE, O_RDONLY)) < 0) {
fprintf(stderr, "Can't open device.\n");
exit(1);
}
set_tty();

g_timeout_add(UPDATE, update_band_info, NULL);

gtk_widget_show_all(window);
gtk_main();

close(fd);
return 0;
}

Compile with (the above is in ant.c file): gcc ant.c -o ant -march=native `pkg-config –cflags –libs gtk+-3.0`

Switched from VNC(RFB) to RDP for remote access

In order to migrate from XOrg to Wayland, I switched from x11vnc & TigerVNC combo (uses RFB protocol) to krdp & remmina (uses RDP protocol). Remmina scrolling was kind of slow at first but after discovering the “step size for auto-scroll” setting in the preferences really helped (10 -> 30). Now the krdp is very picky but it seems that android app aRDP works with it once H264 is enabled.

Beverage on ground (BOG) testing

Made 200′ long beverage on ground (BOG) for receiving on 160m and 80m bands:

Feed point connected to 50 Ohm coax (no impedance transformer). Coax shield connected to ground and hot to the BOG wire.

End of the BOG is ground terminated (ground rod) with a 320 Ohm resistor (white).

The antenna is oriented approximately to Bouvet Island (3Y0K). In some cases seems to give better signal to noise as compared to the transmission antenna (inverted L with 45 full length radials).